幻灯片大小_倾斜的内容幻灯片
幻灯片大小
The FWA landing page has a really nice content slider that plays with 3D perspective on screenshots and animates them in an interesting way. Today we’d like to recreate part of that effect and make a simple content slideshow with some fancy 3D animations. The slideshow won’t be the same as the one on the FWA page: the items won’t be “floating” or moving on hover and we’ll only have a simple navigation.
FWA登陆页面有一个非常不错的内容滑块,可以对屏幕截图进行3D透视,并以有趣的方式对其进行动画处理。 今天,我们想重新创建这种效果的一部分,并用一些精美的3D动画制作一个简单的内容幻灯片。 幻灯片将与FWA页面上的幻灯片不同:这些项目不会“浮动”或在悬停时移动,而我们只有一个简单的导航。
If you have seen the effect over at the FWA landing page you will notice that the movement directions of the screenshots are random (moving up or down and sliding to the sides). We want to achieve the same effect by randomly adding some data-attributes that control the type of animation.
如果您在FWA登陆页面上看到了效果,您会注意到屏幕截图的移动方向是随机的(上下移动以及向两侧滑动)。 我们希望通过随机添加一些控制动画类型的数据属性来达到相同的效果。
Please note that we’ll be using CSS 3D Transforms and CSS Animations which might not work in older or mobile browsers.
请注意,我们将使用CSS 3D变换和CSS动画,它们可能在较旧的或移动的浏览器中不起作用。
For the demo we are using some website screenshots from Zurb’s Responsive Gallery.
对于演示,我们使用Zurb的Responsive Gallery中的一些网站屏幕截图。
So, let’s get started!
所以,让我们开始吧!
标记 (The Markup)
The slideshow has a main container with the class and ID “slideshow” and we can use an ordered list for our slides. Each list item contains a description with a title and a paragraph. It will also contain a division with the class “tiltview” where we will add our screenshots. The classes “col” an “row” will help us set the right layout for the inner anchors:
幻灯片有一个主容器,其类别和ID为“幻灯片”,我们可以为幻灯片使用排序列表。 每个列表项都包含带有标题和段落的描述。 它还将包含一个类“ tiltview”的分区,在该分区中我们将添加屏幕截图。 类“ col”和“ row”将帮助我们为内部锚点设置正确的布局:
<div class="slideshow" id="slideshow">
<ol class="slides">
<li class="current">
<div class="description">
<h2>Some Title</h2>
<p>Some description</p>
</div>
<div class="tiltview col">
<a href="http://grovemade.com/"><img src="img/1_screen.jpg"/></a>
<a href="https://tsovet.com/"><img src="img/2_screen.jpg"/></a>
</div>
</li>
<li>
<div class="description">
<!-- ... -->
</div>
<div class="tiltview row">
<!-- ... -->
</div>
</li>
<li>
<!-- ... -->
</li>
</ol>
</div>
We’ll also add a navigation element in our JavaScript which we’ll place right after the ordered list. It will consist of a nav
with the right amount of spans.
我们还将在JavaScript中添加一个导航元素,将其放置在有序列表之后。 它将由具有适当跨度的nav
组成。
Let’s already have a thought on how we will control the animations for each screenshot. In our script we set a data-attribute for a random incoming and outgoing animation. We’ll use the data-attributes data-effect-in
and data-effect-out
to control our animations in the CSS. We’ll check out the values for those attributes in a while. Let’s first check out the main style of the slideshow.
让我们考虑一下如何控制每个屏幕截图的动画。 在我们的脚本中,我们为传入和传出的随机动画设置了数据属性。 我们将使用数据属性data-effect-in
和data-effect-out
来控制CSS中的动画。 我们将在一段时间后检查这些属性的值。 首先让我们看看幻灯片的主要风格。
CSS (The CSS)
Note that the CSS will not contain any vendor prefixes, but you will find them in the files (-webkit-).
请注意,CSS将不包含任何供应商前缀,但是您可以在文件(-webkit-)中找到它们。
Our slideshow wrapper and the ordered list will have the following style:
我们的幻灯片包装器和排序列表将具有以下样式:
.slideshow {
position: relative;
margin-bottom: 100px;
}
.slides {
list-style: none;
padding: 0;
margin: 0;
position: relative;
height: 500px;
width: 100%;
overflow: hidden;
background: #ddd;
color: #333;
}
The slideshow will be 500px high and we need to set the overflow to hidden, so that we don’t see the items fly out. When we can’t build our slideshow because JavaScript is not enabled, we need to make sure that all slides are shown, so we set the height to auto
:
幻灯片的高度将为500px,我们需要将溢出设置为隐藏,以便我们看不到项目飞出。 当由于未启用JavaScript而无法构建幻灯片时,我们需要确保显示所有幻灯片,因此我们将高度设置为auto
:
.no-js .slides {
height: auto;
}
Each list item will be positioned absolutely and occupy all available width and height. By default, we’ll set the visibility to hidden
. Each slide will also serve as the perspective container and we’ll define a perspective value of 1600px:
每个列表项都将绝对放置并占据所有可用的宽度和高度。 默认情况下,我们将可见性设置为hidden
。 每张幻灯片还将用作透视图容器,我们将定义一个1600px的透视图值:
.slides > li {
width: 100%;
height: 100%;
position: absolute;
visibility: hidden;
perspective: 1600px;
}
Let’s not forget the fallback:
我们不要忘记后备:
.no-js .slides > li {
position: relative;
visibility: visible;
}
The navigation which is added dynamically, will appear as a set of lines. Each navigation item is a span and although we are using a tiny line, we want to make sure that the clickable area is actually bigger. This we can simulate by adding a white border:
动态添加的导航将显示为一组行。 每个导航项都是一个跨度,尽管我们使用的是细线,但我们要确保可点击区域实际上更大。 我们可以通过添加白色边框来模拟:
.slideshow > nav {
text-align: center;
margin-top: 20px;
}
.slideshow > nav span {
display: inline-block;
width: 60px;
height: 25px;
border-top: 10px solid #fff;
border-bottom: 10px solid #fff;
background-color: #ddd;
cursor: pointer;
margin: 0 3px;
transition: background-color 0.2s;
}
.slideshow > nav span:hover {
background-color: #333;
}
.slideshow > nav span.current {
background-color: #aaa;
}
The description will fill half of the width and since we want a transition on the opacity, we need to set it to 0 initially:
描述将填充宽度的一半,并且由于我们要对不透明度进行过渡,因此我们需要将其初始设置为0:
.description {
width: 50%;
padding: 2em 4em;
font-size: 1.5em;
position: relative;
z-index: 1000;
opacity: 0;
}
.no-js .description {
opacity: 1;
}
.description h2 {
font-size: 200%;
}
Now, let’s style the most crucial element in our slideshow. The division with the class “tiltview” will help us put our items into perspective. We need to add preserve-3d
as transform style because some inner items will need to move on the Z-axis in some animations. The “tiltview” wrapper will be centered by setting the top to 50% and transforming it -50% on the Y-axis. We’ll also rotate it on the X and Z-axis to create the 3D look:
现在,让我们设置幻灯片中最关键的元素的样式。 类别为“倾斜视图”的部门将帮助我们将我们的项目放在透视图中。 由于某些内部项目在某些动画中需要在Z轴上移动,因此我们需要添加preserve-3d
作为变换样式。 通过将顶部设置为50%并将其在Y轴上转换为-50%,将“倾斜视图”包装器居中。 我们还将在X和Z轴上旋转它以创建3D外观:
.tiltview {
position: absolute;
left: 50%;
width: 50%;
top: 50%;
transform-style: preserve-3d;
transform: translateY(-50%) rotateX(60deg) rotateZ(35deg);
}
And the anchors and images will have the following style (the outline helps to avoid jagged edges in Firefox):
锚点和图像将具有以下样式(轮廓有助于避免Firefox中出现锯齿状边缘):
.tiltview a {
outline: 1px solid transparent;
}
.tiltview a,
.tiltview a img {
max-width: 100%;
display: block;
margin: 0 auto;
}
.tiltview a:first-child {
margin-bottom: 30px;
}
For the row and column cases we’ll set the widths accordingly:
对于行和列的情况,我们将相应地设置宽度:
.tiltview.row a {
width: 48%;
width: calc(50% - 15px);
margin: 0;
}
.tiltview.row a:nth-child(2) {
left: 50%;
left: calc(50% + 15px);
position: absolute;
top: 0;
}
In our script we will use the classes “show” and “hide” to control the visibility of the slides:
在我们的脚本中,我们将使用“显示”和“隐藏”类来控制幻灯片的可见性:
/* Show/Hide */
.slides > li.current,
.slides > li.show {
visibility: visible;
}
The description will fade in and out:
说明将淡入和淡出:
.description {
transition: opacity 0.75s;
}
.current .description,
.show .description {
opacity: 1;
}
.hide .description {
opacity: 0;
}
As we mentioned before, we’ll control the animations by using some data-attributes. We have to define two types of animations: the incoming one (when we show the next slide) and the outgoing one (when we hide the previous slide). We want to be able to animate the items in all possible directions, so we’ll need six different types: move up, move down, slide up, slide down, slide left, slide right. This makes a total of 12 animations (incoming and outgoing).
如前所述,我们将通过使用一些数据属性来控制动画。 我们必须定义两种类型的动画:传入的动画(当我们显示下一张幻灯片时)和传出的动画(当我们隐藏上一张幻灯片时)。 我们希望能够在所有可能的方向上对项目进行动画处理,因此我们需要六种不同的类型:向上移动,向下移动,向上滑动,向下滑动,向左滑动,向右滑动。 总共制作了12个动画(传入和传出)。
So, let’s define the first one for moving the outgoing element up:
因此,让我们定义第一个向上移动传出元素的元素:
/***********************/
/* Move up */
/***********************/
.hide[data-effect-out="moveUpOut"] .tiltview a {
animation: moveUpOut 1.5s both;
}
.hide[data-effect-out="moveUpOut"] .tiltview a:nth-child(2) {
animation-delay: 0.25s;
}
@keyframes moveUpOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateZ(-30px);
}
100% {
transform: translateZ(3000px);
}
}
We define a slight animation delay for the second item and we use a custom cubic-bezier timing function to add some interesting momentum.
我们为第二项定义了轻微的动画延迟,并使用自定义三次贝塞尔曲线定时功能添加了一些有趣的动量。
The second animation for this movement is the incoming one which has the initial and end step reversed:
此动作的第二个动画是传入的动画,其初始步骤和结束步骤相反:
.show[data-effect-in="moveUpIn"] .tiltview a {
animation: moveUpIn 1.5s 0.5s both;
}
.show[data-effect-in="moveUpIn"] .tiltview a:nth-child(2) {
animation-delay: 0.75s;
}
@keyframes moveUpIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateZ(-3000px);
}
75% {
transform: translateZ(30px);
}
100% {
transform: translateZ(0);
}
}
As you might have noticed, we could simplify the animation delay for the “hide” and the “show” case, but keeping the two rules separated will allow for easier adaption in case you’d like to define some different delays.
您可能已经注意到,我们可以简化“隐藏”和“显示”情况的动画延迟,但是如果您想定义一些不同的延迟,则将这两个规则分开可以更轻松地进行调整。
The resting animations are as follows:
其余的动画如下:
/***********************/
/* Move down */
/***********************/
.hide[data-effect-out="moveDownOut"] .tiltview a {
animation: moveDownOut 1.5s both;
}
.hide[data-effect-out="moveDownOut"] .tiltview a:nth-child(2) {
animation-delay: 0.25s;
}
@keyframes moveDownOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateZ(30px);
}
100% {
transform: translateZ(-3000px);
}
}
.show[data-effect-in="moveDownIn"] .tiltview a {
animation: moveDownIn 1.5s 0.5s both;
}
.show[data-effect-in="moveDownIn"] .tiltview a:nth-child(2) {
animation-delay: 0.75s;
}
@keyframes moveDownIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateZ(3000px);
}
75% {
transform: translateZ(-30px);
}
100% {
transform: translateZ(0);
}
}
/***********************/
/* Slide up */
/***********************/
.hide[data-effect-out="slideUpOut"] .tiltview a {
animation: slideUpOut 1.5s both;
}
.hide[data-effect-out="slideUpOut"] .tiltview a:nth-child(2) {
animation-delay: 0.25s;
}
@keyframes slideUpOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateY(30px);
}
100% {
transform: translateY(-3000px);
}
}
.show[data-effect-in="slideUpIn"] .tiltview a {
animation: slideUpIn 1.5s 0.5s both;
}
.show[data-effect-in="slideUpIn"] .tiltview a:nth-child(2) {
animation-delay: 0.75s;
}
@keyframes slideUpIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateY(3000px);
}
75% {
transform: translateY(-30px);
}
100% {
transform: translateY(0);
}
}
/***********************/
/* Slide down */
/***********************/
.hide[data-effect-out="slideDownOut"] .tiltview a {
animation: slideDownOut 1.5s both;
}
.hide[data-effect-out="slideDownOut"] .tiltview.row a:nth-child(2),
.hide[data-effect-out="slideDownOut"] .tiltview.col a:first-child {
animation-delay: 0.25s;
}
@keyframes slideDownOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateY(-30px);
}
100% {
transform: translateY(3000px);
}
}
.show[data-effect-in="slideDownIn"] .tiltview a {
animation: slideDownIn 1.5s 0.5s both;
}
.show[data-effect-in="slideDownIn"] .tiltview.row a:nth-child(2),
.show[data-effect-in="slideDownIn"] .tiltview.col a:first-child {
animation-delay: 0.75s;
}
@keyframes slideDownIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateY(-3000px);
}
75% {
transform: translateY(30px);
}
100% {
transform: translateY(0);
}
}
/***********************/
/* Slide left */
/***********************/
.hide[data-effect-out="slideLeftOut"] .tiltview a {
animation: slideLeftOut 1.5s both;
}
.hide[data-effect-out="slideLeftOut"] .tiltview a:nth-child(2) {
animation-delay: 0.25s;
}
@keyframes slideLeftOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateX(30px);
}
100% {
transform: translateX(-5000px);
}
}
.show[data-effect-in="slideLeftIn"] .tiltview a {
animation: slideLeftIn 1.5s 0.5s both;
}
.show[data-effect-in="slideLeftIn"] .tiltview a:nth-child(2) {
animation-delay: 0.75s;
}
@keyframes slideLeftIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateX(3000px);
}
75% {
transform: translateX(-30px);
}
100% {
transform: translateX(0);
}
}
/***********************/
/* Slide right */
/***********************/
.hide[data-effect-out="slideRightOut"] .tiltview a {
animation: slideRightOut 1.5s both;
}
.hide[data-effect-out="slideRightOut"] .tiltview.col a:nth-child(2),
.hide[data-effect-out="slideRightOut"] .tiltview.row a:first-child {
animation-delay: 0.25s;
}
@keyframes slideRightOut {
25% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateX(-30px);
}
100% {
transform: translateX(3000px);
}
}
.show[data-effect-in="slideRightIn"] .tiltview a {
animation: slideRightIn 1.5s 0.5s both;
}
.show[data-effect-in="slideRightIn"] .tiltview.col a:nth-child(2),
.show[data-effect-in="slideRightIn"] .tiltview.row a:first-child {
animation-delay: 0.75s;
}
@keyframes slideRightIn {
0% {
animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
transform: translateX(-5000px);
}
75% {
transform: translateX(30px);
}
100% {
transform: translateX(0);
}
}
Note that for some animations we need to define the animation delay for the first child instead of the second. We need to do this so that the anchors don’t overlap each other for some directions.
请注意,对于某些动画,我们需要为第一个孩子而不是第二个孩子定义动画延迟。 我们需要这样做,以使锚点在某些方向上不会相互重叠。
When we don’t have support for CSS 3D Transforms or transform-style: preserve-3d
, then we want to provide a simple fallback:
当我们不支持CSS 3D Transforms或transform-style: preserve-3d
,我们想提供一个简单的备用:
/* Fallback for no 3D Transforms and no preserve-3d */
.no-csstransformspreserve3d .show .tiltview a,
.no-csstransformspreserve3d .hide .tiltview a,
.no-csstransforms3d .show .tiltview a,
.no-csstransforms3d .hide .tiltview a {
animation: none !important;
}
.no-csstransforms3d .tiltview.col {
top: -50%;
}
.no-csstransforms3d .tiltview.row {
top: 20px;
}
And last, but not least, we need to make sure that we have a reasonable look for smaller screens. In this case we want the anchors with their screenshots to be just decoration, so we’ll set their opacity lower and make them be not clickable:
最后但并非最不重要的一点是,我们需要确保小屏幕的外观合理。 在这种情况下,我们希望锚点及其屏幕截图只是装饰,因此我们将其不透明度设置得较低,并使它们不可单击:
@media screen and (max-width: 65.3125em) {
.description,
.tiltview {
width: 100%;
}
.tiltview {
left: 0;
opacity: 0.3;
pointer-events: none;
}
}
@media screen and (max-width: 33.75em) {
.description {
font-size: 1.1em;
}
.slideshow > nav span {
width: 20px;
height: 40px;
margin: 0 10px;
}
}
@media screen and (max-width: 24em) {
.slides {
height: 320px;
}
.description {
font-size: 1em;
padding: 1.4em;
}
.no-csstransforms3d .tiltview.col,
.no-csstransforms3d .tiltview.row {
top: 0;
}
}
And that’s the style! Let’s do our slideshow script.
这就是风格! 让我们来做幻灯片脚本。
JavaScript (The JavaScript)
We will start by initializing some variables, like the two arrays with the animation class names that control the incoming and outgoing of our items. These will be picked randomly and set to the items when we navigate the slideshow. Other variables are the items, the current value of the selected item (we assume this will be the first one) and the total number of items inside the slider:
我们将从初始化一些变量开始,例如两个带有动画类名称的数组,它们控制着项目的传入和传出。 当我们浏览幻灯片时,这些将被随机选择并设置为项目。 其他变量是项目,所选项目的当前值(假设这是第一个)以及滑块内的项目总数:
function TiltSlider( el, options ) {
this.el = el;
// available effects for the animations (animation class names) - when an item comes in or goes out
this.animEffectsOut = ['moveUpOut','moveDownOut','slideUpOut','slideDownOut','slideLeftOut','slideRightOut'];
this.animEffectsIn = ['moveUpIn','moveDownIn','slideUpIn','slideDownIn','slideLeftIn','slideRightIn'];
// the items
this.items = this.el.querySelector( 'ol.slides' ).children;
// total number of items
this.itemsCount = this.items.length;
if( !this.itemsCount ) return;
// index of the current item
this.current = 0;
this.options = extend( {}, this.options );
extend( this.options, options );
this._init();
}
In order to navigate the slideshow we will add some navigation spans that, when clicked, will make the respective slideshow item appear. The total number of spans will be the same as the total number of items. Let’s add the navigation to our component:
为了浏览幻灯片,我们将添加一些导航范围,当单击它们时,将显示相应的幻灯片项。 跨度的总数将与项目的总数相同。 让我们将导航添加到我们的组件中:
TiltSlider.prototype._addNavigation = function() {
// add nav "dots"
this.nav = document.createElement( 'nav' )
var inner = '';
for( var i = 0; i < this.itemsCount; ++i ) {
inner += i === 0 ? '' : '';
}
this.nav.innerHTML = inner;
this.el.appendChild( this.nav );
this.navDots = [].slice.call( this.nav.children );
}
Next, we need to bind the onclick event to the navigation spans. If we click on any other but the current span, then the current item should animate out and the new one should animate in.
接下来,我们需要将onclick事件绑定到导航范围。 如果单击除当前跨度以外的任何其他跨度,则当前项目应设置动画,而新项目应设置动画。
TiltSlider.prototype._initEvents = function() {
var self = this;
// show a new item when clicking the navigation "dots"
this.navDots.forEach( function( dot, idx ) {
dot.addEventListener( 'click', function() {
if( idx !== self.current ) {
self._showItem( idx );
}
} );
} );
}
We need to reference and work with the current item and also the next one that has to appear. We will add and remove classes from both of them in order to apply the respective animations. The animation (i.e. the data-attribute value) itself will be randomly picked from our animEffectsOut
and animEffectsIn
arrays as described before.
我们需要引用并使用当前项以及必须出现的下一项。 我们将在这两个类中添加和删除类,以应用各自的动画。 animEffectsOut
,动画(即数据属性值)本身将从我们的animEffectsOut
和animEffectsIn
数组中随机选取。
TiltSlider.prototype._showItem = function( pos ) {
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
classie.removeClass( this.navDots[ this.current ], 'current' );
var self = this,
// the current item
currentItem = this.items[ this.current ];
this.current = pos;
// next item to come in
var nextItem = this.items[ this.current ],
// set random effects for the items
outEffect = this.animEffectsOut[ Math.floor( Math.random() * this.animEffectsOut.length ) ],
inEffect = this.animEffectsIn[ Math.floor( Math.random() * this.animEffectsOut.length ) ];
currentItem.setAttribute( 'data-effect-out', outEffect );
nextItem.setAttribute( 'data-effect-in', inEffect );
classie.addClass( this.navDots[ this.current ], 'current' );
var cntAnims = 0,
// the number of elements that actually animate inside the current item
animElemsCurrentCount = currentItem.querySelector( '.tiltview' ).children.length,
// the number of elements that actually animate inside the next item
animElemsNextCount = nextItem.querySelector( '.tiltview' ).children.length,
// keep track of the number of animations that are terminated
animEndCurrentCnt = 0, animEndNextCnt = 0,
// check function for the end of each animation
isFinished = function() {
++cntAnims;
if( cntAnims === 2 ) {
self.isAnimating = false;
}
},
// function for the end of the current item animation
onEndAnimationCurrentItem = function() {
++animEndCurrentCnt;
var endFn = function() {
classie.removeClass( currentItem, 'hide' );
classie.removeClass( currentItem, 'current' );
isFinished();
};
if( !isSupported ) {
endFn();
}
else if( animEndCurrentCnt === animElemsCurrentCount ) {
currentItem.removeEventListener( animEndEventName, onEndAnimationCurrentItem );
endFn();
}
},
// function for the end of the next item animation
onEndAnimationNextItem = function() {
++animEndNextCnt;
var endFn = function() {
classie.removeClass( nextItem, 'show' );
classie.addClass( nextItem, 'current' );
isFinished();
};
if( !isSupported ) {
endFn();
}
else if( animEndNextCnt === animElemsNextCount ) {
nextItem.removeEventListener( animEndEventName, onEndAnimationNextItem );
endFn();
}
};
if( isSupported ) {
currentItem.addEventListener( animEndEventName, onEndAnimationCurrentItem );
nextItem.addEventListener( animEndEventName, onEndAnimationNextItem );
}
else {
onEndAnimationCurrentItem();
onEndAnimationNextItem();
}
classie.addClass( currentItem, 'hide' );
classie.addClass( nextItem, 'show' );
}
And that’s it! We hope you enjoyed this tutorial and find it useful!
就是这样! 我们希望您喜欢本教程并发现它有用!
Note that IE11 still does not support transform-style: preserve-3d
, so the fallback will be shown there.
请注意,IE11仍不支持transform-style: preserve-3d
,因此将在此处显示后备。
翻译自: https://tympanus.net/codrops/2014/03/13/tilted-content-slideshow/
幻灯片大小