Johanna Knauer – 德国女摄影师
月份:2013年1月
原文:VERTICAL SHOWCASE SLIDER WITH JQUERY AND CSS TRANSITIONS
在本教程中,我们将针对网上商店创建一个非常简单的和响应式的产品幻灯片。我们的想法是在一个全屏视图中包含不同的部分:图像或预览,导航以及描述。当浏览项目时,我们将通过滑动来展示预览部分,并且同时相反方向滑动展示描述部分。这种反方向动画效果的灵感来自于 National LGBT Museum 网站,当浏览或者滚动页面时以相同的方式来移动左边和右边的内容。
请注意:该效果仅工作在支持各自CSS属性的浏览器上。
用于Demo演示的产品图片和信息内容来自于IKEA。
标记
我们将有过一个包裹以下元素的主容器:一个标题,一个内容或者是描述的包装器以及一个幻灯片包装器:
<section id="ps-container" class="ps-container">
<div class="ps-header">
<h1>Vertical Showcase Slider</h1>
</div><!-- /ps-header -->
<div class="ps-contentwrapper">
<div class="ps-content">
<h2>Bernhard</h2>
<span class="ps-price">£100</span>
<p>With restful springiness in the seat; prevents static sitting and provides enhanced seating comfort. Padded seat and back for enhanced seating comfort. Soft, hardwearing and easy care leather, which ages gracefully.</p>
<a href="http://www.ikea.com/gb/en/catalog/products/80163804/#/60203882">Buy this item</a>
</div>
<div class="ps-content">
<!-- description item 2 -->
</div>
<div class="ps-content">
<!-- description item 3 -->
</div>
<div class="ps-content">
<!-- description item 4 -->
</div>
<div class="ps-content">
<!-- description item 5 -->
</div>
</div><!-- /ps-contentwrapper -->
<div class="ps-slidewrapper">
<div class="ps-slides">
<div style="background-image:url(images/1.jpg);"></div>
<div style="background-image:url(images/2.jpg);"></div>
<div style="background-image:url(images/3.jpg);"></div>
<div style="background-image:url(images/4.jpg);"></div>
<div style="background-image:url(images/5.jpg);"></div>
</div>
<nav>
<a href="#" class="ps-prev"></a>
<a href="#" class="ps-next"></a>
</nav>
</div><!-- /ps-slidewrapper -->
</section><!-- /ps-container -->
幻灯片包装器将包含与内容包装器相同数量的div,每一个div有各自的图片作为背景图片。还将有一个包含前一个和后一个锚点的导航,这些锚点也将有一个背景图片,但是我们将其设置为动态的。
接下来让我们添加一些样式。
CSS
需要注意的是,CSS将不包含任何浏览器供应商前缀,但你会在文件中发现他们。
首先让我们来添加一个已经通过fontello.com创建过的字体。该字体将只有一个字符,它是一个小的购物车,用作“购买此产品”的链接:
@font-face {
font-family: 'icon';
src: url("font/icon.eot");
src:
url("font/icon.eot?#iefix") format('embedded-opentype'),
url("font/icon.woff") format('woff'),
url("font/icon.ttf") format('truetype'),
url("font/icon.svg#icon") format('svg');
font-weight: normal;
font-style: normal;
}
我们的目标是创建一个100%屏幕宽度和高度的布局,所以我们将容器设置为绝对定位,并且内容溢出时隐藏:
.ps-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
text-transform: uppercase;
color: #555;
background: #fff;
}
宽度和高度都为100%,请注意我们也设置了html的高度为100%(demo.css)。
所有主容器直接子元素的宽度将为50%,并且绝对定位:
.ps-container > div {
position: absolute;
width: 50%;
}
将几个元素都设为绝对定位:
.ps-container > div > div,
.ps-slidewrapper > nav,
.ps-slides > div {
position: absolute;
}
标题将有一个150px的高度,我们将它定位在左上角。
.ps-header {
top: 0px;
left: 0px;
height: 150px;
z-index: 1001;
background: #fff;
}
h1的样式如下:
.ps-header h1 {
color: #ccc;
line-height: 150px;
margin: 0;
padding: 0 50px;
font-weight: 200;
font-size: 14px;
letter-spacing: 10px;
}
内容包装器将需要与标题高度相同的高度,并且我们将其设置为溢出时隐藏:
.ps-contentwrapper {
top: 150px;
bottom: 0px;
overflow: hidden;
z-index: 1000;
}
内部div将占据父元素全部的高度和宽度,我们将给它添加一些padding:
.ps-content {
background: #fff;
width: 100%;
height: 100%;
padding: 50px;
}
接下来是文本元素的样式,标题和段落都会有一些边框:
.ps-content h2 {
padding: 10px 15px;
border-right: 1px solid #f2f2f2;
border-bottom: 1px solid #f2f2f2;
letter-spacing: 4px;
margin: 10px 0 30px;
text-align: right;
font-weight: 700;
}
.ps-content p {
line-height: 26px;
font-size: 12px;
letter-spacing: 2px;
word-spacing: 10px;
padding: 10px 15px;
font-weight: 400;
text-align: justify;
border-left: 1px solid #f2f2f2;
border-top: 1px solid #f2f2f2;
}
价格将浮动在左侧,并且我们给它如下的样式:
.ps-content span.ps-price {
float: left;
margin: 10px;
width: 140px;
height: 140px;
line-height: 140px;
text-align: center;
color: #fff;
background: #f7cfc6;
background: rgba(247,197,185,0.8);
font-size: 55px;
font-weight: 200;
}
需要注意的是,我们在设置rgba颜色之前首先设置一个十六进制的颜色,老的浏览器不支持rgba颜色值所以会忽略它。
如果我们不是在一个触摸设备上(使用Modernizr来判断),链接将有一个厚的边框并且当鼠标悬停时变为绿色。
.ps-content a:last-child {
font-size: 14px;
font-weight: 700;
color: #555;
letter-spacing: 4px;
float: right;
border: 3px solid #555;
padding: 3px;
text-indent: 4px;
}
.no-touch .ps-content a:last-child:hover {
color: #b2d79d;
border-color: #b2d79d;
}
我们将定义:after伪类样式来添加购物车图标:
.ps-content a:last-child:before {
content: '\53';
font-family: 'icon';
font-style: normal;
font-weight: normal;
speak: none;
padding-right: 5px;
}
幻灯片容器和导航将被放置在右侧,并且高度为100%:
.ps-slidewrapper {
right: 0px;
top: 0px;
height: 100%;
overflow: hidden;
}
幻灯片包装器将被从顶部0像素到底部200px的位置拉伸,这将保持其高度是弹性的:
.ps-slides {
top: 0px;
bottom: 200px;
width: 100%;
}
包含背景图片的内部div将会有100%的宽度和高度,我们将给它们一个内嵌盒阴影,巧妙的覆盖在主图片预览上。由于我们不知道div的确切大小,所以我们给它一个极其大的传播半径值:
.ps-slides > div {
width: 100%;
height: 100%;
box-shadow: inset 0 0 0 9999px rgba(179,157,250,0.1);
}
导航将定位在幻灯片容器的底部,我们将给它一个200px的固定高度值:
.ps-slidewrapper > nav {
width: 100%;
height: 200px;
bottom: 0px;
right: 0px;
z-index: 1000;
}
前一个和后一个链接元素将被浮动,并且我们也给它们一个内部盒阴影来创建巧妙的遮罩效果。它们还将有一个针对非触摸设备的过渡效果:
.ps-slidewrapper > nav > a {
width: 50%;
height: 100%;
position: relative;
float: left;
outline: none;
box-shadow: inset 0 0 0 9999px rgba(207,227,206,0.8);
}
.ps-slidewrapper > nav > a:first-child {
box-shadow: inset 0 0 0 9999px rgba(233,217,141,0.8);
}
.no-touch .ps-slidewrapper > nav > a {
transition: box-shadow 0.4s ease-in-out;
}
.no-touch .ps-slidewrapper > nav > a:hover {
box-shadow: inset 0 0 0 9999px rgba(246,224,121,0.1);
}
.no-touch .ps-slidewrapper > nav > a:first-child:hover {
box-shadow: inset 0 0 0 9999px rgba(249,15,15,0.1);
}
导航锚点将有一个伪元素用于定义箭头出现时的样式,为此,我们将添加一个左侧和顶部边框,并进行相应的旋转:
.ps-slidewrapper > nav > a:after {
content: '';
position: absolute;
width: 100px;
height: 100px;
top: 50%;
left: 50%;
margin: -20px 0 0 -50px;
transform: rotate(45deg);
border-left: 1px solid #fff;
border-top: 1px solid #fff;
}
.ps-slidewrapper > nav > a:first-child:after {
transform: rotate(-135deg);
margin: -80px 0 0 -50px;
}
主预览和导航链接都将是有背景图片的元素,我们将设置图片的高度拉伸以匹配容器的高度。
.ps-slides > div,
.ps-slidewrapper > nav > a {
background-color: #fff;
background-position: center top;
background-repeat: no-repeat;
background-size: auto 100%;
}
当我们要使幻灯片元素滑入或滑出,下一个class是动态的:
.ps-move {
transition: top 400ms ease-out;
}
最后,但并非最不重要的是,我们将针对小型设备定义一个媒体查询。当JavaScript可用时,我们只希望通过media query来改变样式,而在JavaScript被禁用的情况下,有一个完全不同的布局。
我们需要将主容器的子元素宽度设为100%:
@media screen and (max-width: 860px) {
.js .ps-container > div {
width: 100%;
}
标题将小一些:
.js .ps-header {
height: 50px;
}
.js .ps-header h1 {
line-height: 50px;
padding: 0px 20px;
letter-spacing: 4px;
}
The wrapper for the preview slides will be positioned differently since we’ll place the content under it:
.js .ps-slides {
bottom: 320px;
top: 50px;
}
导航将是原始高度的一半:
.js .ps-slidewrapper > nav {
height: 100px;
}
内容包装器的高度为220px,并且我们将它放置在导航的上方:
.js .ps-contentwrapper {
top: auto;
height: 220px;
bottom: 100px;
}
接下来让我们改变大小的排版元素:
.js .ps-content {
padding: 10px;
}
.js .ps-content h2 {
border-right: none;
font-size: 18px;
margin: 10px 0;
padding-top: 0;
}
.js .ps-content span.ps-price {
font-size: 18px;
width: 50px;
height: 50px;
line-height: 50px;
font-weight: 700;
margin-bottom: 0;
}
我们没有这么大的空间,所以需要给段落设置一个固定的高度,并使其滚动:
.js .ps-content p {
line-height: 20px;
border: none;
padding: 5px 10px;
height: 80px;
overflow-y: scroll;
}
该链接将小一些,并且更好地定位以适应它的上下文:
.js .ps-content a:last-child {
font-size: 13px;
margin: 10px 20px 0 0;
}
}
这就是所有的样式,接下来让我们看下javascript。
Javascript
我们将从通过缓存一些元素和定义一些变量开始:
var $container = $( '#ps-container' ),
$contentwrapper = $container.children( 'div.ps-contentwrapper' ),
// the items (description elements for the slides/products)
$items = $contentwrapper.children( 'div.ps-content' ),
itemsCount = $items.length,
$slidewrapper = $container.children( 'div.ps-slidewrapper' ),
// the slides (product images)
$slidescontainer = $slidewrapper.find( 'div.ps-slides' ),
$slides = $slidescontainer.children( 'div' ),
// navigation arrows
$navprev = $slidewrapper.find( 'nav > a.ps-prev' ),
$navnext = $slidewrapper.find( 'nav > a.ps-next' ),
// current index for items and slides
current = 0,
// checks if the transition is in progress
isAnimating = false,
// support for CSS transitions
support = Modernizr.csstransitions// transition end event
// transition end event
// https://github.com/twitter/bootstrap/issues/2870
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
};
当初始函数调用时,首先显示的第一条项目及相关图片,同时,我们要更新导航箭头正确的背景图片,这意味着我们要使用相同的背景图片作为预览。最后,initEvents函数被调用。
init = function() {
// show first item
var $currentItem = $items.eq( current ),
$currentSlide = $slides.eq( current ),
initCSS = {
top : 0,
zIndex : 999
};
$currentItem.css( initCSS );
$currentSlide.css( initCSS );
// update nav images
updateNavImages();
// initialize some events
initEvents();
},
updateNavImages = function() {
// updates the background image for the navigation arrows
var configPrev = ( current > 0 ) ? $slides.eq( current - 1 ).css( 'background-image' ) : $slides.eq( itemsCount - 1 ).css( 'background-image' ),
configNext = ( current < itemsCount - 1 ) ? $slides.eq( current + 1 ).css( 'background-image' ) : $slides.eq( 0 ).css( 'background-image' );
$navprev.css( 'background-image', configPrev );
$navnext.css( 'background-image', configNext );
},
adjustLayout = function() {
$container.css( 'height', $window.height() );
},
我们需要初始化两个导航元素的click事件以及项目/描述和幻灯片两者的transitionend事件。
initEvents = function() {
$navprev.on( 'click', function( event ) {
if( !isAnimating ) {
slide( 'prev' );
}
return false;
} );
$navnext.on( 'click', function( event ) {
if( !isAnimating ) {
slide( 'next' );
}
return false;
} );
// transition end event
$items.on( transEndEventName, removeTransition );
$slides.on( transEndEventName, removeTransition );
},
主函数当然是slide函数,我们的想法是将下一张幻灯片放置在当前幻灯片的上方或下方(取决于我们所点击的导航元素)。
slide = function( dir ) {
isAnimating = true;
var $currentItem = $items.eq( current ),
$currentSlide = $slides.eq( current );
// update current value
if( dir === 'next' ) {
( current 0 ) ? --current : current = itemsCount - 1;
}
// new item that will be shown
var $newItem = $items.eq( current ),
// new slide that will be shown
$newSlide = $slides.eq( current );
// position the new item up or down the viewport depending on the direction
$newItem.css( {
top : ( dir === 'next' ) ? '-100%' : '100%',
zIndex : 999
} );
$newSlide.css( {
top : ( dir === 'next' ) ? '100%' : '-100%',
zIndex : 999
} );
setTimeout( function() {
// move the current item and slide to the top or bottom depending on the direction
$currentItem.addClass( 'ps-move' ).css( {
top : ( dir === 'next' ) ? '100%' : '-100%',
zIndex : 1
} );
$currentSlide.addClass( 'ps-move' ).css( {
top : ( dir === 'next' ) ? '-100%' : '100%',
zIndex : 1
} );
// move the new ones to the main viewport
$newItem.addClass( 'ps-move' ).css( 'top', 0 );
$newSlide.addClass( 'ps-move' ).css( 'top', 0 );
// if no CSS transitions set the isAnimating flag to false
if( !support ) {
isAnimating = false;
}
}, 0 );
// update nav images
updateNavImages();
};
这就是全部代码,希望你喜欢这个教程并能够有所启发!
原创译文 转载请注明出处
英文原文:VERTICAL SHOWCASE SLIDER WITH JQUERY AND CSS TRANSITIONS