|
1 |
// VERSION: 1.7 LAST UPDATE: 16.12.2010
|
|
2 |
/*
|
|
3 |
* THIS IS FREE SCRIPT BUT LEAVE THIS COMMENT IF
|
|
4 |
* YOU WANT USE THIS CODE ON YOUR SITE
|
|
5 |
*
|
|
6 |
* Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009
|
|
7 |
* http://wilq32.blogspot.com
|
|
8 |
*
|
|
9 |
*/
|
|
10 |
/*
|
|
11 |
Description:
|
|
12 |
|
|
13 |
This is an final product of a Wilq32.PhotoEffect Snippet. Actually you can
|
|
14 |
use this simple and tiny script to get effect of rotated images directly
|
|
15 |
from client side (for ex. user generated content), and animate them using
|
|
16 |
own functions.
|
|
17 |
|
|
18 |
|
|
19 |
Notices:
|
|
20 |
|
|
21 |
Include script after including main jQuery. Whole plugin uses jQuery
|
|
22 |
namespace and should be compatible with older version (unchecked).
|
|
23 |
|
|
24 |
Usage:
|
|
25 |
|
|
26 |
jQuery(imgElement).rotate(angleValue)
|
|
27 |
jQuery(imgElement).rotate(parameters)
|
|
28 |
jQuery(imgElement).rotateAnimation(parameters)
|
|
29 |
jQuery(imgElement).rotateAnimation(parameters)
|
|
30 |
|
|
31 |
|
|
32 |
|
|
33 |
Returns:
|
|
34 |
|
|
35 |
jQueryRotateElement - !!! NOTICE !!! function return rotateElement
|
|
36 |
instance to help connect events with actually created 'rotation' element.
|
|
37 |
|
|
38 |
Parameters:
|
|
39 |
|
|
40 |
({angle:angleValue,
|
|
41 |
[animateAngle:animateAngleValue],
|
|
42 |
[easing:easingFunction],
|
|
43 |
[duration:durationValue],
|
|
44 |
!!! DEPRECATED !!! [maxAngle:maxAngleValue],
|
|
45 |
!!! DEPRECATED !!! [minAngle:minAngleValue],
|
|
46 |
[callback:callbackFunction],
|
|
47 |
[animatedGif:animatedGifBoolean],
|
|
48 |
[bind:[{event: function},{event:function} ] })
|
|
49 |
jQuery(imgElement).rotateAnimation
|
|
50 |
|
|
51 |
Where:
|
|
52 |
|
|
53 |
- angleValue - clockwise rotation given in degrees,
|
|
54 |
- [animateAngleValue] - optional parameter, animate rotating into this value,
|
|
55 |
- [easing] - optional parameter, function to control animation speed - supports native easing
|
|
56 |
function and a jquery easing plugin, default: easeOutQuart
|
|
57 |
- [duration] - optional parameter, duration of a animation - default 1000ms
|
|
58 |
- [maxAngleValue] - !!! DEPRECATED !!! optional parameter, maximum angle possible for animation,
|
|
59 |
- [minAngleValue] - !!! DEPRECATED !!! optional parameter, minimum angle possible for animation,
|
|
60 |
- [callbackFunction] - optional function to run after animation is done
|
|
61 |
- [animatedGifBoolean](boolean) - optional set to display animated gif in firefox/chrome/safari
|
|
62 |
!!! this might slow down browser because it need to render image again and
|
|
63 |
again to display animation,
|
|
64 |
- [bind: [ {event: function}...] -optional parameter, list of events binded
|
|
65 |
to newly created rotateable object
|
|
66 |
|
|
67 |
Examples:
|
|
68 |
|
|
69 |
$(document).ready(function()
|
|
70 |
{
|
|
71 |
$('#image').rotate(-25);
|
|
72 |
});
|
|
73 |
|
|
74 |
$(document).ready(function()
|
|
75 |
{
|
|
76 |
$('#image2').rotate({angle:5});
|
|
77 |
});
|
|
78 |
|
|
79 |
$(document).ready(function()
|
|
80 |
{
|
|
81 |
var rot=$('#image3').rotate({maxAngle:25,minAngle:-55, duration:570,
|
|
82 |
easing:$.easing.easeInOutExpo,
|
|
83 |
bind:
|
|
84 |
[
|
|
85 |
{"mouseover":function(){rot[0].rotateAnimation(85);}},
|
|
86 |
{"mouseout":function(){rot[0].rotateAnimation(-35);}}
|
|
87 |
]
|
|
88 |
});
|
|
89 |
});
|
|
90 |
*/
|
|
91 |
|
|
92 |
(function($) {
|
|
93 |
var supportedCSS,styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform".split(" "); //MozTransform <- firefox works slower with css!!!
|
|
94 |
for (var a=0;a<toCheck.length;a++) if (styles[toCheck[a]] !== undefined) supportedCSS = toCheck[a];
|
|
95 |
// Bad eval to preven google closure to remove it from code o_O
|
|
96 |
// After compresion replace it back to var IE = 'v' == '\v'
|
|
97 |
var IE = eval('"v"=="\v"');
|
|
98 |
|
|
99 |
jQuery.fn.extend({
|
|
100 |
ImageRotate:function(parameters)
|
|
101 |
{
|
|
102 |
// If this element is already a Wilq32.PhotoEffect object, skip creation
|
|
103 |
if (this.Wilq32&&this.Wilq32.PhotoEffect) return;
|
|
104 |
// parameters might be applied to many objects - so because we use them later - a fresh instance is needed
|
|
105 |
var paramClone = $.extend(true, {}, parameters);
|
|
106 |
return (new Wilq32.PhotoEffect(this.get(0),paramClone))._temp;
|
|
107 |
},
|
|
108 |
rotate:function(parameters)
|
|
109 |
{
|
|
110 |
if (this.length===0||typeof parameters=="undefined") return;
|
|
111 |
if (typeof parameters=="number") parameters={angle:parameters};
|
|
112 |
var returned=[];
|
|
113 |
for (var i=0,i0=this.length;i<i0;i++)
|
|
114 |
{
|
|
115 |
var element=this.get(i);
|
|
116 |
if (typeof element.Wilq32 == "undefined")
|
|
117 |
returned.push($($(element).ImageRotate(parameters)));
|
|
118 |
else
|
|
119 |
{
|
|
120 |
element.Wilq32.PhotoEffect._rotate(parameters.angle);
|
|
121 |
}
|
|
122 |
}
|
|
123 |
return returned;
|
|
124 |
},
|
|
125 |
|
|
126 |
rotateAnimation:function(parameters)
|
|
127 |
{
|
|
128 |
if (this.length===0||typeof parameters=="undefined") return;
|
|
129 |
if (typeof parameters=="number") parameters={animateAngle:parameters};
|
|
130 |
var returned=[];
|
|
131 |
for (var i=0,i0=this.length;i<i0;i++)
|
|
132 |
{
|
|
133 |
var element=this.get(i);
|
|
134 |
if (typeof element.Wilq32 == "undefined")
|
|
135 |
returned.push($($(element).ImageRotate(parameters)));
|
|
136 |
else
|
|
137 |
{
|
|
138 |
element.Wilq32.PhotoEffect.rotateAnimation(parameters);
|
|
139 |
}
|
|
140 |
}
|
|
141 |
return returned;
|
|
142 |
}
|
|
143 |
|
|
144 |
});
|
|
145 |
|
|
146 |
// Library agnostic interface
|
|
147 |
|
|
148 |
Wilq32=window.Wilq32||{};
|
|
149 |
Wilq32.PhotoEffect=(function(){
|
|
150 |
function setupParameters(img,parameters){
|
|
151 |
this._img = img;
|
|
152 |
this._parameters = parameters || {};
|
|
153 |
this._parameters.angle = this._angle = parameters.angle || 0;
|
|
154 |
this._parameters.animateAngle = typeof parameters.animateAngle=="number" ? parameters.animateAngle : this._angle;
|
|
155 |
this._parameters.easing = parameters.easing || function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }
|
|
156 |
this._parameters.duration = parameters.duration || 1000;
|
|
157 |
}
|
|
158 |
if (supportedCSS) {
|
|
159 |
return function(img,parameters){
|
|
160 |
setupParameters.call(this,img,parameters);
|
|
161 |
img.Wilq32 = {
|
|
162 |
PhotoEffect: this
|
|
163 |
};
|
|
164 |
// TODO: needed to have a _temp variable accessible outside - used for object retrieval,
|
|
165 |
// needs refactor + change name (temp is not self descriptive)
|
|
166 |
// also need better passing values between functions - to FIX (remove _temp and _img at all)
|
|
167 |
this._temp = this._img;
|
|
168 |
this._BindEvents(img,this._parameters.bind);
|
|
169 |
this._rotate(this._parameters.angle);
|
|
170 |
if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);
|
|
171 |
}
|
|
172 |
} else {
|
|
173 |
return function(img,parameters) {
|
|
174 |
setupParameters.call(this,img,parameters);
|
|
175 |
// Make sure that class and id are also copied - just in case you would like to refeer to an newly created object
|
|
176 |
this._parameters.className=img.className;
|
|
177 |
this._parameters.id=img.getAttribute('id');
|
|
178 |
|
|
179 |
this._temp=document.createElement('span');
|
|
180 |
this._temp.style.display="inline-block";
|
|
181 |
this._temp.Wilq32 =
|
|
182 |
{
|
|
183 |
PhotoEffect: this
|
|
184 |
};
|
|
185 |
img.parentNode.insertBefore(this._temp,img);
|
|
186 |
|
|
187 |
if (img.complete) {
|
|
188 |
this._Loader();
|
|
189 |
} else {
|
|
190 |
var self=this;
|
|
191 |
// TODO: Remove jQuery dependency
|
|
192 |
jQuery(this._img).bind("load", function()
|
|
193 |
{
|
|
194 |
self._Loader();
|
|
195 |
});
|
|
196 |
}
|
|
197 |
}
|
|
198 |
}
|
|
199 |
})();
|
|
200 |
|
|
201 |
Wilq32.PhotoEffect.prototype={
|
|
202 |
|
|
203 |
rotateAnimation : function(parameters){
|
|
204 |
this._parameters.animateAngle = parameters.animateAngle;
|
|
205 |
this._parameters.callback = parameters.callback || this._parameters.callback || function(){};
|
|
206 |
this._animateStart();
|
|
207 |
},
|
|
208 |
|
|
209 |
_BindEvents:function(element,events){
|
|
210 |
if (events)
|
|
211 |
{
|
|
212 |
for (var a in events) if (events.hasOwnProperty(a))
|
|
213 |
for (var b in events[a]) if (events[a].hasOwnProperty(b))
|
|
214 |
// TODO: Remove jQuery dependency
|
|
215 |
jQuery(element).bind(b,events[a][b]);
|
|
216 |
}
|
|
217 |
},
|
|
218 |
|
|
219 |
_Loader:(function()
|
|
220 |
{
|
|
221 |
if (IE)
|
|
222 |
return function()
|
|
223 |
{
|
|
224 |
var width=this._img.width;
|
|
225 |
var height=this._img.height;
|
|
226 |
this._img.parentNode.removeChild(this._img);
|
|
227 |
|
|
228 |
this._vimage = this.createVMLNode('image');
|
|
229 |
this._vimage.src=this._img.src;
|
|
230 |
this._vimage.style.height=height+"px";
|
|
231 |
this._vimage.style.width=width+"px";
|
|
232 |
this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
|
|
233 |
this._vimage.style.top = "0px";
|
|
234 |
this._vimage.style.left = "0px";
|
|
235 |
|
|
236 |
/* Group minifying a small 1px precision problem when rotating object */
|
|
237 |
this._container = this.createVMLNode('group');
|
|
238 |
this._container.style.width=width;
|
|
239 |
this._container.style.height=height;
|
|
240 |
this._container.style.position="absolute";
|
|
241 |
this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix that ugly problem
|
|
242 |
this._container.appendChild(this._vimage);
|
|
243 |
|
|
244 |
this._temp.appendChild(this._container);
|
|
245 |
this._temp.style.position="relative"; // FIXES IE PROBLEM
|
|
246 |
this._temp.style.width=width+"px";
|
|
247 |
this._temp.style.height=height+"px";
|
|
248 |
this._temp.setAttribute('id',this._parameters.id);
|
|
249 |
this._temp.className=this._parameters.className;
|
|
250 |
|
|
251 |
this._BindEvents(this._temp,this._parameters.bind);
|
|
252 |
_finally.call(this);
|
|
253 |
|
|
254 |
}
|
|
255 |
else
|
|
256 |
return function ()
|
|
257 |
{
|
|
258 |
this._temp.setAttribute('id',this._parameters.id);
|
|
259 |
this._temp.className=this._parameters.className;
|
|
260 |
|
|
261 |
this._width=this._img.width;
|
|
262 |
this._height=this._img.height;
|
|
263 |
this._widthHalf=this._width/2; // used for optimisation
|
|
264 |
this._heightHalf=this._height/2;// used for optimisation
|
|
265 |
|
|
266 |
var _widthMax=Math.sqrt((this._height)*(this._height) + (this._width) * (this._width));
|
|
267 |
|
|
268 |
this._widthAdd = _widthMax - this._width;
|
|
269 |
this._heightAdd = _widthMax - this._height; // widthMax because maxWidth=maxHeight
|
|
270 |
this._widthAddHalf=this._widthAdd/2; // used for optimisation
|
|
271 |
this._heightAddHalf=this._heightAdd/2;// used for optimisation
|
|
272 |
|
|
273 |
this._img.parentNode.removeChild(this._img);
|
|
274 |
|
|
275 |
this._aspectW = ((parseInt(this._img.style.width,10)) || this._width)/this._img.width;
|
|
276 |
this._aspectH = ((parseInt(this._img.style.height,10)) || this._height)/this._img.height;
|
|
277 |
|
|
278 |
this._canvas=document.createElement('canvas');
|
|
279 |
this._canvas.setAttribute('width',this._width);
|
|
280 |
this._canvas.style.position="relative";
|
|
281 |
this._canvas.style.left = -this._widthAddHalf + "px";
|
|
282 |
this._canvas.style.top = -this._heightAddHalf + "px";
|
|
283 |
this._canvas.Wilq32 = this._temp.Wilq32;
|
|
284 |
|
|
285 |
this._temp.appendChild(this._canvas);
|
|
286 |
this._temp.style.width=this._width+"px";
|
|
287 |
this._temp.style.height=this._height+"px";
|
|
288 |
|
|
289 |
this._BindEvents(this._canvas,this._parameters.bind);
|
|
290 |
this._cnv=this._canvas.getContext('2d');
|
|
291 |
_finally.call(this);
|
|
292 |
}
|
|
293 |
function _finally(){
|
|
294 |
this._rotate(this._parameters.angle);
|
|
295 |
if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);
|
|
296 |
}
|
|
297 |
|
|
298 |
})(),
|
|
299 |
|
|
300 |
_animateStart:function()
|
|
301 |
{
|
|
302 |
if (this._timer) {
|
|
303 |
clearTimeout(this._timer);
|
|
304 |
}
|
|
305 |
this._animateStartTime = +new Date;
|
|
306 |
this._animateStartAngle = this._angle;
|
|
307 |
this._animate();
|
|
308 |
},
|
|
309 |
_animate:function()
|
|
310 |
{
|
|
311 |
var actualTime = +new Date;
|
|
312 |
//var checkEnd = !!(Math.round(this._angle * 100 - this._parameters.animateAngle * 100)) == 0 && !!this._timer;
|
|
313 |
var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
|
|
314 |
if (this._parameters.callback && checkEnd){
|
|
315 |
this._parameters.callback();
|
|
316 |
}
|
|
317 |
|
|
318 |
// TODO: Bug for animatedGif for static rotation ? (to test)
|
|
319 |
if (checkEnd && !this._parameters.animatedGif)
|
|
320 |
{
|
|
321 |
clearTimeout(this._timer);
|
|
322 |
}
|
|
323 |
else
|
|
324 |
{
|
|
325 |
if (this._canvas||this._vimage||this._img) {
|
|
326 |
// TODO: implement easing and speed of animation
|
|
327 |
this._angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateAngle - this._animateStartAngle, this._parameters.duration);
|
|
328 |
//this._angle-=(this._angle-this._parameters.animateAngle)*0.1;
|
|
329 |
//if (typeof this._parameters.minAngle!="undefined") this._angle=Math.max(this._angle,this._parameters.minAngle);
|
|
330 |
//if (typeof this._parameters.maxAngle!="undefined") this._angle=Math.min(this._angle,this._parameters.maxAngle);
|
|
331 |
this._rotate((~~(this._angle*10))/10);
|
|
332 |
}
|
|
333 |
var self = this;
|
|
334 |
this._timer = setTimeout(function()
|
|
335 |
{
|
|
336 |
self._animate.call(self);
|
|
337 |
}, 10);
|
|
338 |
}
|
|
339 |
},
|
|
340 |
|
|
341 |
_rotate : (function()
|
|
342 |
{
|
|
343 |
var rad = Math.PI/180;
|
|
344 |
if (IE)
|
|
345 |
return function(angle)
|
|
346 |
{
|
|
347 |
this._container.style.rotation=angle+"deg";
|
|
348 |
}
|
|
349 |
else if (supportedCSS)
|
|
350 |
return function(angle){
|
|
351 |
this._img.style[supportedCSS]="rotate("+angle+"deg)";
|
|
352 |
}
|
|
353 |
else
|
|
354 |
return function(angle)
|
|
355 |
{
|
|
356 |
|
|
357 |
if (!this._img.width||typeof angle!="number") return;
|
|
358 |
angle=(angle%360)* rad;
|
|
359 |
// clear canvas
|
|
360 |
this._canvas.width = this._width+this._widthAdd;
|
|
361 |
this._canvas.height = this._height+this._heightAdd;
|
|
362 |
|
|
363 |
// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
|
|
364 |
this._cnv.translate(this._widthAddHalf,this._heightAddHalf); // at least center image on screen
|
|
365 |
this._cnv.translate(this._widthHalf,this._heightHalf); // we move image back to its orginal
|
|
366 |
this._cnv.rotate(angle); // rotate image
|
|
367 |
this._cnv.translate(-this._widthHalf,-this._heightHalf); // move image to its center, so we can rotate around its center
|
|
368 |
this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
|
|
369 |
this._cnv.drawImage(this._img, 0, 0); // First - we draw image
|
|
370 |
}
|
|
371 |
|
|
372 |
})()
|
|
373 |
}
|
|
374 |
|
|
375 |
if (IE)
|
|
376 |
{
|
|
377 |
Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
|
|
378 |
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
|
|
379 |
try {
|
|
380 |
!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
|
|
381 |
return function (tagName) {
|
|
382 |
return document.createElement('<rvml:' + tagName + ' class="rvml">');
|
|
383 |
};
|
|
384 |
} catch (e) {
|
|
385 |
return function (tagName) {
|
|
386 |
return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
|
|
387 |
};
|
|
388 |
}
|
|
389 |
})();
|
|
390 |
}
|
|
391 |
|
|
392 |
})(jQuery);
|