Example of adding a parallax effect to image layers. Works with cursor hover on desktop, and touch+drag on mobile.
A Pen by Jarom Vogel on CodePen.
| <div class="container" ng-app="parallaxApp" ng-controller="mainCtrl"> | |
| <div class="parallaximage-container" parallax> | |
| <img class="layer layer0" src="http://jaromvogel.com/images/design/jumping_rabbit/page2layer0.png"/> | |
| <img class="layer layer1" src="http://jaromvogel.com/images/design/jumping_rabbit/page2layer1.png"/> | |
| <img class="layer layer2" src="http://jaromvogel.com/images/design/jumping_rabbit/page2layer2.png"/> | |
| <img class="layer layer3" src="http://jaromvogel.com/images/design/jumping_rabbit/page2layer3.png"/> | |
| <img class="layer layer4" src="http://jaromvogel.com/images/design/jumping_rabbit/page2layer4.png"/> | |
| </div> | |
| <div class="instructions"> | |
| Move mouse over image, or touch and drag | |
| </div> | |
| </div> |
Example of adding a parallax effect to image layers. Works with cursor hover on desktop, and touch+drag on mobile.
A Pen by Jarom Vogel on CodePen.
| var app = angular.module("parallaxApp", []); | |
| app.controller('mainCtrl', ['$scope', | |
| function($scope) { | |
| } | |
| ]); | |
| app.directive('parallax', function() { | |
| return { | |
| restrict: 'A', | |
| link: function(scope, element, attr) { | |
| // Initialize layers | |
| var all = element.find('.layer0, .layer1, .layer2, .layer3'); | |
| var layer0 = element.find('.layer0'); | |
| var layer1 = element.find('.layer1'); | |
| var layer2 = element.find('.layer2'); | |
| var layer3 = element.find('.layer3'); | |
| layers = document.querySelectorAll('.layer'); | |
| // Move images based on hover | |
| var mouseentered = false; | |
| var entrypoint = null; | |
| var currentpoint = null; | |
| element.bind('mouseenter', function(event) { | |
| mouseentered = true; | |
| entrypoint = { | |
| x: event.offsetX, | |
| y: event.offsetY | |
| }; | |
| }); | |
| element.bind('mousemove', function(event) { | |
| if (mouseentered === true) { | |
| currentpoint = { | |
| x: event.offsetX, | |
| y: event.offsetY | |
| }; | |
| var deltax = currentpoint.x - entrypoint.x; | |
| var deltay = currentpoint.y - entrypoint.y; | |
| layer3.css({ | |
| transform: 'translate3d(' + deltax / 30 + 'px,' + deltay / 30 + 'px,0)', | |
| webkitTransform: 'translate3d(' + deltax / 30 + 'px,' + deltay / 30 + 'px,0)', | |
| }) | |
| layer2.css({ | |
| transform: 'translate3d(' + deltax / 22 + 'px,' + deltay / 22 + 'px,0)', | |
| webkitTransform: 'translate3d(' + deltax / 22 + 'px,' + deltay / 22 + 'px,0)', | |
| }) | |
| layer1.css({ | |
| transform: 'translate3d(' + deltax / 20 + 'px,' + deltay / 20 + 'px,0)', | |
| webkitTransform: 'translate3d(' + deltax / 20 + 'px,' + deltay / 20 + 'px,0)', | |
| }) | |
| layer0.css({ | |
| transform: 'translate3d(' + deltax / 10 + 'px,' + deltay / 10 + 'px,0)', | |
| webkitTransform: 'translate3d(' + deltax / 10 + 'px,' + deltay / 10 + 'px,0)', | |
| }) | |
| } | |
| }); | |
| element.bind('mouseout', function(event) { | |
| mouseentered = false; | |
| dynamics.animate(layers, { | |
| translateX: 0, | |
| translateY: 0, | |
| }, { | |
| type: dynamics.spring, | |
| friction: 200, | |
| duration: 250, | |
| }); | |
| }); | |
| // Move images based on touch and drag | |
| var touchstart = false; | |
| var startpoint = null; | |
| element.bind('touchstart', function(event) { | |
| touchstart = true; | |
| startpoint = { | |
| x: event.originalEvent.touches[0].pageX, | |
| y: event.originalEvent.touches[0].pageY | |
| } | |
| }); | |
| element.bind('touchmove', function(event) { | |
| if (touchstart === true) { | |
| event.preventDefault(); | |
| var deltax = event.originalEvent.touches[0].pageX - startpoint.x; | |
| var deltay = event.originalEvent.touches[0].pageY - startpoint.y; | |
| layer3.css({ | |
| transform: 'translate3d(' + -deltax / 40 + 'px,' + -deltay / 40 + 'px,0)', | |
| webkitTransform: 'translate3d(' + -deltax / 40 + 'px,' + -deltay / 40 + 'px,0)', | |
| }); | |
| layer2.css({ | |
| transform: 'translate3d(' + -deltax / 30 + 'px,' + -deltay / 30 + 'px,0)', | |
| webkitTransform: 'translate3d(' + -deltax / 30 + 'px,' + -deltay / 30 + 'px,0)', | |
| }); | |
| layer1.css({ | |
| transform: 'translate3d(' + -deltax / 25 + 'px,' + -deltay / 25 + 'px,0)', | |
| webkitTransform: 'translate3d(' + -deltax / 25 + 'px,' + -deltay / 25 + 'px,0)', | |
| }); | |
| layer0.css({ | |
| transform: 'translate3d(' + -deltax / 15 + 'px,' + -deltay / 15 + 'px,0)', | |
| webkitTransform: 'translate3d(' + -deltax / 15 + 'px,' + -deltay / 15 + 'px,0)', | |
| }); | |
| } | |
| }); | |
| element.bind('touchend', function(event) { | |
| touchstart = false; | |
| dynamics.animate(layers, { | |
| translateX: 0, | |
| translateY: 0, | |
| }, { | |
| type: dynamics.spring, | |
| friction: 100, | |
| duration: 350, | |
| }); | |
| }); | |
| } | |
| } | |
| }) |
| <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script> | |
| <script src="https://jaromvogel.com/js/lib/dynamics.min.js"></script> |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: sans-serif; | |
| margin: 0; | |
| } | |
| .container { | |
| position: absolute; | |
| height: 100%; | |
| width: 100%; | |
| } | |
| .parallaximage-container { | |
| width: 300px; | |
| height: 300px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| position: relative; | |
| margin: 0 auto; | |
| overflow: hidden; | |
| img { | |
| position: absolute; | |
| width: 100%; | |
| } | |
| } | |
| .instructions { | |
| text-align: center; | |
| position: absolute; | |
| bottom: 0; | |
| width: 100%; | |
| font-size: 12px; | |
| line-height: 60px; | |
| height: 60px; | |
| background: white; | |
| } | |
| @media (max-width: 767px) { | |
| .parallaximage-container { | |
| width: 320px; | |
| height: 320px; | |
| } | |
| } |