Анимация фрагментов для слайд-шоу

Анимация фрагментов для слайд-шоу

От автора: сегодня мы хотим вам рассказать, как создать веселый слайдер с оригинальной фрагментированной графикой. Все элементы слайдера будут разделены на части с разной анимацией. Анимация на основе библиотеки Pieces – я написал эту библиотеку для создания интересных эффектов.

Анимация для слайд шоу даст следующий конечный результат:

ДЕМО | Исходники

Анимация под управлением anime.js.

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее

Оригинальная идея

Источником вдохновения эффектов послужил Dribbble Shift Animation от Alexander Saunki:

Как только я увидел это, я сразу захотел воссоздать такой эффект на своем недавно запущенном сайте lmgonzalves.com.

Для достижения эффекта я разработал библиотеку Pieces. Она позволяет рисовать и анимировать текст, изображения и пути SVG через прямоугольные фигуры. Без дальнейших разглагольствований давайте узнаем, как использовать библиотеку!

Приступая к работе с Pieces

Подробную документацию о Pieces можно найти в репозитории на Github. Тем не менее, давайте разберем парочку основных концепций, чтобы можно было приступить к использованию этой библиотеки.

Предполагаем, что будем рисовать и анимировать изображение. Поэтому изображения будут базовыми элементами сцены.

Изображение, которое мы будем рисовать – item. Оно будет разделено на несколько частей pieces, которые будут иметь разные размеры и положение, исходя из заданных параметров. Все возможные варианты можно посмотреть в документации на Github.

В уроке мы объясним каждую строчку кода, чтобы вы смогли создать свою анимацию с помощью библиотеки Pieces. Начнем!

Структура HTML

Прежде чем писать JS, давайте разберем HTML слайдера. Разметка простая – у нас есть слайды со своими изображениями и текстом, canvas для анимации и кнопки навигации по слайдеру.

<!-- Pieces Slider -->
<div class="pieces-slider">
 <!-- Each slide with corresponding image and text -->
 <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 to draw the pieces -->
 <canvas class="pieces-slider__canvas"></canvas>
 <!-- Slider buttons: prev and next -->
 <button class="pieces-slider__button pieces-slider__button--prev">prev</button>
 <button class="pieces-slider__button pieces-slider__button--next">next</button>
</div>

Стилизация слайдера

Для эффектов в слайдшоу понадобится добавить особые стили. Необходимо прятать изображения и текст – мы будем рисовать их заново с помощью библиотеки. Изображения должны быть фолбеком на случай отключения JS. С помощью пары медиа запросов необходимо определить адаптивность слайдера:

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее
.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 для анимации слайдера

Начнем с определения переменных и получения информации о слайдере из DOM:

// 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;

Далее необходимо определить переменные-индексы для обработки всех элементов, рисуемых на canvas:

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

Точно так же мы определим опции для других типов элементов. Смотрите комментарии, чтобы понять используемые свойства:

// Опции для текста
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};
 }
};

// Опции для чисел
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, options: imageOptions});
 items.push({type: 'text', value: textEl.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.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
});

Все элементы и части готовы к анимации. Они созданы, но скрыты по умолчанию. Давайте посмотрим, как показать первый слайд и привязать к нему анимацию:

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

// Hide current items: image, text and number
function hideItems() {
 piecesSlider.hidePieces({items: [currentImageIndex, currentTextIndex, currentNumberIndex]});
}

Для перемещения между слайдами мы написали эти функции:

// 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, сохраняю текущую width окна и пересоздавая слайдер:

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

Теперь все! Надеемся, этот урок был полезен, и вы нашли вдохновение!

Автор: Luis Manuel

Источник: https://tympanus.net/

Редакция: Команда webformyself.

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее

Практика HTML5 и CSS3 с нуля до результата!

Получите бесплатный пошаговый видеокурс по основам адаптивной верстки с полного нуля на HTML5 и CSS3

Получить

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree