Share Code

轻松实现动画片段幻灯片

2018-11-26  本文已影响0人  JKol123

资源下载:

介绍

本文使用"Pieces"库轻松实现动画片段幻灯片效果。


image

今天我们想向您展示如何创建一个具有动画片段幻灯片效果的图片。 图片被分成多个片段,这些片段将以不同的方式进行动画制作,使用Pieces,可以轻松实现这些有趣的效果。

这将是最终结果:


image

动画由anime.js提供支持。

最初的想法

这种效果的灵感来源Dribbble的Shift动画:


Shift Animation (转移动画)

开始使用Pieces

有关Pieces库的所有详细文档都可以在其Github存储库中找到。 但无论如何,让我们快速看到一些基本概念,以便能够开始使用这个库。

如果我们要绘制一个带有动画的图片,这些是构成场景的基本元素:

image

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) { // 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);
}

我们终于完成了!

我们真的希望本教程有用,并且您已经从这些效果中找到了灵感!

参考

上一篇下一篇

猜你喜欢

热点阅读