/** * main.js * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2017, Codrops * http://www.codrops.com */ ;(function(window) { 'use strict'; // From https://davidwalsh.name/javascript-debounce-function. function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; // from http://www.quirksmode.org/js/events_properties.html#position function getMousePos(e) { var posx = 0; var posy = 0; if (!e) var e = window.event; if (e.pageX || e.pageY) { posx = e.pageX; posy = e.pageY; } else if (e.clientX || e.clientY) { posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } return { x : posx, y : posy } } var DOM = {}; // The loader. DOM.loader = document.querySelector('.overlay--loader'); // The room wrapper. This will be the element to be transformed in order to move around. DOM.scroller = document.querySelector('.container-wrap > .scroller'); // The rooms. DOM.rooms = [].slice.call(DOM.scroller.querySelectorAll('.room')); // The content wrapper. DOM.content = document.querySelector('.content'); // Rooms navigation controls. DOM.nav = { leftCtrl : DOM.content.querySelector('nav > .btn--nav-left'), rightCtrl : DOM.content.querySelector('nav > .btn--nav-right') }; // Content slides. DOM.slides = [].slice.call(DOM.content.querySelectorAll('.slides > .slide')); // The off canvas menu button. DOM.menuCtrl = DOM.content.querySelector('.btn--menu'); // The menu overlay. DOM.menuOverlay = DOM.content.querySelector('.overlay--menu'); // The menu items // DOM.menuItems = DOM.menuOverlay.querySelectorAll('.menu > .menu__item'); // The info button. DOM.infoCtrl = DOM.content.querySelector('.btn--info'); // The info overlay. DOM.infoOverlay = DOM.content.querySelector('.overlay--info'); // The info text. // DOM.infoText = DOM.infoOverlay.querySelector('.info'); var currentRoom = 0, // Total number of rooms. totalRooms = DOM.rooms.length, // Initial transform. initTransform = { translateX : 0, translateY : 0, translateZ : '500px', rotateX : 0, rotateY : 0, rotateZ : 0 }, // Reset transform. resetTransform = { translateX : 0, translateY : 0, translateZ : 0, rotateX : 0, rotateY : 0, rotateZ : 0 }, // View from top. menuTransform = { translateX : 0, translateY : '150%', translateZ : 0, rotateX : '15deg', rotateY : 0, rotateZ : 0 }, menuTransform = { translateX : 0, translateY : '50%', translateZ : 0, rotateX : '-10deg', rotateY : 0, rotateZ : 0 }, // Info view transform. infoTransform = { translateX : 0, translateY : 0, translateZ : '200px', rotateX : '2deg', rotateY : 0, rotateZ : '4deg' }, // Room initial moving transition. initTransition = { speed: '0.9s', easing: 'ease' }, // Room moving transition. roomTransition = { speed: '0.4s', easing: 'ease' }, // View from top transition. menuTransition = { speed: '1.5s', easing: 'cubic-bezier(0.2,1,0.3,1)' }, // Info transition. infoTransition = { speed: '15s', easing: 'cubic-bezier(0.3,1,0.3,1)' }, // Tilt transition tiltTransition = { speed: '0.2s', easing: 'ease-out' }, tilt = false, // How much to rotate when the mouse moves. tiltRotation = { rotateX : 1, // a relative rotation of -1deg to 1deg on the x-axis rotateY : -3 // a relative rotation of -3deg to 3deg on the y-axis }, // Transition end event handler. onEndTransition = function(el, callback) { var onEndCallbackFn = function(ev) { this.removeEventListener('transitionend', onEndCallbackFn); if( callback && typeof callback === 'function' ) { callback.call(); } }; el.addEventListener('transitionend', onEndCallbackFn); }, // Window sizes. win = {width: window.innerWidth, height: window.innerHeight}, // Check if moving inside the room and check if navigating. isMoving, isNavigating; function init() { // Move into the current room. move({transition: initTransition, transform: initTransform}).then(function() { initTilt(); }); // Animate the current slide in. showSlide(100); // Init/Bind events. initEvents(); } function initTilt() { applyRoomTransition(tiltTransition); tilt = true; } function removeTilt() { tilt = false; } function move(opts) { return new Promise(function(resolve, reject) { if( isMoving && !opts.stopTransition ) { return false; } isMoving = true; if( opts.transition ) { applyRoomTransition(opts.transition); } if( opts.transform ) { applyRoomTransform(opts.transform); var onEndFn = function() { isMoving = false; resolve(); }; onEndTransition(DOM.scroller, onEndFn); } else { resolve(); } }); } function initEvents() { // Mousemove event / Tilt functionality. var onMouseMoveFn = function(ev) { requestAnimationFrame(function() { if( !tilt ) return false; var mousepos = getMousePos(ev), // transform values rotX = tiltRotation.rotateX ? initTransform.rotateX - (2 * tiltRotation.rotateX / win.height * mousepos.y - tiltRotation.rotateX) : 0, rotY = tiltRotation.rotateY ? initTransform.rotateY - (2 * tiltRotation.rotateY / win.width * mousepos.x - tiltRotation.rotateY) : 0; // apply transform applyRoomTransform({ 'translateX' : initTransform.translateX, 'translateY' : initTransform.translateY, 'translateZ' : initTransform.translateZ, 'rotateX' : rotX + 'deg', 'rotateY' : rotY + 'deg', 'rotateZ' : initTransform.rotateZ }); }); }, // Window resize. debounceResizeFn = debounce(function() { win = {width: window.innerWidth, height: window.innerHeight}; }, 10); document.addEventListener('mousemove', onMouseMoveFn); window.addEventListener('resize', debounceResizeFn); // Room navigation. var onNavigatePrevFn = function() { navigate('prev'); }, onNavigateNextFn = function() { navigate('next'); }; DOM.nav.leftCtrl.addEventListener('click', onNavigatePrevFn); DOM.nav.rightCtrl.addEventListener('click', onNavigateNextFn); } function applyRoomTransform(transform) { DOM.scroller.style.transform = 'translate3d(' + transform.translateX + ', ' + transform.translateY + ', ' + transform.translateZ + ') ' + 'rotate3d(1,0,0,' + transform.rotateX + ') rotate3d(0,1,0,' + transform.rotateY + ') rotate3d(0,0,1,' + transform.rotateZ + ')'; } function applyRoomTransition(transition) { DOM.scroller.style.transition = transition === 'none' ? transition : 'transform ' + transition.speed + ' ' + transition.easing; } function toggleSlide(dir, delay) { var slide = DOM.slides[currentRoom], // Slide's name. name = slide.querySelector('.slide__name'); // cat = slide.querySelector('.cat'), // Slide's title and date elements. // title = slide.querySelector('.slide__title'), // date = slide.querySelector('.slide__date'); delay = delay !== undefined ? delay : 0; // anime.remove([cat, name, title, date]); anime.remove([name]); var animeOpts = { // targets: [cat, name, title, date], targets: [name], duration: dir === 'in' ? 400 : 400, //delay: 0,//dir === 'in' ? 150 : 0, delay: function(t, i, c) { return delay + 75+i*75; }, easing: [0.25,0.1,0.25,1], opacity: { value: dir === 'in' ? [0,1] : [1,0], duration: dir === 'in' ? 550 : 250 }, translateY: function(t, i) { return dir === 'in' ? [150,0] : [0,-150]; } }; if( dir === 'in' ) { animeOpts.begin = function() { slide.classList.add('slide--current'); }; } else { animeOpts.complete = function() { slide.classList.remove('slide--current'); }; } anime(animeOpts); } function showSlide(delay) { toggleSlide('in', delay); } function hideSlide(delay) { toggleSlide('out', delay); } function navigate(dir) { if( isMoving || isNavigating ) { return false; } isNavigating = true; var room = DOM.rooms[currentRoom]; // Remove tilt. removeTilt(); // Animate the current slide out - animate the name, title and date elements. hideSlide(); // Update currentRoom. if( dir === 'next' ) { currentRoom = currentRoom < totalRooms - 1 ? currentRoom + 1 : 0; } else { currentRoom = currentRoom > 0 ? currentRoom - 1 : totalRooms - 1; } // Position the next room. var nextRoom = DOM.rooms[currentRoom]; nextRoom.style.transform = 'translate3d(' + (dir === 'next' ? 100 : -100) + '%,0,0) translate3d(' + (dir === 'next' ? 1 : -1) + 'px,0,0)' ; nextRoom.style.opacity = 1; // Move back. move({transition: roomTransition, transform: resetTransform}) .then(function() { // Move left or right. return move({transform: { translateX : (dir === 'next' ? -100 : 100) + '%', translateY : 0, translateZ : 0, rotateX : 0, rotateY : 0, rotateZ : 0 }}); }) .then(function() { // Update current room class. nextRoom.classList.add('room--current'); room.classList.remove('room--current'); room.style.opacity = 0; // Show the next slide. showSlide(); // Move into room. // Update final transform state: return move({transform: { translateX : (dir === 'next' ? -100 : 100) + '%', translateY : 0, translateZ : '500px', rotateX : 0, rotateY : 0, rotateZ : 0 }}); }) .then(function() { // Reset positions. applyRoomTransition('none'); nextRoom.style.transform = 'translate3d(0,0,0)'; applyRoomTransform(initTransform); setTimeout(function() { initTilt(); }, 60); isNavigating = false; }); } function addAdjacentRooms() { // Current room. var room = DOM.rooms[currentRoom], // Adjacent rooms. nextRoom = DOM.rooms[currentRoom < totalRooms - 1 ? currentRoom + 1 : 0], prevRoom = DOM.rooms[currentRoom > 0 ? currentRoom - 1 : totalRooms - 1]; // Position the adjacent rooms. nextRoom.style.transform = 'translate3d(100%,0,0) translate3d(3px,0,0)'; nextRoom.style.opacity = 1; prevRoom.style.transform = 'translate3d(-100%,0,0) translate3d(-3px,0,0)'; prevRoom.style.opacity = 1; } function removeAdjacentRooms() { // Current room. var room = DOM.rooms[currentRoom], // Adjacent rooms. nextRoom = DOM.rooms[currentRoom < totalRooms - 1 ? currentRoom + 1 : 0], prevRoom = DOM.rooms[currentRoom > 0 ? currentRoom - 1 : totalRooms - 1]; // Position the adjacent rooms. nextRoom.style.transform = 'none'; nextRoom.style.opacity = 0; prevRoom.style.transform = 'none'; prevRoom.style.opacity = 0; } function toggleInfo() { if( isNavigating ) { return false; } if( DOM.infoCtrl.classList.contains('btn--active') ) { // Close it. closeInfo(); } else { // Open it. showInfo(); } } function showInfo() { // Button becomes cross. DOM.infoCtrl.classList.add('btn--active'); // Remove tilt. removeTilt(); // Hide current slide. hideSlide(); // Apply info transition. applyRoomTransition(infoTransition); // Infoview: move({transform: infoTransform, stopTransition: true}); // Show info text and animate photos out of the walls. var photos = DOM.rooms[currentRoom].querySelectorAll('.room__img'); anime.remove(photos); anime({ targets: photos, duration: function() { return anime.random(15000, 30000); }, easing: [0.3,1,0.3,1], translateY: function() { return anime.random(-50,50); }, rotateX: function() { return anime.random(-2,2); }, rotateZ: function() { return anime.random(-5,5); }, translateZ: function() { return [10,anime.random(50,win.width/3)]; } }); // Animate info text and overlay. anime.remove([DOM.infoOverlay, DOM.infoText]); var animeInfoOpts = { targets: [DOM.infoOverlay, DOM.infoText], duration: 1500, delay: function(t,i) { return !i ? 0 : 150; }, easing: [0.25,0.1,0.25,1], opacity: [0,1], translateY: function(t,i) { return !i ? 0 : [30,0]; }, begin: function() { DOM.infoOverlay.classList.add('overlay--active'); } }; anime(animeInfoOpts); } function closeInfo() { // Button becomes info. DOM.infoCtrl.classList.remove('btn--active'); // Apply room transition. applyRoomTransition(roomTransition); // Show current slide. showSlide(100); // back to room view: move({transform: initTransform, stopTransition: true}).then(function() { initTilt(); }); // Hide info text and animate photos into the walls. var photos = DOM.rooms[currentRoom].querySelectorAll('.room__img'); anime.remove(photos); anime({ targets: photos, duration: 400, easing: [0.3,1,0.3,1], translateY: 0, rotateX: 0, rotateZ: 0, translateZ: 10 }); // Animate info text and overlay. anime.remove([DOM.infoOverlay, DOM.infoText]); var animeInfoOpts = { targets: [DOM.infoOverlay, DOM.infoText], duration: 400, easing: [0.25,0.1,0.25,1], opacity: [1,0], translateY: function(t,i) { return !i ? 0 : [0,30]; }, complete: function() { DOM.infoOverlay.classList.remove('overlay--active'); } }; anime(animeInfoOpts); } // Preload all the images. imagesLoaded(DOM.scroller, function() { var extradelay = 1000; // Slide out loader. anime({ targets: DOM.loader, duration: 600, easing: 'easeInOutCubic', delay: extradelay, translateY: '-100%', begin: function() { init(); }, complete: function() { DOM.loader.classList.remove('overlay--active'); } }); }); })(window);