-
Notifications
You must be signed in to change notification settings - Fork 110
/
Copy pathatvImg.js
217 lines (188 loc) · 7.22 KB
/
atvImg.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
* 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 = '';
}
}
}