'use strict';
/**
* Demo. */
var demo = (function (window) {
/** * Enum of CSS selectors. */ var SELECTORS = { pattern: '.pattern', card: '.card', cardImage: '.card__image', cardClose: '.card__btn-close' }; /** * Enum of CSS classes. */ var CLASSES = { patternHidden: 'pattern--hidden', polygon: 'polygon', polygonHidden: 'polygon--hidden' }; var ATTRIBUTES = { index: 'data-index', id: 'data-id' }; /** * Map of svg paths and points. */ var polygonMap = { paths: null, points: null }; /** * Container of Card instances. */ var layout = {}; /** * Initialise demo. */ var init = function () { // For options see: https://github.com/qrohlf/Trianglify var pattern = Trianglify({ width: window.innerWidth, height: window.innerHeight, cell_size: 90, variance: 1, stroke_width: 1, x_colors: 'random', y_colors: 'random' }).svg(); // Render as SVG. _mapPolygons(pattern); _bindCards(); _bindHashChange(); _triggerOpenCard('', _getHashFromURL(location.href)); }; /** * Store path elements, map coordinates and sizes. * @param {Element} pattern The SVG Element generated with Trianglify. * @private */ var _mapPolygons = function (pattern) { // Append SVG to pattern container. $(SELECTORS.pattern).append(pattern); // Convert nodelist to array, // Used `.childNodes` because IE doesn't support `.children` on SVG. polygonMap.paths = [].slice.call(pattern.childNodes); polygonMap.points = []; polygonMap.paths.forEach(function (polygon) { // Hide polygons by adding CSS classes to each svg path (used attrs because of IE). $(polygon).attr('class', CLASSES.polygon); var rect = polygon.getBoundingClientRect(); var point = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; polygonMap.points.push(point); }); // All polygons are hidden now, display the pattern container. $(SELECTORS.pattern).removeClass(CLASSES.patternHidden); }; /** * Bind Card elements. * @private */ var _bindCards = function () { var elements = $(SELECTORS.card); $.each(elements, function (card, i) { var instance = new Card(i, card); layout[i] = { card: instance }; var $card = $(card); $card.attr(ATTRIBUTES.index, i + ''); var cardImage = $card.find(SELECTORS.cardImage); var cardClose = $card.find(SELECTORS.cardClose); $(cardImage).on('click', function () { location.hash = $card.attr(ATTRIBUTES.id); }); $(cardClose).on('click', function () { location.hash = ''; }); }); }; /** * Create a sequence for the open or close animation and play. * @param {boolean} isOpenClick Flag to detect when it's a click to open. * @param {number} id The id of the clicked card. * @private * */ var _playSequence = function (isOpenClick, id) { var card = layout[id].card; // Prevent when card already open and user click on image. if (card.isOpen && isOpenClick) { return; } // Create timeline for the whole sequence. var sequence = new TimelineLite({paused: true}); var tweenOtherCards = _showHideOtherCards(id); if (!card.isOpen) { // Open sequence. _setPatternBgImg($(this).find(SELECTORS.cardImage).find('image')); sequence.add(tweenOtherCards); sequence.add(card.openCard(_onCardMove), 0); } else { // Close sequence. var closeCard = card.closeCard(); var position = closeCard.duration() * 0.8; // 80% of close card tween. sequence.add(closeCard); sequence.add(tweenOtherCards, position); } sequence.play(); }; /** * Show/Hide all other cards. * @param {number} id The id of the clcked card to be avoided. * @private */ var _showHideOtherCards = function (id) { var TL = new TimelineLite; var selectedCard = layout[id].card; for (var i in layout) { if (layout.hasOwnProperty(i)) { var card = layout[i].card; // When called with `openCard`. if (card.id !== id && !selectedCard.isOpen) { TL.add(card.hideCard(), 0); } // When called with `closeCard`. if (card.id !== id && selectedCard.isOpen) { TL.add(card.showCard(), 0); } } } return TL; }; /** * Add card image to pattern background. * @param {Element} image The clicked SVG Image Element. * @private */ var _setPatternBgImg = function (image) { var imagePath = $(image).attr('xlink:href'); $(SELECTORS.pattern).css('background-image', 'url(' + imagePath + ')'); }; /** * Callback to be executed on Tween update, whatever a polygon * falls into a circular area defined by the card width the path's * CSS class will change accordingly. * @param {Object} track The card sizes and position during the floating. * @private */ var _onCardMove = function (track) { var radius = track.width / 2; var center = { x: track.x, y: track.y }; polygonMap.points.forEach(function (point, i) { if (_detectPointInCircle(point, radius, center)) { $(polygonMap.paths[i]).attr('class', CLASSES.polygon + ' ' + CLASSES.polygonHidden); } else { $(polygonMap.paths[i]).attr('class', CLASSES.polygon); } }); }; /** * Detect if a point is inside a circle area. * @private */ var _detectPointInCircle = function (point, radius, center) { var xp = point.x; var yp = point.y; var xc = center.x; var yc = center.y; var d = radius * radius; return Math.pow(xp - xc, 2) + Math.pow(yp - yc, 2) <= d; }; /** * initialize page view according to hash * @private */ var _triggerOpenCard = function (fromId, toId) { var getIndex = function (card) { var index = $(card).attr(ATTRIBUTES.index); return parseInt(index, 10); }; if (fromId) { var fromBlogCard = $('[' + ATTRIBUTES.id + '="' + fromId + '"]')[0]; if (fromBlogCard) { _playSequence.call(fromBlogCard, false, getIndex(fromBlogCard)); } } if (toId) { var toBlogCard = $('[' + ATTRIBUTES.id + '="' + toId + '"]')[0]; if (toBlogCard) { _playSequence.call(toBlogCard, true, getIndex(toBlogCard)); } } }; var _getHashFromURL = function (url) { var a = document.createElement('a'); a.href = url; return a.hash.slice(1); }; var _bindHashChange = function () { window.addEventListener('hashchange', function (e) { var newHash = _getHashFromURL(e.newURL); var oldHash = _getHashFromURL(e.oldURL); _triggerOpenCard(oldHash, newHash); }); }; // Expose methods. return { init: init };
})(window);
// Kickstart Demo. window.onload = demo.init;