轻松实现动画片段幻灯片
资源下载:
介绍
本文使用"Pieces"库轻松实现动画片段幻灯片效果。
image
今天我们想向您展示如何创建一个具有动画片段幻灯片效果的图片。 图片被分成多个片段,这些片段将以不同的方式进行动画制作,使用Pieces,可以轻松实现这些有趣的效果。
这将是最终结果:
image
动画由anime.js提供支持。
最初的想法
这种效果的灵感来源Dribbble的Shift动画:
Shift Animation (转移动画)
开始使用Pieces
有关Pieces库的所有详细文档都可以在其Github存储库中找到。 但无论如何,让我们快速看到一些基本概念,以便能够开始使用这个库。
如果我们要绘制一个带有动画的图片,这些是构成场景的基本元素:
imagePieces Basic Elements (Pieces基本元素)
如您所见,我们想要绘制的图像将是我们的项item,它被分成几个pieces,根据我们定义的选项,它们的大小和位置也可能不同。 要查看所有可能的选项,我建议您查看Github上的文档。
在整个教程中,我们将解释每段代码,以便您可以学习如何使用Pieces库实现自己的动画。 开始吧!
HTML 结构
在开始编写Javascript代码之前,让我们看看我们如何为slider定义HTML。 标记非常简单,因为我们每个幻灯片都有相应的图像和文本,canvas元素用于动画,而按钮用于浏览slider。
<!-- Pieces Slider -->
<div class="pieces-slider">
<!-- 每一个slider都有对应的图像和文本 -->
<div class="pieces-slider__slide">
<img class="pieces-slider__image" src="img/ricardo-gomez-angel-381749.jpg" alt="">
<div class="pieces-slider__text">Ricardo Gomez Angel</div>
</div>
<div class="pieces-slider__slide">
<img class="pieces-slider__image" src="img/josh-calabrese-527813.jpg" alt="">
<div class="pieces-slider__text">Josh Calabrese</div>
</div>
<div class="pieces-slider__slide">
<img class="pieces-slider__image" src="img/samuel-zeller-103111.jpg" alt="">
<div class="pieces-slider__text">Samuel Zeller</div>
</div>
<div class="pieces-slider__slide">
<img class="pieces-slider__image" src="img/sweet-ice-cream-photography-143023.jpg" alt="">
<div class="pieces-slider__text">Sweet Ice Cream</div>
</div>
<div class="pieces-slider__slide">
<img class="pieces-slider__image" src="img/sticker-mule-199237.jpg" alt="">
<div class="pieces-slider__text">Sticker Mule</div>
</div>
<!-- Canvas 绘制 pieces -->
<canvas class="pieces-slider__canvas"></canvas>
<!-- Slider 按钮: prev 和 next -->
<button class="pieces-slider__button pieces-slider__button--prev">prev</button>
<button class="pieces-slider__button pieces-slider__button--next">next</button>
</div>
设置slider的样式
需要一些特殊的样式来实现我们的效果。 我们需要隐藏图像和文本,因为我们将使用我们的库重绘它们。 但是如果没有可用的JavaScript,我们也希望它们回退到它们的初始标记。 最后,我们需要确保slider响应媒体查询:
.pieces-slider {
position: relative;
text-align: center;
padding: 8rem 0;
}
.js .pieces-slider {
padding: 0;
}
/* Make all slides absolutes and hide them */
.js .pieces-slider__slide {
position: absolute;
right: 100%;
}
/* Define image dimensions and also hide them */
.pieces-slider__image {
max-width: 600px;
max-height: 400px;
}
.js .pieces-slider__image {
visibility: hidden;
}
/* Hide the titles */
.js .pieces-slider__text {
text-indent: -9999px;
}
/* Canvas with viewport width and height */
.js .pieces-slider__canvas {
position: relative;
width: 100vw;
height: 100vh;
transition: 0.2s opacity;
}
/* Class for when we resize */
.pieces-slider__canvas--hidden {
opacity: 0;
transition-duration: 0.3s;
}
/* Navigation buttons */
.pieces-slider__button {
position: absolute;
left: 0;
top: 50%;
width: 100px;
height: 100px;
margin: -25px 0 0 0;
background-color: #5104ab;
color: #fff;
font-family: inherit;
font-weight: bold;
border: none;
cursor: pointer;
transition: 0.1s background-color;
}
.pieces-slider__button:hover {
background: #5f3abf;
}
.pieces-slider__button--next {
left: auto;
right: 0;
}
/* Hide the buttons when no JS */
.no-js .pieces-slider__button {
display: none;
}
/* Media queries with styles for smaller screens */
@media screen and (max-width: 720px) {
.pieces-slider__image {
max-width: 300px;
}
}
@media screen and (max-width: 55em) {
.pieces-slider__canvas {
width: 100vw;
height: 100vw;
}
.pieces-slider__button {
width: 60px;
height: 60px;
}
}
如您所见,我们隐藏了我们为滑块定义的HTML元素(按钮除外),因为我们将在canvas元素中绘制所有内容。
使用Pieces为slider设置动画
让我们定义一些变量并从DOM获取slider信息:
// Get all images and texts, get the `canvas` element, and save slider length
var sliderCanvas = document.querySelector('.pieces-slider__canvas');
var imagesEl = [].slice.call(document.querySelectorAll('.pieces-slider__image'));
var textEl = [].slice.call(document.querySelectorAll('.pieces-slider__text'));
var slidesLength = imagesEl.length;
然后我们需要定义索引变量来处理我们在画布上绘制的所有item:
// Define indexes related variables, as we will use indexes to reference items
var currentIndex = 0, currentImageIndex, currentTextIndex, currentNumberIndex;
var textIndexes = [];
var numberIndexes = [];
// Update current indexes for image, text and number
function updateIndexes() {
currentImageIndex = currentIndex * 3;
currentTextIndex = currentImageIndex + 1;
currentNumberIndex = currentImageIndex + 2;
}
updateIndexes();
现在我们将开始为每种类型的项目(图像,文本,数字和按钮)定义选项。 您可以在Pieces文档中找到完整的参考,这里详细解释了用于绘制图像的每个选项的作用:
// Options for images
var imageOptions = {
angle: 45, // rotate item pieces 45deg
extraSpacing: {extraX: 100, extraY: 200}, // this extra spacing is needed to cover all the item, because angle != 0
piecesWidth: function() { return Pieces.random(50, 200); }, // every piece will have a random width between 50px and 200px
ty: function() { return Pieces.random(-400, 400); } // every piece will be translated in the Y axis a random distance between -400px and 400px
};
同样,我们将为其他项类型定义选项。 请参阅注释以了解所使用的一些属性:
/ Options for texts
var textOptions = {
color: 'white',
backgroundColor: '#0066CC',
fontSize: function() { return windowWidth > 720 ? 50 : 30; },
padding: '15 20 10 20',
angle: -45,
extraSpacing: {extraX: 0, extraY: 300},
piecesWidth: function() { return Pieces.random(50, 200); },
ty: function() { return Pieces.random(-200, 200); },
translate: function() {
if (windowWidth > 1120) return {translateX: 200, translateY: 200};
if (windowWidth > 720) return {translateX: 0, translateY: 200};
return {translateX: 0, translateY: 100};
}
};
// Options for numbers
var numberOptions = {
color: 'white',
backgroundColor: '#0066CC',
backgroundRadius: 300,
fontSize: function() { return windowWidth > 720 ? 100 : 50; },
padding: function() { return windowWidth > 720 ? '18 35 10 38' : '18 25 10 28'; },
angle: 0,
piecesSpacing: 2,
extraSpacing: {extraX: 10, extraY: 10},
piecesWidth: 35,
ty: function() { return Pieces.random(-200, 200); },
translate: function() {
if (windowWidth > 1120) return {translateX: -340, translateY: -180};
if (windowWidth > 720) return {translateX: -240, translateY: -180};
return {translateX: -140, translateY: -100};
}
};
现在我们为每种类型的项目提供了所有选项,让它们放在一起将它传递给Pieces库!
// Build the array of items to draw using Pieces
var items = [];
var imagesReady = 0;
for (var i = 0; i < slidesLength; i++) {
// Wait for all images to load before initializing the slider and event listeners
var slideImage = new Image();
slideImage.onload = function() {
if (++imagesReady == slidesLength) {
initSlider();
initEvents();
}
};
// Push all elements for each slide with the corresponding options
items.push({type: 'image', value: imagesEl[i], options: imageOptions});
items.push({type: 'text', value: textEl[i].innerText, options: textOptions});
items.push({type: 'text', value: i + 1, options: numberOptions});
// Save indexes
textIndexes.push(i * 3 + 1);
numberIndexes.push(i * 3 + 2);
// Set image src
slideImage.src = imagesEl[i].src;
}
除了构建元素数组之外,在前面的代码块中,我们定义了一个简单的机制,只有在加载了所有图像时才调用initSlider函数。 这非常重要,因为我们无法使用Pieces绘制不可用的图像。
到目前为止,我们还没有画任何东西,但我们现在已经准备好了。 让我们看看我们如何初始化一个新的Pieces实例。
// Save the new Pieces instance
piecesSlider = new Pieces({
canvas: sliderCanvas, // CSS selector to get the canvas
items: items, // the Array of items we've built before
x: 'centerAll', // center all items in the X axis
y: 'centerAll', // center all items in the Y axis
piecesSpacing: 1, // default spacing between pieces
fontFamily: ["'Helvetica Neue', sans-serif"],
animation: { // animation options to use in any operation
duration: function() { return Pieces.random(1000, 2000); },
easing: 'easeOutQuint'
},
debug: false // set `debug: true` to enable debug mode
});
现在,所有的items和pieces都可以添加动画。 它们实际上已经创建的好了,但默认是隐藏的,所以,让我们看看如何显示第一张幻灯片并启动我们想要的动画:
// Animate all numbers to rotate clockwise indefinitely
piecesSlider.animateItems({
items: numberIndexes,
duration: 20000,
angle: 360,
loop: true
});
// Show current items: image, text and number
showItems();
因此,要显示和隐藏当前项目,我们需要分别调用showItems和hideItems函数。 我们已经实现了如下:
// Show current items: image, text and number
function showItems() {
// Show image pieces
piecesSlider.showPieces({items: currentImageIndex, ignore: ['tx'], singly: true, update: (anim) => {
// Stop the pieces animation at 60%, and run a new indefinitely animation of `ty` for each piece
if (anim.progress > 60) {
var piece = anim.animatables[0].target;
var ty = piece.ty;
anime.remove(piece);
anime({
targets: piece,
ty: piece.h_ty < 300
? [{value: ty + 10, duration: 1000}, {value: ty - 10, duration: 2000}, {value: ty, duration: 1000}]
: [{value: ty - 10, duration: 1000}, {value: ty + 10, duration: 2000}, {value: ty, duration: 1000}],
duration: 2000,
easing: 'linear',
loop: true
});
}
}});
// Show pieces for text and number, using alternate `ty` values
piecesSlider.showPieces({items: currentTextIndex});
piecesSlider.showPieces({items: currentNumberIndex, ty: function(p, i) { return p.s_ty - [-3, 3][i % 2]; }});
}
// Hide current items: image, text and number
function hideItems() {
piecesSlider.hidePieces({items: [currentImageIndex, currentTextIndex, currentNumberIndex]});
}
最后,为了浏览slides,我们已经定义了这些功能:
// Select the prev slide: hide current items, update indexes, and show the new current item
function prevItem() {
hideItems();
currentIndex = currentIndex > 0 ? currentIndex - 1 : slidesLength - 1;
updateIndexes();
showItems();
}
// Select the next slide: hide current items, update indexes, and show the new current item
function nextItem() {
hideItems();
currentIndex = currentIndex < slidesLength - 1 ? currentIndex + 1 : 0;
updateIndexes();
showItems();
}
因此,如果单击导航按钮或按下某些箭头键(向左或向右),我们需要调用这些函数:
// Select prev or next slide using buttons
prevButtonEl.addEventListener('click', prevSlide);
nextButtonEl.addEventListener('click', nextSlide);
// Select prev or next slide using arrow keys
document.addEventListener('keydown', function (e) {
if (e.keyCode == 37) { // left
prevSlide();
} else if (e.keyCode == 39) { // right
nextSlide();
}
});
我们差不多完成🙂我们只需要实现响应行为,监听resize事件,保存当前窗口宽度,以及重新初始化滑块:
// Handle `resize` event
window.addEventListener('resize', resizeStart);
var initial = true, hideTimer, resizeTimer;
// User starts resizing, so wait 300 ms before reinitialize the slider
function resizeStart() {
if (initial) {
initial = false;
if (hideTimer) clearTimeout(hideTimer);
sliderCanvas.classList.add('pieces-slider__canvas--hidden');
}
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(resizeEnd, 300);
}
// User ends resizing, then reinitialize the slider
function resizeEnd() {
initial = true;
windowWidth = window.innerWidth;
initSlider();
hideTimer = setTimeout(() => {
sliderCanvas.classList.remove('pieces-slider__canvas--hidden');
}, 500);
}
我们终于完成了!
我们真的希望本教程有用,并且您已经从这些效果中找到了灵感!
参考
- Animated Fragment Slideshow:https://tympanus.net/codrops/2018/02/21/animated-fragment-slideshow/