This Blueprint is a responsive product grid layout with comparison functionality. A maximum of three items can be selected for the product comparison. The comparison view shows flexbox-powered columns or rows (depending on the viewport size) that appear with a slide-in effect. There are a couple of example media queries for smaller viewports.
此布局是具有比较多功能的响应式产品网格布局,最多可选择三个项目进行产品比较,(根据视口大小)出现在效果的幻灯片。
HTML
<!-- Compare basket -->
<div class="compare-basket">
<!-- comparison item miniatures come here -->
<button class="action action--button action--compare"><i class="fa fa-check"></i><span class="action__text">Compare</span></button>
</div>
<!-- Main view -->
<div class="view">
<!-- Blueprint header -->
<header class="bp-header cf"><!-- ... --></header>
<!-- Product grid -->
<section class="grid">
<!-- Products -->
<div class="product">
<div class="product__info">
<img class="product__image" src="images/1.png" alt="Product 1" />
<h3 class="product__title">Chryseia</h3>
<span class="product__year extra highlight">2011</span>
<span class="product__region extra highlight">Douro</span>
<span class="product__varietal extra highlight">Touriga Nacional</span>
<span class="product__alcohol extra highlight">13%</span>
<span class="product__price highlight">$55.90</span>
<button class="action action--button action--buy">
<i class="fa fa-shopping-cart"></i>
<span class="action__text">Add to cart</span>
</button>
</div>
<label class="action action--compare-add">
<input class="check-hidden" type="checkbox" />
<i class="fa fa-plus"></i>
<i class="fa fa-check"></i>
<span class="action__text action__text--invisible">Add to compare</span>
</label>
</div>
<div class="product">
<!-- ... -->
</div>
<div class="product">
<!-- ... -->
</div>
<!-- ... -->
</section>
</div><!-- /view -->
<!-- product compare wrapper -->
<section class="compare">
<!-- comparison items come here -->
<button class="action action--close"><i class="fa fa-remove"></i><span class="action__text action__text--invisible">Close comparison overlay</span></button>
</section>
<script src="js/classie.js"></script>
<script src="js/main.js"></script>
CSS
.view {
-webkit-transition: -webkit-transform 0.4s ease-in-out;
transition: transform 0.4s ease-in-out;
}
.view--compare {
-webkit-transform: scale3d(0.9,0.9,1);
transform: scale3d(0.9,0.9,1);
}
/* product grid */
.grid {
margin: 0 auto;
padding: 4em 1em;
max-width: 1200px;
text-align: center;
overflow: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* if flexbox is supported, let's use it to lay out the products */
.flexbox .grid {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-content: stretch;
-ms-flex-line-pack: stretch;
align-content: stretch;
-webkit-align-items: flex-start;
-ms-flex-align: start;
align-items: flex-start;
}
/* product */
.product {
position: relative;
display: inline-block;
vertical-align: top;
min-width: 16em;
margin: 0 1em 2.5em;
padding: 1.5em 1.5em 2em;
background: #24252A;
border-radius: 5px;
}
.product--selected {
box-shadow: 0 0 0 2px #5C5EDC;
}
.flexbox .product {
display: block;
-webkit-flex: 0 0 16em;
-ms-flex: 0 0 16em;
flex: 0 0 16em;
}
/* product info */
.product__info > span {
display: block;
padding: 1em 0;
}
/* since we'll be using the product info inside of the comparison, we'll hide the extra info for the grid view */
.grid .extra {
display: none;
}
.product__image {
display: block;
margin: 0 auto;
max-width: 100%;
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
}
.product:hover .product__image {
-webkit-animation: swing 0.6s forwards;
animation: swing 0.6s forwards;
}
/* https://daneden.github.io/animate.css/ */
@-webkit-keyframes swing {
25% {
-webkit-transform: rotate3d(0, 0, 1, 6deg);
transform: rotate3d(0, 0, 1, 6deg);
}
50% {
-webkit-transform: rotate3d(0, 0, 1, -4deg);
transform: rotate3d(0, 0, 1, -4deg);
}
75% {
-webkit-transform: rotate3d(0, 0, 1, 2deg);
transform: rotate3d(0, 0, 1, 2deg);
}
100% {
-webkit-transform: rotate3d(0, 0, 1, 0deg);
transform: rotate3d(0, 0, 1, 0deg);
}
}
@keyframes swing {
25% {
-webkit-transform: rotate3d(0, 0, 1, 6deg);
transform: rotate3d(0, 0, 1, 6deg);
}
50% {
-webkit-transform: rotate3d(0, 0, 1, -4deg);
transform: rotate3d(0, 0, 1, -4deg);
}
75% {
-webkit-transform: rotate3d(0, 0, 1, 2deg);
transform: rotate3d(0, 0, 1, 2deg);
}
100% {
-webkit-transform: rotate3d(0, 0, 1, 0deg);
transform: rotate3d(0, 0, 1, 0deg);
}
}
.product__title {
font-size: 150%;
margin: 1em 0 0;
min-height: 3em;
}
.product__price {
font-weight: bold;
color: #797BED;
}
.action {
display: inline-block;
font-size: 1em;
white-space: nowrap;
padding: 0.85em 1.25em;
cursor: pointer;
border: none;
background: transparent;
text-align: center;
}
.action:focus {
outline: none;
}
.action--button {
background: #2C2D34;
color: #fff;
border-radius: 2px;
-webkit-transition: background 0.2s;
transition: background 0.2s;
}
.action--button:hover {
background: #5C5EDC;
}
.action__text {
font-family: 'Raleway', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: bold;
letter-spacing: 1px;
font-size: .813em;
vertical-align: middle;
display: inline-block;
}
.action__text--invisible {
position: absolute;
top: 100%;
opacity: 0;
pointer-events: none;
}
.action--button i + span {
margin-left: 1em;
}
.flexbox .action--buy {
-webkit-align-self: center;
-ms-flex-item-align: center;
align-self: center;
margin-top: 1em;
-webkit-flex: none;
-ms-flex: none;
flex: none;
}
.action--close {
position: absolute;
overflow: hidden;
top: 0;
right: 0;
font-size: 1.5em;
color: #ddd;
pointer-events: none;
opacity: 0;
-webkit-transition: opacity 0.3s, background 0.2s;
transition: opacity 0.3s, background 0.2s;
}
.view--compare + .compare .action--close {
pointer-events: auto;
opacity: 1;
-webkit-transition-delay: 0.4s, 0s;
transition-delay: 0.4s, 0s;
}
.action--close:hover,
.action--close:focus {
color: #797BED;
}
.action--compare {
margin: 0 0 0 4px;
opacity: 0;
pointer-events: none;
cursor: default;
background-color: #34363D;
color: #565B6C;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
.compare-basket--active .action--compare {
opacity: 1;
}
.compare-basket--active .action--compare:nth-child(3),
.compare-basket--active .action--compare:nth-child(4) {
background-color: #494BC7;
color: #fff;
pointer-events: auto;
cursor: pointer;
}
.action--remove {
position: absolute;
overflow: hidden;
color: #ddd;
top: 0px;
right: 2px;
padding: 0;
font-size: 0.65em;
}
.action--compare-add {
color: #ddd;
position: absolute;
top: 10px;
right: 5px;
}
.action--compare-add:hover .action__text--invisible {
opacity: 1;
top: 35px;
right: 10px;
color: #ddd;
font-size: 75%;
letter-spacing: 0;
background: #2F3035;
border-radius: 2px;
padding: 3px 5px;
}
.action--remove:hover,
.action--compare-add:hover {
color: #5C5EDC;
}
.action--compare-add .fa-check,
.action--compare-add input[type=checkbox]:checked ~ .fa-plus {
display: none;
}
.action--compare-add input[type=checkbox]:checked ~ .fa-check {
display: block;
color: #5C5EDC;
}
.check-hidden {
position: absolute;
opacity: 0;
}
.compare-basket {
width: 100%;
padding: 0.75em;
text-align: right;
position: fixed;
top: 0;
left: 0;
background: #212227;
z-index: 1000;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: translate3d(0,-100%,0);
transform: translate3d(0,-100%,0);
-webkit-transition: -webkit-transform 0.3s cubic-bezier(0.2,1,0.3,1);
transition: transform 0.3s cubic-bezier(0.2,1,0.3,1);
}
.compare-basket--active {
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
.flexbox .compare-basket {
display: -webkit-flex;
display: -ms-flex;
display: flex;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
}
.product-icon {
display: inline-block;
vertical-align: middle;
background: #42444F;
width: 50px;
height: 50px;
padding: 5px;
margin: 0 3px;
border-radius: 2px;
position: relative;
}
.product-icon::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border-radius: 4px;
z-index: -1;
box-shadow: -56px 0 #2C2D34;
}
.compare-basket--full .product-icon::after {
display: none;
}
.flexbox .product-icon {
display: block;
}
/* comparison overlay */
.compare {
position: fixed;
z-index: 100;
width: 100%;
height: 0;
overflow: hidden;
top: 0;
left: 0;
z-index: 1001;
-webkit-transition: height 0s 0.4s;
transition: height 0s 0.4s;
}
.flexbox .compare {
display: -webkit-flex;
display: -ms-flex;
display: flex;
}
.view--compare + .compare {
pointer-events: auto;
height: 100%;
-webkit-transition: none;
transition: none;
}
.compare::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0,0,0,0.5);
opacity: 0;
-webkit-transition: opacity 0.4s;
transition: opacity 0.4s;
}
.view--compare + .compare::before {
opacity: 1;
}
.compare__item {
height: 100%;
width: 50%;
background: #24252A;
text-align: center;
cursor: default;
padding: 2em 0;
-webkit-transition: -webkit-transform 0.4s ease-in-out;
transition: transform 0.4s ease-in-out;
}
.no-flexbox .compare__item {
display: inline-block;
width: 50%;
}
.compare__item:nth-of-type(2) {
background: #212227;
}
.compare__item .product__title {
margin: 1em 0;
min-height: 0;
}
.compare__item .product__price {
color: #CECECE;
}
.compare__item span[class^="product__"] {
display: block;
padding: 0.85em 0;
-webkit-transition: background-color 0.3s;
transition: background-color 0.3s;
}
.compare__item span[class^="product__"].hover {
background: #000;
}
/* three items */
.compare__item:first-of-type:nth-last-of-type(3),
.compare__item:first-of-type:nth-last-of-type(3) ~ .compare__item {
width: 33.3333%;
}
.flexbox .compare__item {
-webkit-flex: auto;
-ms-flex: auto;
flex: auto;
}
.compare__item:nth-child(odd) {
-webkit-transform: translate3d(0,-100vh,0);
transform: translate3d(0,-100vh,0);
}
.compare__item:nth-child(even) {
-webkit-transform: translate3d(0,100vh,0);
transform: translate3d(0,100vh,0);
}
.view--compare + .compare .compare__item:nth-child(odd),
.view--compare + .compare .compare__item:nth-child(even) {
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
.compare__effect {
width: 100%;
height: 100%;
opacity: 0;
-webkit-transition: -webkit-transform 1s cubic-bezier(0.2, 1, 0.3, 1), opacity 1s cubic-bezier(0.2, 1, 0.3, 1);
transition: transform 1s cubic-bezier(0.2, 1, 0.3, 1), opacity 1s cubic-bezier(0.2, 1, 0.3, 1);
}
.compare__item:nth-child(odd) .compare__effect {
-webkit-transform: translate3d(0,-250px,0);
transform: translate3d(0,-250px,0);
}
.compare__item:nth-child(even) .compare__effect {
-webkit-transform: translate3d(0,250px,0);
transform: translate3d(0,250px,0);
}
.view--compare + .compare .compare__item:nth-child(odd) .compare__effect,
.view--compare + .compare .compare__item:nth-child(even) .compare__effect {
opacity: 1;
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
-webkit-transition-delay: 0.3s;
transition-delay: 0.3s;
}
@media screen and (min-width: 59.688em) {
.flexbox .compare__effect {
display: -webkit-flex;
display: -ms-flex;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
}
.flexbox .compare__item .product__image {
-webkit-align-self: center;
-ms-flex-align: center;
align-self: center;
-webkit-flex: none;
-ms-flex: none;
flex: none;
}
}
@media screen and (max-width: 59.688em) {
.grid {
padding: 2em 0.5em;
font-size: 65%;
}
.product {
margin: 0 0.5em 1em;
min-width: 13em;
}
.product__title {
font-size: 115%;
}
.flexbox .product {
-webkit-flex: 0 0 13em;
-ms-flex: 0 0 13em;
flex: 0 0 13em;
}
.flexbox .compare {
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
}
.no-flexbox .compare__item,
.compare__item,
.compare__item:first-of-type:nth-last-of-type(3),
.compare__item:first-of-type:nth-last-of-type(3) ~ .compare__item {
width: 100%;
}
.compare__item {
text-align: left;
padding: 1.5em;
font-size: 90%;
}
.compare__item .product__image {
height: 40px;
float: left;
}
.compare__item .product__title {
margin: 0 40px 0 43px;
font-size: 0.85em;
display: block;
}
.compare__item .product__year { border-bottom: 2px solid #2E294E; }
.compare__item .product__region { border-bottom: 2px solid #6D6FD2; }
.compare__item .product__varietal { border-bottom: 2px solid #4B5267; }
.compare__item .product__alcohol { border-bottom: 2px solid #3C3474; }
.action--close {
padding: 0.5em 0.75em;
}
.compare__item .action--buy {
margin: 0;
display: block;
}
.compare__item span[class^="product__"] {
display: inline-block;
padding: 0.25em;
margin: 0 0 0.5em 0;
font-size: 0.85em;
}
.compare__item:nth-child(odd) {
-webkit-transform: translate3d(-100%,0,0);
transform: translate3d(-100%,0,0);
}
.compare__item:nth-child(even) {
-webkit-transform: translate3d(100%,0,0);
transform: translate3d(100%,0,0);
}
.compare__item:nth-child(odd) .compare__effect {
-webkit-transform: translate3d(-250px,0,0);
transform: translate3d(-250px,0,0);
}
.compare__item:nth-child(even) .compare__effect {
-webkit-transform: translate3d(250px,0,0);
transform: translate3d(250px,0,0);
}
}
JavaScript
/**
* main.js
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2015, Codrops
* http://www.codrops.com
*/
(function() {
var viewEl = document.querySelector('.view'),
gridEl = viewEl.querySelector('.grid'),
items = [].slice.call(gridEl.querySelectorAll('.product')),
basket;
// the compare basket
function CompareBasket() {
this.el = document.querySelector('.compare-basket');
this.compareCtrl = this.el.querySelector('.action--compare');
this.compareWrapper = document.querySelector('.compare'),
this.closeCompareCtrl = this.compareWrapper.querySelector('.action--close')
this.itemsAllowed = 3;
this.totalItems = 0;
this.items = [];
// compares items in the compare basket: opens the compare products wrapper
this.compareCtrl.addEventListener('click', this._compareItems.bind(this));
// close the compare products wrapper
var self = this;
this.closeCompareCtrl.addEventListener('click', function() {
// toggle compare basket
classie.add(self.el, 'compare-basket--active');
// animate..
classie.remove(viewEl, 'view--compare');
});
}
CompareBasket.prototype.add = function(item) {
// check limit
if( this.isFull() ) {
return false;
}
classie.add(item, 'product--selected');
// create item preview element
var preview = this._createItemPreview(item);
// prepend it to the basket
this.el.insertBefore(preview, this.el.childNodes[0]);
// insert item
this.items.push(preview);
this.totalItems++;
if( this.isFull() ) {
classie.add(this.el, 'compare-basket--full');
}
classie.add(this.el, 'compare-basket--active');
};
CompareBasket.prototype._createItemPreview = function(item) {
var self = this;
var preview = document.createElement('div');
preview.className = 'product-icon';
preview.setAttribute('data-idx', items.indexOf(item));
var removeCtrl = document.createElement('button');
removeCtrl.className = 'action action--remove';
removeCtrl.innerHTML = '<i class="fa fa-remove"></i><span class="action__text action__text--invisible">Remove product</span>';
removeCtrl.addEventListener('click', function() {
self.remove(item);
});
var productImageEl = item.querySelector('img.product__image').cloneNode(true);
preview.appendChild(productImageEl);
preview.appendChild(removeCtrl);
var productInfo = item.querySelector('.product__info').innerHTML;
preview.setAttribute('data-info', productInfo);
return preview;
};
CompareBasket.prototype.remove = function(item) {
classie.remove(this.el, 'compare-basket--full');
classie.remove(item, 'product--selected');
var preview = this.el.querySelector('[data-idx = "' + items.indexOf(item) + '"]');
this.el.removeChild(preview);
this.totalItems--;
var indexRemove = this.items.indexOf(preview);
this.items.splice(indexRemove, 1);
if( this.totalItems === 0 ) {
classie.remove(this.el, 'compare-basket--active');
}
// checkbox
var checkbox = item.querySelector('.action--compare-add > input[type = "checkbox"]');
if( checkbox.checked ) {
checkbox.checked = false;
}
};
CompareBasket.prototype._compareItems = function() {
var self = this;
// remove all previous items inside the compareWrapper element
[].slice.call(this.compareWrapper.querySelectorAll('div.compare__item')).forEach(function(item) {
self.compareWrapper.removeChild(item);
});
for(var i = 0; i < this.totalItems; ++i) {
var compareItemWrapper = document.createElement('div');
compareItemWrapper.className = 'compare__item';
var compareItemEffectEl = document.createElement('div');
compareItemEffectEl.className = 'compare__effect';
compareItemEffectEl.innerHTML = this.items[i].getAttribute('data-info');
compareItemWrapper.appendChild(compareItemEffectEl);
this.compareWrapper.insertBefore(compareItemWrapper, this.compareWrapper.childNodes[0]);
}
setTimeout(function() {
// toggle compare basket
classie.remove(self.el, 'compare-basket--active');
// animate..
classie.add(viewEl, 'view--compare');
}, 25);
};
CompareBasket.prototype.isFull = function() {
return this.totalItems === this.itemsAllowed;
};
function init() {
// initialize an empty basket
basket = new CompareBasket();
initEvents();
}
function initEvents() {
items.forEach(function(item) {
var checkbox = item.querySelector('.action--compare-add > input[type = "checkbox"]');
checkbox.checked = false;
// ctrl to add to the "compare basket"
checkbox.addEventListener('click', function(ev) {
if( ev.target.checked ) {
if( basket.isFull() ) {
ev.preventDefault();
return false;
}
basket.add(item);
}
else {
basket.remove(item);
}
});
});
}
init();
})();