<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="container">
<header class="codrops-header">
<div class="hero">
<div class="hero__imgwrap">
<img class="hero__img tilt-effect" data-tilt-options='{ "opacity" : 0.6, "movement": { "perspective" : 1500, "translateX" : 10, "translateY" : 10, "translateZ" : 2, "rotateX" : 3, "rotateY" : 3 } }' src="http://tympanus.net/Development/ImageTiltEffect/img/1.jpg" alt="Hero image" />
</div>
<h1><span>A subtle tilt effect for your images</span> Image Tilt Effect</h1>
<div class="mobile-note">This effect currently only works on hover. Please switch to a desktop browser in order to see the effect.</div>
</div>
</header><!-- /codrops-header -->
<div class="content">
<h2 class="poster-headline">How it works</h2>
<p class="poster-text">A normal image is replaced with layers of semi-transparent divisions of the same image. Every layer moves according to the configuration, creating a subtle motion effect.</p>
<p class="poster-text"><strong>Hover over the grid images to see how the effect works:</strong></p>
<ul class="grid grid--xray">
<li class="grid__item">
<div class="grid__img grid__img--border">
<img src="http://tympanus.net/Development/ImageTiltEffect/img/2.jpg" class="tilt-effect" alt="grid01" />
</div>
</li>
<li class="grid__item">
<div class="grid__img grid__img--border">
<img src="http://tympanus.net/Development/ImageTiltEffect/img/2.jpg" class="tilt-effect" data-tilt-options='{ "extraImgs" : 4, "opacity" : 0.5, "movement": { "perspective" : 500, "translateX" : -15, "translateY" : -15, "translateZ" : 20, "rotateX" : 15, "rotateY" : 15 } }' alt="grid01" />
</div>
</li>
<li class="grid__item">
<div class="grid__img grid__img--border">
<img src="http://tympanus.net/Development/ImageTiltEffect/img/2.jpg" class="tilt-effect" data-tilt-options='{ "bgfixed" : false, "movement": { "perspective" : 1000, "translateX" : 30, "translateY" : 30, "translateZ" : -50, "rotateX" : 3, "rotateY" : 3, "rotateZ" : 10 } }' alt="grid01" />
</div>
</li>
</ul>
</div>
</div><!-- /container -->
@import url(http://fonts.googleapis.com/css?family=Questrial|Playfair+Display:400,700);
@font-face {
font-weight: normal;
font-style: normal;
font-family: 'codropsicons';
src:url('../fonts/codropsicons/codropsicons.eot');
src:url('../fonts/codropsicons/codropsicons.eot?#iefix') format('embedded-opentype'),
url('../fonts/codropsicons/codropsicons.woff') format('woff'),
url('../fonts/codropsicons/codropsicons.ttf') format('truetype'),
url('../fonts/codropsicons/codropsicons.svg#codropsicons') format('svg');
}
*, *:after, *:before { -webkit-box-sizing: border-box; box-sizing: border-box; }
.clearfix:before, .clearfix:after {display: table; content: ''; }
.clearfix:after { clear: both; }
body {
background: #fff;
color: #333;
font-size: 1em;
font-family: 'Questrial', 'Avenir', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a {
outline: none;
color: rgb(160, 148, 147);
text-decoration: none;
}
a:hover, a:focus {
color: rgb(197, 135, 130);
}
.mobile-note {
position: absolute;
bottom: 1em;
color: #fff;
font-size: 1.15em;
text-align: center;
width: 100%;
padding: 1em;
background: #fb7f93;
display: none;
font-family: 'Avenir', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
/* Header */
.codrops-header {
text-align: center;
height: 100vh;
overflow: hidden;
background: url(../img/1.jpg) no-repeat center center;
background-size: auto 100vh;
}
.codrops-header h1 {
margin: 0.5em 0 0;
letter-spacing: -1px;
font-weight: 400;
font-size: 4.25em;
line-height: 1;
position: absolute;
width: 100%;
top: 25vh;
color: #fff;
pointer-events: none;
font-family: 'Playfair Display', Georgia, serif;
}
.codrops-header h1 span {
display: block;
padding: 2em 0 1em;
color: #3a3231;
font-size: 0.2em;
text-transform: uppercase;
font-weight: 400;
letter-spacing: 3px;
text-shadow: none;
font-family: 'Questrial', 'Avenir', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.github-link {
z-index: 100;
font-size: 0.95em;
position: absolute;
right: 50px;
top: 18px;
}
.github-link span {
margin: 0 0 0 5px;
}
.hero {
position: absolute;
top: 50px;
left: 50px;
bottom: 50px;
right: 50px;
overflow: hidden;
}
.hero__imgwrap {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.hero__imgwrap::after {
content: '';
position: absolute;
background: rgba(76,0,1,0.1);
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.hero__img {
position: absolute;
top: 50%;
left: 50%;
width: auto;
height: 100vh;
-webkit-transform: translate3d(-50%,-50%,0);
transform: translate3d(-50%,-50%,0);
}
.hero__imgwrap .tilt__back,
.hero__imgwrap .tilt__front {
background-size: auto 100vh;
}
/* Top Navigation Style */
.codrops-links {
position: absolute;
z-index: 1000;
display: inline-block;
text-align: center;
font-size: 0.85em;
white-space: nowrap;
left: 50px;
top: 10px;
}
.codrops-links::after {
position: absolute;
top: 0;
left: 51%;
width: 1px;
height: 100%;
background: rgba(160, 148, 147, 0.5);
content: '';
-webkit-transform: rotate3d(0,0,1,22.5deg);
transform: rotate3d(0,0,1,22.5deg);
}
.codrops-icon {
display: inline-block;
margin: 0.5em;
padding: 0em 0;
width: 1.5em;
text-decoration: none;
}
.codrops-icon span {
display: none;
}
.codrops-icon:before {
margin: 0 5px;
text-transform: none;
font-weight: normal;
font-style: normal;
font-variant: normal;
font-family: 'codropsicons';
line-height: 1;
speak: none;
-webkit-font-smoothing: antialiased;
}
.codrops-icon--drop:before {
content: "\e001";
}
.codrops-icon--prev:before {
content: "\e004";
}
/* Content */
.content {
padding: 4em 0 2em;
}
.content > p {
max-width: 900px;
margin: 1em auto;
padding: 1em 0.5em 0;
}
.content p > code {
background: #ddd;
display: inline-block;
padding: 0.25em 0.5em;
border-radius: 3px;
}
.content--color-alt {
background: #EDE8DA;
color: #A39C88;
}
.poster-headline {
text-align: center;
font-family: 'Playfair Display', Georgia, serif;
font-size: 2.65em;
font-weight: 400;
margin: 0.25em 0 0.5em;
}
.poster-text {
font-size: 1.15em;
text-align: center;
padding: 1em 0;
max-width: 1000px;
margin: 0 auto;
line-height: 1.5;
max-width: 800px;
}
/* Grid */
.grid {
position: relative;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-justify-content: center;
-ms-justify-content: center;
justify-content: center;
list-style: none;
padding: 3em 0 0 0;
margin: 0 auto;
max-width: 1020px;
}
.grid__item {
margin: 0 65px 100px;
width: 100%;
}
.grid--xray .grid__item {
margin: 0 20px 20px;
width: 300px;
}
.grid__item-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 1em;
padding: 1em 0;
font-weight: bold;
}
.grid__item-title code {
color: #aaa;
font-weight: normal;
font-size: 0.85em;
}
.grid--xray .grid__img {
width: 300px;
height: 300px;
}
.grid__img img {
max-width: 100%;
display: block;
}
.grid__img--border .tilt__back,
.grid__img--border .tilt__front {
border: 1px solid #333;
}
.grid__img--border .tilt {
overflow: visible;
}
/* Examples custom styles */
.grid--examples {
max-width: 830px;
}
.grid--examples .grid__img {
position: relative;
height: 0;
padding-bottom: 66.714%;
overflow: hidden;
}
.grid--examples .tilt {
position: absolute;
top: 0;
}
.grid__img--example-2::after {
content: '';
position: absolute;
box-shadow: inset 0 0 50px 30px rgba(2,0,37,0.8);
pointer-events: none;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.grid__img--example-3 .tilt {
-webkit-filter: grayscale(100%) brightness(105%) contrast(120%);
filter: grayscale(100%) brightness(105%) contrast(120%);
-webkit-transition: filter 0.5s, -webkit-filter 0.5s;
transition: filter 0.5s, -webkit-filter 0.5s;
}
.grid__img--example-3 .tilt:hover {
-webkit-filter: none;
filter: none;
}
.grid__img--example-4 .tilt {
width: 120%;
height: 120%;
top: -10%;
left: -10%;
}
/* Definition list */
.def-list {
max-width: 900px;
background: #f0f0f0;
border-radius: 10px;
margin: 1em auto;
padding: 3%;
}
.def-list dt {
font-family: 'Avenir', 'Helvetica', Arial, sans-serif;
}
.def-list dt:not(:first-child) {
margin-top: 2em;
}
.def-list dd {
margin: 0;
font-size: 0.95em;
line-height: 1.4;
}
/* Related demos */
.content--related {
text-align: center;
font-weight: bold;
padding: 8em 0;
}
.media-item {
display: inline-block;
padding: 1em;
vertical-align: top;
-webkit-transition: color 0.3s;
transition: color 0.3s;
}
.media-item__img {
max-width: 100%;
opacity: 0.7;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
.media-item:hover .media-item__img,
.media-item:focus .media-item__img {
opacity: 1;
}
.media-item__title {
margin: 0;
padding: 0.5em;
font-size: 1em;
}
@media screen and (min-aspect-ratio: 1440/960) {
.hero__imgwrap .tilt__back,
.hero__imgwrap .tilt__front,
.codrops-header {
background-size: 100vw auto;
}
.hero__img {
width: 100vw;
height: auto;
}
}
@media screen and (max-width: 40em){
.codrops-header h1 {
font-size: 2.5em;
}
.hero {
left: 20px;
bottom: 20px;
right: 20px;
top: 20px;
}
.github-link {
top: 40px;
right: 40px;
}
.codrops-links {
top: 35px;
left: 30px;
}
.mobile-note {
display: block;
}
}
.tilt {
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
margin: 0 auto;
}
.tilt__back,
.tilt__front {
width: 100%;
height: 100%;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.tilt__back {
position: relative;
}
.tilt__front {
position: absolute;
top: 0;
left: 0;
}
/**
* tiltfx.js
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2015, Codrops
* http://www.codrops.com
*/
;(function(window) {
'use strict';
/**
* **************************************************************************
* utils
* **************************************************************************
*/
// from https://gist.github.com/desandro/1866474
var lastTime = 0;
var prefixes = 'webkit moz ms o'.split(' ');
// get unprefixed rAF and cAF, if present
var requestAnimationFrame = window.requestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame;
// loop through vendor prefixes and get prefixed rAF and cAF
var prefix;
for( var i = 0; i < prefixes.length; i++ ) {
if ( requestAnimationFrame && cancelAnimationFrame ) {
break;
}
prefix = prefixes[i];
requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] ||
window[ prefix + 'CancelRequestAnimationFrame' ];
}
// fallback to setTimeout and clearTimeout if either request/cancel is not supported
if ( !requestAnimationFrame || !cancelAnimationFrame ) {
requestAnimationFrame = function( callback, element ) {
var currTime = new Date().getTime();
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
var id = window.setTimeout( function() {
callback( currTime + timeToCall );
}, timeToCall );
lastTime = currTime + timeToCall;
return id;
};
cancelAnimationFrame = function( id ) {
window.clearTimeout( id );
};
}
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
// 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
}
}
// from http://www.sberry.me/articles/javascript-event-throttling-debouncing
function throttle(fn, delay) {
var allowSample = true;
return function(e) {
if (allowSample) {
allowSample = false;
setTimeout(function() { allowSample = true; }, delay);
fn(e);
}
};
}
/***************************************************************************/
/**
* TiltFx fn
*/
function TiltFx(el, options) {
this.el = el;
this.options = extend( {}, this.options );
extend( this.options, options );
this._init();
this._initEvents();
}
/**
* TiltFx options.
*/
TiltFx.prototype.options = {
// number of extra image elements (div with background-image) to add to the DOM - min:1, max:5 (for a higher number, it's recommended to remove the transitions of .tilt__front in the stylesheet.
extraImgs : 2,
// the opacity value for all the image elements.
opacity : 0.7,
// by default the first layer does not move.
bgfixed : true,
// image element's movement configuration
movement : {
perspective : 1000, // perspective value
translateX : -10, // a relative movement of -10px to 10px on the x-axis (setting a negative value reverses the direction)
translateY : -10, // a relative movement of -10px to 10px on the y-axis
translateZ : 20, // a relative movement of -20px to 20px on the z-axis (perspective value must be set). Also, this specific translation is done when the mouse moves vertically.
rotateX : 2, // a relative rotation of -2deg to 2deg on the x-axis (perspective value must be set)
rotateY : 2, // a relative rotation of -2deg to 2deg on the y-axis (perspective value must be set)
rotateZ : 0 // z-axis rotation; by default there's no rotation on the z-axis (perspective value must be set)
}
}
/**
* Initialize: build the necessary structure for the image elements and replace it with the HTML img element.
*/
TiltFx.prototype._init = function() {
this.tiltWrapper = document.createElement('div');
this.tiltWrapper.className = 'tilt';
// main image element.
this.tiltImgBack = document.createElement('div');
this.tiltImgBack.className = 'tilt__back';
this.tiltImgBack.style.backgroundImage = 'url(' + this.el.src + ')';
this.tiltWrapper.appendChild(this.tiltImgBack);
// image elements limit.
if( this.options.extraImgs < 1 ) {
this.options.extraImgs = 1;
}
else if( this.options.extraImgs > 5 ) {
this.options.extraImgs = 5;
}
if( !this.options.movement.perspective ) {
this.options.movement.perspective = 0;
}
// add the extra image elements.
this.imgElems = [];
for(var i = 0; i < this.options.extraImgs; ++i) {
var el = document.createElement('div');
el.className = 'tilt__front';
el.style.backgroundImage = 'url(' + this.el.src + ')';
el.style.opacity = this.options.opacity;
this.tiltWrapper.appendChild(el);
this.imgElems.push(el);
}
if( !this.options.bgfixed ) {
this.imgElems.push(this.tiltImgBack);
++this.options.extraImgs;
}
// add it to the DOM and remove original img element.
this.el.parentNode.insertBefore(this.tiltWrapper, this.el);
this.el.parentNode.removeChild(this.el);
// tiltWrapper properties: width/height/left/top
this.view = { width : this.tiltWrapper.offsetWidth, height : this.tiltWrapper.offsetHeight };
};
/**
* Initialize the events on the main wrapper.
*/
TiltFx.prototype._initEvents = function() {
var self = this,
moveOpts = self.options.movement;
// mousemove event..
this.tiltWrapper.addEventListener('mousemove', function(ev) {
requestAnimationFrame(function() {
// mouse position relative to the document.
var mousepos = getMousePos(ev),
// document scrolls.
docScrolls = {left : document.body.scrollLeft + document.documentElement.scrollLeft, top : document.body.scrollTop + document.documentElement.scrollTop},
bounds = self.tiltWrapper.getBoundingClientRect(),
// mouse position relative to the main element (tiltWrapper).
relmousepos = {
x : mousepos.x - bounds.left - docScrolls.left,
y : mousepos.y - bounds.top - docScrolls.top
};
// configure the movement for each image element.
for(var i = 0, len = self.imgElems.length; i < len; ++i) {
var el = self.imgElems[i],
rotX = moveOpts.rotateX ? 2 * ((i+1)*moveOpts.rotateX/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.rotateX/self.options.extraImgs) : 0,
rotY = moveOpts.rotateY ? 2 * ((i+1)*moveOpts.rotateY/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.rotateY/self.options.extraImgs) : 0,
rotZ = moveOpts.rotateZ ? 2 * ((i+1)*moveOpts.rotateZ/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.rotateZ/self.options.extraImgs) : 0,
transX = moveOpts.translateX ? 2 * ((i+1)*moveOpts.translateX/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.translateX/self.options.extraImgs) : 0,
transY = moveOpts.translateY ? 2 * ((i+1)*moveOpts.translateY/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.translateY/self.options.extraImgs) : 0,
transZ = moveOpts.translateZ ? 2 * ((i+1)*moveOpts.translateZ/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.translateZ/self.options.extraImgs) : 0;
el.style.WebkitTransform = 'perspective(' + moveOpts.perspective + 'px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg) rotate3d(0,0,1,' + rotZ + 'deg)';
el.style.transform = 'perspective(' + moveOpts.perspective + 'px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg) rotate3d(0,0,1,' + rotZ + 'deg)';
}
});
});
// reset all when mouse leaves the main wrapper.
this.tiltWrapper.addEventListener('mouseleave', function(ev) {
setTimeout(function() {
for(var i = 0, len = self.imgElems.length; i < len; ++i) {
var el = self.imgElems[i];
el.style.WebkitTransform = 'perspective(' + moveOpts.perspective + 'px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
el.style.transform = 'perspective(' + moveOpts.perspective + 'px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
}
}, 60);
});
// window resize
window.addEventListener('resize', throttle(function(ev) {
// recalculate tiltWrapper properties: width/height/left/top
self.view = { width : self.tiltWrapper.offsetWidth, height : self.tiltWrapper.offsetHeight };
}, 50));
};
function init() {
// search for imgs with the class "tilt-effect"
[].slice.call(document.querySelectorAll('img.tilt-effect')).forEach(function(img) {
new TiltFx(img, JSON.parse(img.getAttribute('data-tilt-options')));
});
}
init();
window.TiltFx = TiltFx;
})(window);