root / ui / static / jQueryRotate.js @ 24d0c6a9
History | View | Annotate | Download (13.3 kB)
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); |