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; | |
} | |
} |