Created
October 4, 2016 15:28
-
-
Save jgthms/d874fe2afb0af38e8d76b00b783efd1e to your computer and use it in GitHub Desktop.
atv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.atvImg { | |
border-radius: 5px; | |
transform-style: preserve-3d; | |
-webkit-tap-highlight-color: rgba(#000,0); | |
} | |
.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,.25) 0%,rgba(255,255,255,0) 60%); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="atvImg"> | |
<img src="/images/flattened-icon.jpg"> | |
<div class="atvImg-layer" data-img="/images/back.png"></div> | |
<div class="atvImg-layer" data-img="/images/front.png"></div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* atvImg | |
* Copyright 2015 Drew Wilson | |
* http://drewwilson.com | |
* | |
* Version 1.2 - Updated: Dec. 09, 2015 | |
* | |
* atvImg = 'AppleTV Image' | |
* | |
* This plug-in will automatically turn your layered Apple TV PNGs into | |
* 3D parallax icons, the same way the new Apple TV treats app icons. | |
* You can have any number of AppleTV Images on the page. | |
* | |
* An example of this plug-in can bee seen here: http://kloc.pm | |
* | |
* ------------------- | |
* | |
* Here is how to setup the HTML for a single atvImg: | |
* <div class="atvImg"> | |
* <img src="/images/flattened-icon.jpg"> | |
* | |
* <div class="atvImg-layer" data-img="/images/back.png"></div> | |
* <div class="atvImg-layer" data-img="/images/front.png"></div> | |
* </div> | |
* | |
* You can have any number of 'atvImg-layer' elements. So add as many | |
* as your icon needs. Be sure to use 2x (retina) scale PNGs. The plug-in | |
* will downscale for 1x screens. Using 2x scale PNGs is recommended | |
* so the icon will appear crisp on 2x screens. | |
* Layer images should be 2x the size you want to display the icon as. | |
* The plug-in will adapt the atvImg to be whatever size it's parent | |
* element is. So if you set your '.atvImg' element to be 320px X 190px, | |
* that is how big the icon will appear. If you set it to be | |
* 640px X 380px, that is how big it will appear. Just be sure to | |
* use the correct aspect ratio for AppleTV icons. | |
* | |
* The <img> element in the example above is a fallback in case | |
* javascript is not allowed to run. It will be removed when the plug-in | |
* is running. Put a flattened version (no layers) of you icon in there. | |
* | |
* Then call the funciton in you <script> tag or JS file like this: | |
* | |
* atvImg(); | |
* | |
* | |
* Just be sure you add that line after you've loaded the DOM. So put | |
* it below all your page's HTML just before the closing </body> tag or | |
* in a document.ready() function. | |
* | |
* ------------------- | |
* | |
* This atvImg plug-in is dual licensed under the MIT and GPL licenses: | |
* http://www.opensource.org/licenses/mit-license.php | |
* http://www.gnu.org/licenses/gpl.html | |
* | |
* I used http://designmodo.com/apple-tv-effect as reference and | |
* inspiration when creating this plug-in. | |
*/ | |
function atvImg(){ | |
var d = document, | |
de = d.documentElement, | |
bd = d.getElementsByTagName('body')[0], | |
htm = d.getElementsByTagName('html')[0], | |
win = window, | |
imgs = d.querySelectorAll('.atvImg'), | |
totalImgs = imgs.length, | |
supportsTouch = 'ontouchstart' in win || navigator.msMaxTouchPoints; | |
if(totalImgs <= 0){ | |
return; | |
} | |
// build HTML | |
for(var l=0;l<totalImgs;l++){ | |
var thisImg = imgs[l], | |
layerElems = thisImg.querySelectorAll('.atvImg-layer'), | |
totalLayerElems = layerElems.length; | |
if(totalLayerElems <= 0){ | |
continue; | |
} | |
while(thisImg.firstChild) { | |
thisImg.removeChild(thisImg.firstChild); | |
} | |
var containerHTML = d.createElement('div'), | |
shineHTML = d.createElement('div'), | |
shadowHTML = d.createElement('div'), | |
layersHTML = d.createElement('div'), | |
layers = []; | |
thisImg.id = 'atvImg__'+l; | |
containerHTML.className = 'atvImg-container'; | |
shineHTML.className = 'atvImg-shine'; | |
shadowHTML.className = 'atvImg-shadow'; | |
layersHTML.className = 'atvImg-layers'; | |
for(var i=0;i<totalLayerElems;i++){ | |
var layer = d.createElement('div'), | |
imgSrc = layerElems[i].getAttribute('data-img'); | |
layer.className = 'atvImg-rendered-layer'; | |
layer.setAttribute('data-layer',i); | |
layer.style.backgroundImage = 'url('+imgSrc+')'; | |
layersHTML.appendChild(layer); | |
layers.push(layer); | |
} | |
containerHTML.appendChild(shadowHTML); | |
containerHTML.appendChild(layersHTML); | |
containerHTML.appendChild(shineHTML); | |
thisImg.appendChild(containerHTML); | |
var w = thisImg.clientWidth || thisImg.offsetWidth || thisImg.scrollWidth; | |
thisImg.style.transform = 'perspective('+ w*3 +'px)'; | |
if(supportsTouch){ | |
win.preventScroll = false; | |
(function(_thisImg,_layers,_totalLayers,_shine) { | |
thisImg.addEventListener('touchmove', function(e){ | |
if (win.preventScroll){ | |
e.preventDefault(); | |
} | |
processMovement(e,true,_thisImg,_layers,_totalLayers,_shine); | |
}); | |
thisImg.addEventListener('touchstart', function(e){ | |
win.preventScroll = true; | |
processEnter(e,_thisImg); | |
}); | |
thisImg.addEventListener('touchend', function(e){ | |
win.preventScroll = false; | |
processExit(e,_thisImg,_layers,_totalLayers,_shine); | |
}); | |
})(thisImg,layers,totalLayerElems,shineHTML); | |
} else { | |
(function(_thisImg,_layers,_totalLayers,_shine) { | |
thisImg.addEventListener('mousemove', function(e){ | |
processMovement(e,false,_thisImg,_layers,_totalLayers,_shine); | |
}); | |
thisImg.addEventListener('mouseenter', function(e){ | |
processEnter(e,_thisImg); | |
}); | |
thisImg.addEventListener('mouseleave', function(e){ | |
processExit(e,_thisImg,_layers,_totalLayers,_shine); | |
}); | |
})(thisImg,layers,totalLayerElems,shineHTML); | |
} | |
} | |
function processMovement(e, touchEnabled, elem, layers, totalLayers, shine){ | |
var bdst = bd.scrollTop || htm.scrollTop, | |
bdsl = bd.scrollLeft, | |
pageX = (touchEnabled)? e.touches[0].pageX : e.pageX, | |
pageY = (touchEnabled)? e.touches[0].pageY : e.pageY, | |
offsets = elem.getBoundingClientRect(), | |
w = elem.clientWidth || elem.offsetWidth || elem.scrollWidth, // width | |
h = elem.clientHeight || elem.offsetHeight || elem.scrollHeight, // height | |
wMultiple = 320/w, | |
offsetX = 0.52 - (pageX - offsets.left - bdsl)/w, //cursor position X | |
offsetY = 0.52 - (pageY - offsets.top - bdst)/h, //cursor position Y | |
dy = (pageY - offsets.top - bdst) - h / 2, //@h/2 = center of container | |
dx = (pageX - offsets.left - bdsl) - w / 2, //@w/2 = center of container | |
yRotate = (offsetX - dx)*(0.07 * wMultiple), //rotation for container Y | |
xRotate = (dy - offsetY)*(0.1 * wMultiple), //rotation for container X | |
imgCSS = 'rotateX(' + xRotate + 'deg) rotateY(' + yRotate + 'deg)', //img transform | |
arad = Math.atan2(dy, dx), //angle between cursor and center of container in RAD | |
angle = arad * 180 / Math.PI - 90; //convert rad in degrees | |
//get angle between 0-360 | |
if (angle < 0) { | |
angle = angle + 360; | |
} | |
//container transform | |
if(elem.firstChild.className.indexOf(' over') != -1){ | |
imgCSS += ' scale3d(1.07,1.07,1.07)'; | |
} | |
elem.firstChild.style.transform = imgCSS; | |
//gradient angle and opacity for shine | |
shine.style.background = 'linear-gradient(' + angle + 'deg, rgba(255,255,255,' + (pageY - offsets.top - bdst)/h * 0.4 + ') 0%,rgba(255,255,255,0) 80%)'; | |
shine.style.transform = 'translateX(' + (offsetX * totalLayers) - 0.1 + 'px) translateY(' + (offsetY * totalLayers) - 0.1 + 'px)'; | |
//parallax for each layer | |
var revNum = totalLayers; | |
for(var ly=0;ly<totalLayers;ly++){ | |
layers[ly].style.transform = 'translateX(' + (offsetX * revNum) * ((ly * 2.5) / wMultiple) + 'px) translateY(' + (offsetY * totalLayers) * ((ly * 2.5) / wMultiple) + 'px)'; | |
revNum--; | |
} | |
} | |
function processEnter(e, elem){ | |
elem.firstChild.className += ' over'; | |
} | |
function processExit(e, elem, layers, totalLayers, shine){ | |
var container = elem.firstChild; | |
container.className = container.className.replace(' over',''); | |
container.style.transform = ''; | |
shine.style.cssText = ''; | |
for(var ly=0;ly<totalLayers;ly++){ | |
layers[ly].style.transform = ''; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment