Flipper Without Dirty Array

by alekskorovin

HTML

<script src="https://dl.dropboxusercontent.com/u/939178/master-class-chikuyonok/pointerEvents.js"></script>
<script src="https://dl.dropboxusercontent.com/u/939178/master-class-chikuyonok/tween.js"></script>
	
<div class="wrap">
		<div class="card card_cur" style="background: red;">1</div>
		<div class="card" style="background: blue;">2</div>
		<div class="card" style="background: green;">3</div>
		<div class="card" style="background: yellow;">4</div>
	</div>

CSS

html, body {
    padding: 0;
    margin: 0;
    width: 100%;
}

.wrap {
    position: relative;
    margin: 0 auto;
    width: 320px;
}

.card {
    position: absolute;
    width: 320px;
    height: 480px;
    font-size: 260px;
    font-family: sans-serif;
    overflow: hidden;
    line-height: 480px;
    text-align: center;
}

.card_cur {
    z-index: 2;
}
.flipper {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	z-index: 1004;

	-webkit-user-select: none;
	user-select: none;
	
	-webkit-transform-style: preserve-3d;
	-webkit-perspective: 1000px;
}

.flipper__item {
	position: absolute;

	-webkit-transform-origin: 50% 50%;
	transform-origin: 50% 50%;

	-webkit-backface-visibility: hidden;
	backface-visibility: hidden;

	/* Сразу выносим слой на GPU  */
	-webkit-transform: translate3d(0, 0, 0);
	transform: translate3d(0, 0, 0);
}

/**
 * Обозначения элементов:
 * .flipper__item_current — текущий (активный) слой
 * .flipper__item_next – следующий слой
 * .flipper__item_h1 — верхняя половина слоя
 * .flipper__item_h2 — нижняя половина слоя
 */

.flipper__item_current.flipper__item_h1 {
	z-index: 1 !important;
}

.flipper__item_current.flipper__item_h2 {
	z-index: 3 !important;
}

.flipper__item_next.flipper__item_h1 {
	z-index: 4 !important;
	-webkit-transform: rotateX(-180deg);
	-moz-transform: rotateX(-180deg);
	-ms-transform: rotateX(-180deg);
	-o-transform: rotateX(-180deg);
	transform: rotateX(-180deg);
}

.flipper__item_next.flipper__item_h2 {
	z-index: 2 !important;
}

.flipper__shade {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 2;
	opacity: 0;

	-webkit-transform: translate3d(0, 0, 0);
	transform: translate3d(0, 0, 0);
}

.flipper__shade_light {
	background: rgba(255, 255, 255, 0.5);
}

.flipper__shade_dark {
	background: rgba(0, 0, 0, 0.9);
}

JavaScript

(function() {
	/**
	 * Объект, описывающий текущее состояние переворота.
	 * Если равен null, значит, ничего сейчас не переворачиваем
	 * @type {Object}
	 */
	var flipState = null;

	/**
	 * Флаг, указывающий, заблокировано ли взаимодействие
	 * с картами (например, во время анимации)
	 * @type {Boolean}
	 */
	var locked = false;

	/**
	 * Всопомгательная функция, которая создаёт элемент с указанным
	 * именем и классами
	 * @param  {String} name      Название элемента
	 * @param  {String} className Классы элемента
	 * @return {Element}
	 */
	function el(name, className) {
		var elem = document.createElement(name);
		if (className) {
			elem.className = className;
		}

		return elem;
	}

	/**
	 * Проверяет, сработало ли событие на нужном элементе
	 * @param  {Event} evt
	 * @return {Boolean}
	 */
	function hitTest(evt) {
		var elem = evt.target;
		while (elem && elem !== document) {
			if (elem.classList.contains('card')) {
				return true;
			}

			elem = elem.parentNode;
		}
	}

	/**
	 * Вспомогательный метод, который возвращает
	 * массив карт, а также индекс текущей карты
	 * @return {Object}
	 */
	function getCards() {
		var cards = document.querySelectorAll('.card');
		var out = {
			list: [],
			selected: -1
		};

		for (var i = 0, il = cards.length; i < il; i++) {
			out.list.push(cards[i]);
			if (cards[i].classList.contains('card_cur')) {
				out.selected = i;
			}
		}

		return out;
	}

	/**
	 * Возвращает объект с картами, котороые нужно перевернуть
	 * Свойства объекта:
	 * - cur: текущая карта
	 * - next: следующая карта
	 * @return {Object}
	 */
function getCardsToFlip() {
	var cards = getCards();
	return {
		cur: cards.list[cards.selected],
		next: cards.list[cards.selected + 1]
	};
}

	/**
	 * Берёт следующую карту (относительно текущей)
	 * и помечает её как активную
	 */
	function pickNextCard() {
		var cards = getCards();
		var cur = cards.list[cards.selected];
		if (cur) {
			cur.classList.remove('card_cur');
		}

		var next =...