Создание анимированной сетки с фотографиями на CSS и jQuery

Сегодня мы хотели бы показать вам, как создать очень простой интро эффект, где стопка палароид фотографий при помощи анимации, плавно переходит в сетку. Данный эффект вы можете увидеть на сайте takeit. На этом сайте сетка также имеет различную анимацию фотографий, но мы не будем анимировать саму сетку, создадим только сам переход.

Сложность

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

У нас есть страница интро с большим заголовком, несколько фотографии и изображение мобильного устройства со стрелкой. Также в демонстрационном примере присутствуют несколько ссылок. Нам необходимо создать анимацию перехода фотографий в сетку, в которой не должно быть ссылок и заголовка. Запуск анимации будет происходить либо при нажатии на кнопку, либо при прокрутке страницы.

Будем думать о сетке как о состоянии проекта по умолчанию. Мы не будем вычислять позицию фотографий, а затем переводить их в сетку, вместо этого, мы возьмем готовую сетку и уже опираясь от этого состояния, разбросаем её на фотографии. Мы используем первые шесть изображений сетки, переместим их в центральную точку и разбросаем по экрану случайным образом. То есть мы должны переместить изображение вверх на предыдущую секцию? Нет, мы можем использовать фиксированную секцию интро вместо анимации за пределами области просмотра, создавая иллюзию движения вниз.

Так как у нас будет много анимации, нам нужно её каким-то образом контролировать. В этом нам поможет библиотека dynamic.js, которая будет анимировать элементы, поэтому мы будем контролировать анимацию при помощи скрипта.

Большая часть макета будет работать на Flexbox. Если вы еще не знакомы с данной технологией, советуем прочитать статью Уроки Flexbox.

Изображения для демонстрационного примера мы взяли с сайта Unsplash.com.

HTML разметка

Как нам воссоздать эффект прокрутки без фактической прокрутки страницы? Мы можем просто переместить фиксированный фон за пределы области просмотра, создавая иллюзию прокрутки документа вниз. Для этого мы создадим секцию с классом page и модификатором page–mover. Внутри которой будет только загрузчик.

Основной заголовок не будет находится внутри этой секции, так как мы хотим анимировать его немного другим способом. Если бы мы расположили его внутри данной секции, пришлось бы еще определить движение родительского элемента, но нам это не нужно. Поэтому мы создадим отдельный контейнер для заголовка с классом title-wrap.

Затем мы создадим секцию для сетки, которая также будет иметь класс page, но с модификатором page static. В этой секции мы создадим еще один заголовок и неупорядоченный список в качестве сетки. Последним элементом в нашей разметке будет кнопка для загрузки.

Последние два элемента это изображение устройства и стрелка для вызова анимации и отображения сетки.

<div class="view">
	<header class="header">
		<!-- some links -->
	</header>

	<section class="page page--mover">
		<div class="la-square-loader"><div></div></div>
	</section>

	<div class="title-wrap">
		<h1 class="title title--main">OH<em>!</em>SNAP</h1>
		<p class="title title--sub">Smart Auto-Filtering for your shots.</p>
	</div>

	<section class="page page--static">

		<div class="page__title">
			<h2 class="page__title-main">Polaroid Stack to Grid Intro Animation</h2>
			<p class="page__title-sub">Recreating the effect seen on the <a href="http://www.takeitapp.co/en">takeit</a> website</p>
		</div>

		<ul class="grid">
			<li class="grid__item">
				<a class="grid__link" href="#">
					<img class="grid__img" src="/img/photos/1.jpg" alt="Some image" />
					<h3 class="grid__item-title">Natural saturation effects</h3>
				</a>
			</li>
			<li class="grid__item"><!-- ... --></li>
			<li class="grid__item"><!-- ... --></li>
			<li class="grid__item"><!-- ... --></li>
			<!-- ... -->
		</ul>

		<button class="button button--load" aria-label="Load more images">
			<svg class="polaroid" width="100%" height="100%" viewBox="0 0 220 243" preserveAspectRatio="xMidYMid meet">
				<rect class="polaroid__base" x="0" y="0" width="220" height="243" rx="5"></rect>
				<rect class="polaroid__inner" x="16" y="20" width="189" height="149"></rect>
				<g class="polaroid__loader">
					<circle cx="61.5" cy="94.5" r="17.5"></circle>
					<circle cx="110.5" cy="94.5" r="17.5"></circle>
					<circle cx="159.5" cy="94.5" r="17.5"></circle>
				</g>
			</svg>
			<span class="button__text">Load more</span>
		</button>

	</section>

	<div class="device">
		<div class="device__screen"></div>
	</div>

	<button id="showgrid" class="button button--view" aria-label="Show me more">
		<svg width="100%" height="100%" viewBox="0 0 310 177" preserveAspectRatio="xMidYMid meet">
			<path fill="#FFFFFF" d="M159.875,174.481L306.945,27.41c2.93-2.929,2.93-7.678,0-10.606L292.803,2.661c-1.406-1.407-3.314-2.197-5.303-2.197c-1.989,0-3.896,0.79-5.303,2.197L154.572,130.287L26.946,2.661c-1.406-1.407-3.314-2.197-5.303-2.197c-1.989,0-3.897,0.79-5.303,2.197L2.197,16.804C0.733,18.269,0,20.188,0,22.107s0.732,3.839,2.197,5.303l147.071,147.071C152.197,177.411,156.945,177.411,159.875,174.481L159.875,174.481z" />
		</svg>
	</button>
</div><!-- /view -->

CSS

Для начала сбросим стандартные стили:

*,
*::after,
*::before {
	box-sizing: border-box;
}

Установим стили по умолчанию для типографии и цвета:

body {
	font-family: 'Avenir Next', Avenir, 'Helvetica Neue', Helvetica, Arial, sans-serif;
	overflow-x: hidden;
	color: #a5aeb5;
	background: #e9ecef;
}

Нам понадобится вспомогательный класс, который запрещает первоначальную прокрутку (пока не загрузится JavaScript):

.js body {
	overflow: hidden;
}

.js body.overflow {
	overflow: auto;
}

Шапка с ссылками будет иметь свойство position: absolute;, также мы используем Flexbox для расположения внутренних элементов:

.header {
	position: absolute;
	z-index: 1000;
	display: -webkit-flex;
	display: flex;
	-webkit-justify-content: space-between;
	justify-content: space-between;
	-webkit-align-items: flex-start;
	align-items: flex-start;
	width: 100%;
	padding: 2.5em;
	pointer-events: none;
}

Теперь, давайте определим стили для страницы, которую будем перемещать. Так как это будет всего лишь иллюзией, мы зафиксируем её и зададим размер во весь экран. Фон сделаем темным:

.page--mover {
	position: fixed;
	width: 100%;
	height: 100vh;
	pointer-events: none;
	background: #2d323c;
}

Класс title-wrap расположим вверху страницы:

.title-wrap {
	position: absolute;
	z-index: 101;
	width: 100%;
	margin: 10vh 0 0 0;
	text-align: center;
	pointer-events: none;
}

Стиль типографии:

.title {
	line-height: 1;
	position: relative;
	text-indent: 0.2em;
	letter-spacing: 0.2em;
	text-transform: uppercase;
}

.title--main {
	font-size: 5.75em;
	margin: 0 auto;
	color: #df2d70;
}

.title--sub {
	font-size: 1.15em;
	font-weight: 700;
	display: block;
	margin: 0;
	color: #565f73;
}

Определим размер текста для больших экранов:

@media screen and (min-width: 100em) {
	.title--main {
		font-size: 7vw;
	}

	.title--sub {
		font-size: 1.35vw;
	}
}

Для позиционирования сетки мы воспользуемся Flexbox колонками, которые будут расположены по центру. Установим для них максимально допустимую ширину, чтобы расположить три фотографии в строке.

.page--static {
	display: -webkit-flex;
	display: flex;
	-webkit-flex-direction: column;
	flex-direction: column;
	-webkit-align-items: center;
	align-items: center;
	max-width: 1220px;
	margin: 0 auto;
	padding: 2em 0 0;
	text-align: center;
}

Стиль для заголовка сетки:

.page__title {
	padding: 0 2em;
}

.page__title-main {
	font-size: 2em;
	margin: 0 auto;
	padding: 3em 0 0;
	color: #03a9f4;
}

.page__title-sub {
	font-size: 1.05em;
	margin: 0.5em 0 4em;
}

Перейдем к стилю изображения мобильного устройства. Мы взяли скетч мобильного устройства из Facebook devices collection и экспортировали его в SVG. Он будет работать в качестве фонового изображения для контейнера device. Зафиксируем контейнер и зададим высокий z-index. Сделаем его квадратным по отношению высоты окна. Это дает гарантию того, что устройство никогда не станет выше половины экрана.

.device {
	position: fixed;
	z-index: 1000;
	bottom: 0;
	left: 50%;
	width: 45vh;
	height: 45vh;
	margin: 0 0 0 -22.5vh;
	background: url(../img/device.svg) no-repeat 50% 0%;
	background-size: 100%;
}

На экране мобильного устройства будет отображаться скриншот приложения, его размер установлен в процентном соотношении, относительно самого устройства:

.device__screen {
	position: absolute;
	top: 25.5%;
	left: 8.5%;
	width: 83%;
	height: 100%;
	background: url(../img/screen.jpg) no-repeat 50% 0%;
	background-size: 100%;
}

Перейдем к стилю стрелки и кнопки "Показать больше фотографий". Для начала сбросим основные стили:

.button {
	margin: 0;
	padding: 0;
	border: none;
	background: none;
}

Для кнопки view зададим свойство position: absolute и добавим небольшой переход. При наведении зальем фон синим цветом:

.button--view {
	position: absolute;
	z-index: 1000;
	top: 100vh;
	left: 50%;
	width: 3em;
	height: 1.75em;
	margin: -3em 0 0 -1.5em;
	-webkit-animation: pointDown 0.6s 0.6s ease-in infinite alternate forwards;
	animation: pointDown 0.6s 0.6s ease-in infinite alternate forwards;
	fill: #fff;
}

@-webkit-keyframes pointDown {
	from {
		-webkit-transform: translate3d(0,-10px,0);
		transform: translate3d(0,-10px,0);
	}
	to {
		-webkit-transform: translate3d(0,0,0);
		transform: translate3d(0,0,0);
	}
}

@keyframes pointDown {
	from {
		-webkit-transform: translate3d(0,-10px,0);
		transform: translate3d(0,-10px,0);
	}
	to {
		-webkit-transform: translate3d(0,0,0);
		transform: translate3d(0,0,0);
	}
}

.button--view path {
	-webkit-transition: fill 0.3s;
	transition: fill 0.3s;
	fill: inherit;
}

.button--view:hover,
.button--view:focus {
	outline: none;
	fill: #03a9f4;
}

Стиль кнопки "Показать больше фотографий":

.button--load {
	width: 7em;
	color: #a3b0bd;
}

.button--load svg {
	width: 2.5em;
}

.button__text {
	font-size: 0.65em;
	font-weight: bold;
	display: block;
	margin: 0.85em 0 0 0;
	text-indent: 3px;
	letter-spacing: 3px;
	text-transform: uppercase;
	color: inherit;
	-webkit-transition: color 0.3s;
	transition: color 0.3s;
}

Также внутри этой кнопки анимируем небольшие кружочки:

.polaroid__base,
.polaroid__loader {
	-webkit-transition: fill 0.3s;
	transition: fill 0.3s;
	fill: #a3b0bd;
}

.button--load:hover,
.button--load:focus {
	color: #03a9f4;
	outline: none;
}

.button:hover .polaroid__base,
.button:focus .polaroid__base,
.button:hover .polaroid__loader,
.button:focus .polaroid__loader {
	fill: #03a9f4;
}

.polaroid__inner {
	fill: #e9ecef;
}

.button--loading .polaroid__loader circle {
	-webkit-animation: fadeInOut 0.3s ease-in infinite alternate forwards;
	animation: fadeInOut 0.3s ease-in infinite alternate forwards;
}

.button--loading .polaroid__loader circle:nth-child(2) {
	-webkit-animation-delay: 0.1s;
	animation-delay: 0.1s;
}

.button--loading .polaroid__loader circle:nth-child(3) {
	-webkit-animation-delay: 0.2s;
	animation-delay: 0.2s;
}

@-webkit-keyframes fadeInOut {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

@keyframes fadeInOut {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

После загрузки всех изображений, необходимо скрыть эту кнопку:

.button--hidden { pointer-events: none; opacity: 0; -webkit-transition: opacity 0.3s; transition: opacity 0.3s; }

Сетка будет работать на Flexbox. Зададим z-index со значением 100, для того чтобы она находилась под изображением устройства и основным заголовком:

.grid {
	position: relative;
	z-index: 100;
	display: -webkit-flex;
	display: flex;
	-webkit-flex-wrap: wrap;
	flex-wrap: wrap;
	-webkit-justify-content: center;
	justify-content: center;
	-webkit-align-items: center;
	align-items: center;
	max-width: 100%;
	margin: 0 auto;
	padding: 0 0 6em;
	list-style: none;
}

Нам необходимо контролировать размер элементов сетки, для этого отключим свойство flex. Ширина каждого элемента будет 33.33% для того, чтобы разместить по три элемента в строке, пример такой сетки вы можете увидеть на сайте 7prog.by. Также добавим отступы в 10px:

.grid__item {
	display: block;
	-webkit-flex: none;
	flex: none;
	width: 33.33%;
	padding: 10px;
}

Для того чтобы контролировать видимость элементов сетки, мы создали вспомогательный класс:

.grid__item--hidden {
	opacity: 0;
}

Чтобы создать белую рамку вокруг изображений, мы установим отступы в 13px для элемента a:

.grid__link {
	display: block;
	padding: 13px 13px 0 13px;
	background: #fff;
}

Изображение будет занимать все свободное пространство:

.grid__img {
	display: block;
	width: 100%;
}

Для названия фотографий, добавим красивый рукописный шрифт:

.grid__item-title {
	font-family: 'Caveat', cursive;
	line-height: 1;
	position: relative;
	overflow: hidden;
	margin: 0;
	padding: 1em 0.5em;
	text-align: left;
	white-space: nowrap;
	text-overflow: ellipsis;
	color: #698b8d;
	background: #fff;
}

Мы добавили этот шрифт из Google CDN.

Создадим анимированный загрузчик в виде палароид фотографии:

/* Loader (styles defined in square-loader.min.css) */
.la-square-loader {
	position: absolute;
	top: 60%;
	left: 50%;
	color: #565f73;
	opacity: 0;
	-webkit-transform: translate3d(-50%,-50%,0) scale3d(1.35,1.35,1);
	transform: translate3d(-50%,-50%,0) scale3d(1.35,1.35,1);
}

.la-square-loader > div {
	border-radius: 2px;
}

После загрузки изображений, мы отображаем сетку, устройство и кнопку со стрелкой:

.js .la-square-loader {
	opacity: 1;
	-webkit-transition: opacity 0.3s;
	transition: opacity 0.3s;
}

.view--loaded .la-square-loader {
	opacity: 0;
}

.js .grid,
.js .device,
.js .button--view {
	opacity: 0;
}

.view--loaded .grid,
.view--loaded .device,
.view--loaded .button--view {
	opacity: 1;
}

.view--loaded .button--view {
	-webkit-transition: opacity 0.3s;
	transition: opacity 0.3s;
}

Настройка макета для небольших экранов:

@media screen and (max-width: 56em) {
	.header {
		padding: 1em;
	}
	.title-wrap {
		font-size: 53%;
		margin: 85px 0 0 0;
	}
	.page__title-main {
		font-size: 1.3em;
	}
	.page__title-sub {
		margin-bottom: 1em;
	}
	.grid__item {
		width: 50%;
		max-width: none;
	}
}

JavaScript

Для начала определим и инициализируем некоторые переменные:

// основной контейнер
var mainContainer = document.querySelector('.view'),
	// сетка
	gridEl = mainContainer.querySelector('.grid'),
	// элементы сетки
	gridItems = [].slice.call(gridEl.querySelectorAll('.grid__item')),
	// основной заголовок
	titleEl = mainContainer.querySelector('.title-wrap > .title--main'),
	// вложенные элементы
	subtitleEl = mainContainer.querySelector('.title-wrap > .title--sub'),
	// полноэкранный контейнер, который перемещается вверх
	pagemover = mainContainer.querySelector('.page--mover'),
	// загрузчик
	loadingStatusEl = pagemover.querySelector('.la-square-loader'),
	// размеры окна (ширина и высота)
	winsize = {width: window.innerWidth, height: window.innerHeight},
	// значения перехода (x и y): значения ширины и высоты элементов сетки; scale; rotation (z)
	// these are the values that the 6 initial images will have
	introPositions = [
		{tx: -.6, ty:-.3, s:1.1, r:-20},
		{tx: .2, ty:-.7, s:1.4, r:1},
		{tx: .5, ty:-.5, s:1.3, r:15},
		{tx: -.2, ty:-.4, s:1.4, r:-17},
		{tx: -.15, ty:-.4, s:1.2, r:-5},
		{tx: .7, ty:-.2, s:1.1, r:15}
	],
	// устройство
	deviceEl = mainContainer.querySelector('.device'),
	// кнопка запуска анимации
	showGridCtrl = document.getElementById('showgrid'),
	// заголовок сетки
	pageTitleEl = mainContainer.querySelector('.page__title > .page__title-main'),
	pageSubTitleEl = mainContainer.querySelector('.page__title > .page__title-sub'),
	// кнопка "показать больше фотографий"
	loadMoreCtrl = mainContainer.querySelector('button.button--load'),
	// true, если работает анимация
	isAnimating,
	// true, если пользователь использовал прокрутку
	// current view: stack | grid
view = 'stack';

В массиве introPositions, определяем позицию для каждого из шести изображений. Изначально, изображения будут находиться в центре экрана (внизу), а затем каждого из них установим различное значение transform в зависимости от introPositions. Значения "tx" и "ty" это процентное соотношение ширины/высоты элемента сетки, в зависимости от которого будет установлено перемещение. "s" и "r" - масштаб и поворот элементов. Для того, чтобы стек имел другой вид, просто поменяйте эти значения массива.

Далее определим функцию init:

function init() {
	// отключение прокрутки при загрузке изображений
	classie.add(document.body, 'overflow');
	disableScroll();
	// предварительная загрузка изображений
	imagesLoaded(gridEl, function() {
		// включение прокрутки
		enableScroll();
		// контроль видимости элементов сетки. Добавление классов для отображения
		classie.add(mainContainer, 'view--loaded');
		// исходный вид
		showIntro();
		// привязка событий
		initEvents();
	});
}

Предварительно загружаем все изображения внутри сетки, после этого отображаем шесть фотографий. Когда происходит загрузка изображений, мы отключаем прокрутку.

Функция showIntro позиционирует шесть изображений позади устройства. Изображения располагаются по центру экрана, и анимируются получая значения масштабирования, поворота, переходов из массива introPositions. Нам нужно, чтобы после окончательной загрузки изображений, при нажатии на стрелку, изображение устройства перемещалось вниз, за пределы видимости, а фотографии плавно переходили в сетку:

function showIntro() {
	// отображение первых 6 изображений сетки
	gridItems.slice(0,6).forEach(function(item, pos) {
		// для начала позиционируем шесть изображений на первой странице 
		// затем перемещаем их вверх 
		// и добавляем переход
		var itemOffset = item.getBoundingClientRect(),
			settings = introPositions[pos],
			center = {
				x : winsize.width/2 - (itemOffset.left + item.offsetWidth/2),
				y : winsize.height - (itemOffset.top + item.offsetHeight/2)
			}

		// позиция изображений позади устройства
		dynamics.css(item, {
			opacity: 1,
			translateX: center.x,
			translateY: center.y,
			scale: 0.5
		});

		// анимируем каждый элемент в конечную позицию
		dynamics.animate(item, {
			translateX: center.x + settings.tx*item.offsetWidth,
			translateY: center.y + settings.ty*item.offsetWidth,
			scale : settings.s,
			rotateZ: settings.r
		}, {
			type: dynamics.bezier,
			points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
			duration: 1000,
			delay: pos * 80
		});
	});

	// также анимируем устройство
	dynamics.animate(deviceEl, { translateY: 0 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 1000
	});
}

Функция инициализации событий выглядит следующим образом:

function initEvents() {
	// отображение сетки при нажатии на showGridCtrl
	showGridCtrl.addEventListener('click', showGrid);

	// отображение сетки при прокрутке
	var scrollfn = function() {
		scrolled = true;
		showGrid();
		window.removeEventListener('scroll', scrollfn);	
	};
	window.addEventListener('scroll', scrollfn);

	// отображение/загрузка дополнительных элементов
	loadMoreCtrl.addEventListener('click', loadNextItems);

	// изменение размеров окна: смена позиции изображений 
	// 6 изображений позади устройства
	window.addEventListener('resize', debounce(function(ev) {
		// сброс размеров окна
		winsize = {width: window.innerWidth, height: window.innerHeight};
		
		if( view === 'stack' ) {
			gridItems.slice(0,6).forEach(function(item, pos) {
				// сброс всех элементов
				dynamics.css(item, { scale: 1, translateX: 0, translateY: 0, rotateZ: 0 });

				// перерасчет
				var itemOffset = item.getBoundingClientRect(),
					settings = introPositions[pos];

				dynamics.css(item, {
					translateX: winsize.width/2 - (itemOffset.left + item.offsetWidth/2) + settings.tx*item.offsetWidth,
					translateY: winsize.height - (itemOffset.top + item.offsetHeight/2) + settings.ty*item.offsetWidth,
					scale : settings.s,
					rotateZ: settings.r
				});
			});
		}
	}, 10));
}

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

Теперь давайте определим все анимации для отображения сетки. Еще раз, мы будем использовать библиотеку dynamics.js которая упростит этот процесс:
function showGrid() {
	if( isAnimating ) return;
	isAnimating = true;

	// скрытие showGrid ctrl
	dynamics.css(showGridCtrl, {display: 'none'});

	// анимация основного заголовка
	dynamics.animate(titleEl, { translateY: -winsize.height/2, opacity: 0 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.7,"y":0}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 600
	});

	// анимация подзаголовка
	dynamics.animate(subtitleEl, { translateY: -winsize.height/2, opacity: 0 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.7,"y":0}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 600,
		delay: 100
	});

	// анимация устройства
	dynamics.animate(deviceEl, { translateY: 500, opacity: 0 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.7,"y":0}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 600
	});

	// анимация pagemover
	dynamics.animate(pagemover, { translateY: -winsize.height}, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.7,"y":0}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 600,
		delay: scrolled ? 0 : 120,
		complete: function(el) {
			// скрытие pagemover
			dynamics.css(el, { opacity: 0 });
			// отображение сетки
			view = 'grid';
			classie.add(mainContainer, 'view--grid');
		}
	});		
	
	// анимация элементов
	gridItems.slice(0,6).forEach(function(item, pos) {
		dynamics.stop(item);
		dynamics.animate(item, { scale: 1, translateX: 0, translateY: 0, rotateZ: 0 }, {
			type: dynamics.easeInOut,
			duration: 600,
			delay: scrolled ? 0 : 120
		});
	});

	// page title animation
	dynamics.css(pageTitleEl, { translateY: 200, opacity: 0 });
	dynamics.animate(pageTitleEl, { translateY: 0, opacity: 1 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 800,
		delay: 400
	});

	// анимация заголовка сетки
	dynamics.css(pageSubTitleEl, { translateY: 150, opacity: 0 });
	dynamics.animate(pageSubTitleEl, { translateY: 0, opacity: 1 }, {
		type: dynamics.bezier,
		points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
		duration: 800,
		delay: 500
	});

	gridItems.slice(6).forEach(function(item) {
		dynamics.css(item, { scale: 0, opacity: 0 });
		dynamics.animate(item, { scale: 1, opacity: 1 }, {
			type: dynamics.bezier,
			points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
			duration: 800,
			delay: randomIntFromInterval(100,400)
		});
	});
}

Первым делом мы скроем стрелку, затем анимируем следующие элементы: основной заголовок и подзаголовки, мобильное устройство, контейнер перехода (полноэкранный элемент который перемещается вверх и создает иллюзию перемещения фотографий вниз), каждую фотографию по отдельности, заголовок сетки и все элементы сетки. Обратите внимание, что каждая анимация запускается в определенном порядке, для каждой из них, мы должны задать определенное время и задержку. Кроме того, при прокрутке страницы, задержка может быть разной.

 

Перевод статьи Polaroid Stack to Grid Intro Animation

Тэги: gallerygrid

Вход

Уважаемый пользователь! Мы обнаружили, что вы используете AdBlock и вынуждены скрыть часть материалов на нашем сайте. Siteacademy существует и развивается за счет доходов от рекламы. Просим внести наш сайт в список исключений или отключить Блокировщик рекламы на нашем сайте.