Statistics
| Branch: | Tag: | Revision:

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);