【Share Code | HTML & CSS & Javascript】動畫片段幻燈片
資源下載:
介紹
本文使用"Pieces"庫輕鬆實現動畫片段幻燈片效果。
今天我們想向您展示如何建立一個具有動畫片段幻燈片效果的圖片。 圖片被分成多個片段,這些片段將以不同的方式進行動畫製作,使用Pieces,可以輕鬆實現這些有趣的效果。
這將是最終結果:
最初的想法
這種效果的靈感來源Dribbble的Shift動畫:
開始使用Pieces
有關Pieces庫的所有詳細文件都可以在其Github儲存庫中找到。 但無論如何,讓我們快速看到一些基本概念,以便能夠開始使用這個庫。
如果我們要繪製一個帶有動畫的圖片,這些是構成場景的基本元素:
Pieces 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)