├── README.md ├── LICENSE.md ├── parallax.min.js └── parallax.js /README.md: -------------------------------------------------------------------------------- 1 | # Parallax 2 | 3 | Simple 0.8KB plugin for [Animate Plus](https://github.com/bendc/animateplus) that lets you easily 4 | create layered icons. [View demo](http://animateplus.com/demos/parallax/). 5 | 6 | ## Usage 7 | 8 | 1. Include `animate.min.js` 9 | ([download](https://github.com/bendc/animateplus/blob/master/animate.min.js)) and `parallax.min.js` 10 | in your document. 11 | 2. Define a container with a `parallax` class and add your image layers in it. 12 | 13 | ```html 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ``` 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Benjamin De Cock 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /parallax.min.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded",function(){function k(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b { 2 | "use strict"; 3 | 4 | const icons = Object.freeze([...document.getElementsByClassName("parallax")]); 5 | const threshold = .2; 6 | const perspective = 800; 7 | 8 | const isLast = (arr, el) => [...arr].pop() === el; 9 | 10 | const centerLayers = (icon, layers) => { 11 | const sizes = layers.map(layer => { 12 | const rect = layer.getBoundingClientRect(); 13 | return Object.freeze({ 14 | width: rect.width, 15 | height: rect.height 16 | }); 17 | }); 18 | 19 | layers.forEach((layer, index) => { 20 | const size = sizes[index]; 21 | layer.style.position = "absolute"; 22 | layer.style.top = `calc(50% - ${size.height / 2}px)`; 23 | layer.style.left = `calc(50% - ${size.width / 2}px)`; 24 | }); 25 | 26 | icon.style.overflow = "hidden"; 27 | if (getComputedStyle(icon).getPropertyValue("position") != "static") return; 28 | icon.style.position = "relative"; 29 | }; 30 | 31 | const getParallax = (event, coordinates, level) => { 32 | const cursor = Object.freeze({ 33 | x: (event.clientX - coordinates.left) - (coordinates.width / 2), 34 | y: (event.clientY - coordinates.top) - (coordinates.height / 2) 35 | }); 36 | 37 | const limit = 1 - level * threshold; 38 | 39 | return Object.freeze({ 40 | x: cursor.x * (1 - Math.abs(cursor.x) / coordinates.width) * threshold * limit, 41 | y: cursor.y * (1 - Math.abs(cursor.y) / coordinates.height) * threshold * limit 42 | }); 43 | }; 44 | 45 | const setParallax = icon => { 46 | const coordinates = icon.getBoundingClientRect(); 47 | const layers = Object.freeze([...icon.getElementsByTagName("img")]); 48 | 49 | const hover = event => 50 | layers.forEach((layer, level) => { 51 | const {x, y} = getParallax(event, coordinates, level); 52 | layer.style.transform = `translate(${x}px, ${y}px)`; 53 | if (!isLast(layers, layer)) return; 54 | icon.style.transform = `perspective(${perspective}px) rotateX(${y}deg) rotateY(${-x}deg)`; 55 | }); 56 | 57 | const events = Object.freeze({ 58 | enter: event => { 59 | const easing = "easeOutQuad"; 60 | const duration = 150; 61 | animate.stop([icon, ...layers]); 62 | layers.forEach((layer, level) => { 63 | const {x, y} = getParallax(event, coordinates, level); 64 | animate({ 65 | el: layer, 66 | translateX: x, 67 | translateY: y, 68 | easing, 69 | duration 70 | }); 71 | if (!isLast(layers, layer)) return; 72 | animate({ 73 | el: icon, 74 | perspective: [perspective, perspective], 75 | rotateX: y, 76 | rotateY: -x, 77 | easing, 78 | duration, 79 | complete: () => icon.addEventListener("mousemove", hover) 80 | }); 81 | }); 82 | }, 83 | leave: event => 84 | layers.forEach((layer, level) => { 85 | const {x, y} = getParallax(event, coordinates, level); 86 | animate({ 87 | el: layer, 88 | translateX: [x, 0], 89 | translateY: [y, 0] 90 | }); 91 | if (!isLast(layers, layer)) return; 92 | animate({ 93 | el: icon, 94 | perspective: [perspective, perspective], 95 | rotateX: [y, 0], 96 | rotateY: [-x, 0], 97 | complete: () => icon.removeEventListener("mousemove", hover) 98 | }); 99 | }) 100 | }); 101 | 102 | centerLayers(icon, layers); 103 | Object.keys(events).forEach(event => icon.addEventListener(`mouse${event}`, events[event])); 104 | }; 105 | 106 | icons.forEach(setParallax); 107 | }); 108 | --------------------------------------------------------------------------------