<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.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 ---------->
<!-- Images from Dribbble: https://dribbble.com/Rishavmalla -->
<div class="container">
<h1 class="heading">3D Parallax Hover Effect</h1>
<div class="card">
<div class="card__item">
<h2 class="card__item-title">Time for a vacation</h2>
<a href="https://dribbble.com/shots/2792589-Time-for-a-vacation" title="Time for a vacation">
<div class="atvImg">
<img src="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/2792589/for_dribbble-01.png">
<div class="atvImg-layer" data-img="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/2792589/for_dribbble-01.png"></div>
</div>
</a>
</div>
<div class="card__item">
<h2 class="card__item-title">Singha Durbar</h2>
<a href="https://dribbble.com/shots/2787693-Singha-Durbar" title="Singha Durbar">
<div class="atvImg">
<img src="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/2787693/singha_durbar_final-02-02-02.png">
<div class="atvImg-layer" data-img="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/2787693/singha_durbar_final-02-02-02.png"></div>
</div>
</a>
</div>
<div class="card__item">
<h2 class="card__item-title">Little Miss Sunshine</h2>
<a href="https://dribbble.com/shots/3002552-Little-Miss-Sunshine" title="Little Miss Sunshine">
<div class="atvImg">
<img src="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/3002552/little_miss_sunshine-01.png">
<div class="atvImg-layer" data-img="https://d13yacurqjgara.cloudfront.net/users/1085581/screenshots/3002552/little_miss_sunshine-01.png"></div>
</div>
</a>
</div>
</div>
</div>
<script>
/*
* atvImg
* Copyright 2015 Drew Wilson
* http://drewwilson.com
*
* Version 1.1 - Updated: Oct. 26, 2015
*/
function FastClick(e, t) {
function n(e, t) {
return function() {
return e.apply(t, arguments)
}
}
var i;
t = t || {}, this.trackingClick = !1, this.trackingClickStart = 0, this.targetElement = null, this.touchStartX = 0, this.touchStartY = 0, this.lastTouchIdentifier = 0, this.touchBoundary = t.touchBoundary || 10, this.layer = e, this.tapDelay = t.tapDelay || 200, FastClick.notNeeded(e) || (deviceIsAndroid && (e.addEventListener("mouseover", n(this.onMouse, this), !0), e.addEventListener("mousedown", n(this.onMouse, this), !0), e.addEventListener("mouseup", n(this.onMouse, this), !0)), e.addEventListener("click", n(this.onClick, this), !0), e.addEventListener("touchstart", n(this.onTouchStart, this), !1), e.addEventListener("touchmove", n(this.onTouchMove, this), !1), e.addEventListener("touchend", n(this.onTouchEnd, this), !1), e.addEventListener("touchcancel", n(this.onTouchCancel, this), !1), Event.prototype.stopImmediatePropagation || (e.removeEventListener = function(t, n, i) {
var a = Node.prototype.removeEventListener;
"click" === t ? a.call(e, t, n.hijacked || n, i) : a.call(e, t, n, i)
}, e.addEventListener = function(t, n, i) {
var a = Node.prototype.addEventListener;
"click" === t ? a.call(e, t, n.hijacked || (n.hijacked = function(e) {
e.propagationStopped || n(e)
}), i) : a.call(e, t, n, i)
}), "function" == typeof e.onclick && (i = e.onclick, e.addEventListener("click", function(e) {
i(e)
}, !1), e.onclick = null))
}
function atvImg() {
function e(e, t, n, i, a, s) {
var c = o.scrollTop || r.scrollTop,
l = o.scrollLeft,
d = t ? e.touches[0].pageX : e.pageX,
u = t ? e.touches[0].pageY : e.pageY,
h = n.getBoundingClientRect(),
v = n.clientWidth || n.offsetWidth || n.scrollWidth,
p = n.clientHeight || n.offsetHeight || n.scrollHeight,
m = 320 / v,
f = .52 - (d - h.left - l) / v,
g = .52 - (u - h.top - c) / p,
k = u - h.top - c - p / 2,
C = d - h.left - l - v / 2,
E = (f - C) * (.07 * m),
y = (k - g) * (.1 * m),
S = "rotateX(" + y + "deg) rotateY(" + E + "deg)",
I = Math.atan2(k, C),
T = 180 * I / Math.PI - 90;
0 > T && (T += 360), -1 != n.firstChild.className.indexOf(" over") && (S += " scale3d(1.07,1.07,1.07)"), n.firstChild.style.transform = S, s.style.background = "linear-gradient(" + T + "deg, rgba(255,255,255," + (u - h.top - c) / p * .4 + ") 0%,rgba(255,255,255,0) 80%)", s.style.transform = "translateX(" + f * a - .1 + "px) translateY(" + g * a - .1 + "px)";
for (var w = a, F = 0; a > F; F++) i[F].style.transform = "translateX(" + f * w * (2.5 * F / m) + "px) translateY(" + g * a * (2.5 * F / m) + "px)", w--
}
function t(e, t) {
t.firstChild.className += " over"
}
function n(e, t, n, i, a) {
var o = t.firstChild;
o.className = o.className.replace(" over", ""), o.style.transform = "", a.style.cssText = "";
for (var r = 0; i > r; r++) n[r].style.transform = ""
}
var i = document,
a = i.documentElement,
o = i.getElementsByTagName("body")[0],
r = i.getElementsByTagName("html")[0],
s = window,
c = i.querySelectorAll(".atvImg"),
l = c.length,
d = "ontouchstart" in s || navigator.msMaxTouchPoints;
if (!(0 >= l))
for (var u = 0; l > u; u++) {
var h = c[u],
v = h.querySelectorAll(".atvImg-layer"),
p = v.length;
if (!(0 >= p)) {
for (; h.firstChild;) h.removeChild(h.firstChild);
var m = i.createElement("div"),
f = i.createElement("div"),
g = i.createElement("div"),
k = i.createElement("div"),
C = [];
h.id = "atvImg__" + u, m.className = "atvImg-container", f.className = "atvImg-shine", g.className = "atvImg-shadow", k.className = "atvImg-layers";
for (var E = 0; p > E; E++) {
var y = i.createElement("div"),
S = v[E].getAttribute("data-img");
y.className = "atvImg-rendered-layer", y.setAttribute("data-layer", E), y.style.backgroundImage = "url(" + S + ")", k.appendChild(y), C.push(y)
}
m.appendChild(g), m.appendChild(k), m.appendChild(f), h.appendChild(m);
var I = h.clientWidth || h.offsetWidth || h.scrollWidth;
h.style.transform = "perspective(" + 3 * I + "px)", d ? (s.preventScroll = !1, function(i, a, o, r) {
h.addEventListener("touchmove", function(t) {
s.preventScroll && t.preventDefault(), e(t, !0, i, a, o, r)
}), h.addEventListener("touchstart", function(e) {
s.preventScroll = !0, t(e, i)
}), h.addEventListener("touchend", function(e) {
s.preventScroll = !1, n(e, i, a, o, r)
})
}(h, C, p, f)) : ! function(i, a, o, r) {
h.addEventListener("mousemove", function(t) {
e(t, !1, i, a, o, r)
}), h.addEventListener("mouseenter", function(e) {
t(e, i)
}), h.addEventListener("mouseleave", function(e) {
n(e, i, a, o, r)
})
}(h, C, p, f)
}
}
}
var deviceIsAndroid = navigator.userAgent.indexOf("Android") > 0,
deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent),
deviceIsIOS4 = deviceIsIOS && /OS 4_\d(_\d)?/.test(navigator.userAgent),
deviceIsIOSWithBadTarget = deviceIsIOS && /OS ([6-9]|\d{2})_\d/.test(navigator.userAgent);
FastClick.prototype.needsClick = function(e) {
switch (e.nodeName.toLowerCase()) {
case "button":
case "select":
case "textarea":
if (e.disabled) return !0;
break;
case "input":
if (deviceIsIOS && "file" === e.type || e.disabled) return !0;
break;
case "label":
case "video":
return !0
}
return /\bneedsclick\b/.test(e.className)
}, FastClick.prototype.needsFocus = function(e) {
switch (e.nodeName.toLowerCase()) {
case "textarea":
return !0;
case "select":
return !deviceIsAndroid;
case "input":
switch (e.type) {
case "button":
case "checkbox":
case "file":
case "image":
case "radio":
case "submit":
return !1
}
return !e.disabled && !e.readOnly;
default:
return /\bneedsfocus\b/.test(e.className)
}
}, FastClick.prototype.sendClick = function(e, t) {
var n, i;
document.activeElement && document.activeElement !== e && document.activeElement.blur(), i = t.changedTouches[0], n = document.createEvent("MouseEvents"), n.initMouseEvent(this.determineEventType(e), !0, !0, window, 1, i.screenX, i.screenY, i.clientX, i.clientY, !1, !1, !1, !1, 0, null), n.forwardedTouchEvent = !0, e.dispatchEvent(n)
}, FastClick.prototype.determineEventType = function(e) {
return deviceIsAndroid && "select" === e.tagName.toLowerCase() ? "mousedown" : "click"
}, FastClick.prototype.focus = function(e) {
var t;
deviceIsIOS && e.setSelectionRange && 0 !== e.type.indexOf("date") && "time" !== e.type ? (t = e.value.length, e.setSelectionRange(t, t)) : e.focus()
}, FastClick.prototype.updateScrollParent = function(e) {
var t, n;
if (t = e.fastClickScrollParent, !t || !t.contains(e)) {
n = e;
do {
if (n.scrollHeight > n.offsetHeight) {
t = n, e.fastClickScrollParent = n;
break
}
n = n.parentElement
} while (n)
}
t && (t.fastClickLastScrollTop = t.scrollTop)
}, FastClick.prototype.getTargetElementFromEventTarget = function(e) {
return e.nodeType === Node.TEXT_NODE ? e.parentNode : e
}, FastClick.prototype.onTouchStart = function(e) {
var t, n, i;
if (e.targetTouches.length > 1) return !0;
if (t = this.getTargetElementFromEventTarget(e.target), n = e.targetTouches[0], deviceIsIOS) {
if (i = window.getSelection(), i.rangeCount && !i.isCollapsed) return !0;
if (!deviceIsIOS4) {
if (n.identifier === this.lastTouchIdentifier) return e.preventDefault(), !1;
this.lastTouchIdentifier = n.identifier, this.updateScrollParent(t)
}
}
return this.trackingClick = !0, this.trackingClickStart = e.timeStamp, this.targetElement = t, this.touchStartX = n.pageX, this.touchStartY = n.pageY, e.timeStamp - this.lastClickTime < this.tapDelay && e.preventDefault(), !0
}, FastClick.prototype.touchHasMoved = function(e) {
var t = e.changedTouches[0],
n = this.touchBoundary;
return Math.abs(t.pageX - this.touchStartX) > n || Math.abs(t.pageY - this.touchStartY) > n ? !0 : !1
}, FastClick.prototype.onTouchMove = function(e) {
return this.trackingClick ? ((this.targetElement !== this.getTargetElementFromEventTarget(e.target) || this.touchHasMoved(e)) && (this.trackingClick = !1, this.targetElement = null), !0) : !0
}, FastClick.prototype.findControl = function(e) {
return void 0 !== e.control ? e.control : e.htmlFor ? document.getElementById(e.htmlFor) : e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")
}, FastClick.prototype.onTouchEnd = function(e) {
var t, n, i, a, o, r = this.targetElement;
if (!this.trackingClick) return !0;
if (e.timeStamp - this.lastClickTime < this.tapDelay) return this.cancelNextClick = !0, !0;
if (this.cancelNextClick = !1, this.lastClickTime = e.timeStamp, n = this.trackingClickStart, this.trackingClick = !1, this.trackingClickStart = 0, deviceIsIOSWithBadTarget && (o = e.changedTouches[0], r = document.elementFromPoint(o.pageX - window.pageXOffset, o.pageY - window.pageYOffset) || r, r.fastClickScrollParent = this.targetElement.fastClickScrollParent), i = r.tagName.toLowerCase(), "label" === i) {
if (t = this.findControl(r)) {
if (this.focus(r), deviceIsAndroid) return !1;
r = t
}
} else if (this.needsFocus(r)) return e.timeStamp - n > 100 || deviceIsIOS && window.top !== window && "input" === i ? (this.targetElement = null, !1) : (this.focus(r), this.sendClick(r, e), deviceIsIOS4 && "select" === i || (this.targetElement = null, e.preventDefault()), !1);
return deviceIsIOS && !deviceIsIOS4 && (a = r.fastClickScrollParent, a && a.fastClickLastScrollTop !== a.scrollTop) ? !0 : (this.needsClick(r) || (e.preventDefault(), this.sendClick(r, e)), !1)
}, FastClick.prototype.onTouchCancel = function() {
this.trackingClick = !1, this.targetElement = null
}, FastClick.prototype.onMouse = function(e) {
return this.targetElement ? e.forwardedTouchEvent ? !0 : e.cancelable && (!this.needsClick(this.targetElement) || this.cancelNextClick) ? (e.stopImmediatePropagation ? e.stopImmediatePropagation() : e.propagationStopped = !0, e.stopPropagation(), e.preventDefault(), !1) : !0 : !0
}, FastClick.prototype.onClick = function(e) {
var t;
return this.trackingClick ? (this.targetElement = null, this.trackingClick = !1, !0) : "submit" === e.target.type && 0 === e.detail ? !0 : (t = this.onMouse(e), t || (this.targetElement = null), t)
}, FastClick.prototype.destroy = function() {
var e = this.layer;
deviceIsAndroid && (e.removeEventListener("mouseover", this.onMouse, !0), e.removeEventListener("mousedown", this.onMouse, !0), e.removeEventListener("mouseup", this.onMouse, !0)), e.removeEventListener("click", this.onClick, !0), e.removeEventListener("touchstart", this.onTouchStart, !1), e.removeEventListener("touchmove", this.onTouchMove, !1), e.removeEventListener("touchend", this.onTouchEnd, !1), e.removeEventListener("touchcancel", this.onTouchCancel, !1)
}, FastClick.notNeeded = function(e) {
var t, n;
if ("undefined" == typeof window.ontouchstart) return !0;
if (n = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [, 0])[1]) {
if (!deviceIsAndroid) return !0;
if (t = document.querySelector("meta[name=viewport]")) {
if (-1 !== t.content.indexOf("user-scalable=no")) return !0;
if (n > 31 && window.innerWidth <= window.screen.width) return !0
}
}
return "none" === e.style.msTouchAction ? !0 : !1
}, FastClick.attach = function(e, t) {
return new FastClick(e, t)
}, "undefined" != typeof define && define.amd ? define(function() {
return FastClick
}) : "undefined" != typeof module && module.exports ? (module.exports = FastClick.attach, module.exports.FastClick = FastClick) : window.FastClick = FastClick, document.addEventListener("touchstart", function() {}, !0), atvImg();
</script>
@import url("https://fonts.googleapis.com/css?family=Amaranth|Dancing+Script");
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
body {
background: #43cea2;
/* fallback for old browsers */
background: -webkit-linear-gradient(to left, #43cea2, #185a9d);
/* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to left, #43cea2, #185a9d);
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
font-family: 'Dancing Script', cursive;
}
.heading {
text-align: center;
color: #fff;
font-size: 60px;
font-family: 'Amaranth', sans-serif;
}
.card {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
max-width: 960px;
margin: 0 auto;
padding: 50px 20px 0;
}
.card__item {
width: 29%;
margin: 10px 20px;
}
.card__item-title {
text-align: center;
color: #fff;
font-weight: normal;
font-size: 20px;
letter-spacing: 1px;
}
.atvImg {
border-radius: 5px;
transform-style: preserve-3d;
-webkit-tap-highlight-color: transparent;
width: 100%;
height: 350px;
display: inline-block;
}
.atvImg img {
border-radius: 5px;
box-shadow: 0 2px 8px rgba(14, 21, 47, 0.25);
}
.atvImg-container {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
transition: all 0.2s ease-out;
}
.atvImg-container.over .atvImg-shadow {
box-shadow: 0 45px 100px rgba(14, 21, 47, 0.4), 0 16px 40px rgba(14, 21, 47, 0.4);
}
.atvImg-layers {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
overflow: hidden;
transform-style: preserve-3d;
}
.atvImg-rendered-layer {
position: absolute;
width: 104%;
height: 104%;
top: -2%;
left: -2%;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
background-size: cover;
transition: all 0.1s ease-out;
}
.atvImg-shadow {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
transition: all 0.2s ease-out;
box-shadow: 0 8px 30px rgba(14, 21, 47, 0.6);
}
.atvImg-shine {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 5px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 60%);
}