source: Dev/branches/rest-dojo-ui/client/dojox/fx/text.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 16.1 KB
Line 
1define(["dojo/_base/lang", "./_base", "dojo/_base/fx", "dojo/fx","dojo/fx/easing", "dojo/dom", "dojo/dom-style", "dojo/_base/html", "dojo/_base/connect"],
2function(lang, dojoxFx, baseFx, coreFx, easingLib, dom, domStyle, htmlLib, connectUtil ){
3var textFx = lang.getObject("dojox.fx.text", true);
4textFx._split = function(/*Object*/ args){
5        // summary: Split a block of text into words or letters
6        //
7        // description:
8        //              Returns an animation that will split the node into a grid
9        //              of pieces that move independently.
10        //
11        // NOTE:
12        //              In some rendering engines, the text will appear to "jump" from its initial position
13        //              when the animation begins.      To work around this bug, enclose the node's text in a <p> or <div>.
14        //
15        //      args:
16        //              args.crop: Boolean - If true, pieces will be positioned relatively rather than absolutely
17        //              args.text: String - Text to place inside the node (otherwise node.innerHTML is used)
18        //              args.words: Boolean - If true, the text will be split into words rather than characters
19        //              args.pieceAnimation: Function(piece, pieceCoords, nodeCoords, number, numPieces)
20        //                      - Returns either the dojo.Animation or an array of dojo.Animation objects for the piece;
21        //                      pieceCoords is the result of dojo.coords(piece, true);
22        //                      nodeCoords is the result of dojo.coords(args.node, true);
23        //                      number is the piece's position in the array of pieces, and numPieces is the array.length
24
25        var node = args.node = dom.byId(args.node),
26                s = node.style,
27                cs = domStyle.getComputedStyle(node),
28                nodeCoords = htmlLib.coords(node, true);
29               
30        args.duration = args.duration || 1000;
31        args.words = args.words || false;
32       
33        var originalHTML = (args.text && typeof(args.text) == "string") ? args.text : node.innerHTML,
34                originalHeight = s.height,
35                originalWidth = s.width,
36                animations = [];
37
38        domStyle.set(node, {
39                height: cs.height,
40                width: cs.width
41        });
42
43        // The following regular expression courtesy of Phil Haack
44        // http://haacked.com/archive/2004/10/25/usingregularexpressionstomatchhtml.aspx
45        var tagReg = /(<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>)/g;
46
47        // Translation: /(HTML tag plus spaces)|(word/letter without '<' plus spaces)/g
48        var reg = (args.words ?
49                /(<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>)\s*|([^\s<]+\s*)/g :
50                /(<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>)\s*|([^\s<]\s*)/g
51        );
52
53        // Split the text into pieces
54        var pieces = (typeof args.text == "string") ? args.text.match(reg) : node.innerHTML.match(reg);
55        var html = "";
56        var numPieces = 0;
57        var number = 0;
58        for(var i = 0; i < pieces.length; i++){
59                var piece = pieces[i];
60                if(!piece.match(tagReg)){
61                        html += "<span>" + piece + "</span>";
62                        numPieces++;
63                }else{
64                        html += piece;
65                }
66        }
67        node.innerHTML = html;
68
69        // Find the newly-created spans and create their animations
70        function animatePieces(piece){
71                var next = piece.nextSibling;
72                if(piece.tagName == "SPAN" && piece.childNodes.length == 1 && piece.firstChild.nodeType == 3){
73                        var pieceCoords = htmlLib.coords(piece, true);
74                        number++;
75                        domStyle.set(piece, {
76                                padding: 0,
77                                margin: 0,
78                                top: (args.crop ? "0px" : pieceCoords.t + "px"),
79                                left: (args.crop ? "0px" : pieceCoords.l + "px"),
80                                display: "inline"
81                        });
82                        var pieceAnimation = args.pieceAnimation(piece, pieceCoords, nodeCoords, number, numPieces);
83                        if(lang.isArray(pieceAnimation)){
84                                // if pieceAnimation is an array, append its elements
85                                animations = animations.concat(pieceAnimation);
86                        }else{
87                                // otherwise, append it
88                                animations[animations.length] = pieceAnimation;
89                        }
90                }else if(piece.firstChild){
91                        animatePieces(piece.firstChild);
92                }
93
94                if(next){
95                        animatePieces(next);
96                }
97        }
98
99        animatePieces(node.firstChild);
100        var anim = coreFx.combine(animations);
101        connectUtil.connect(anim, "onEnd", anim, function(){
102                node.innerHTML = originalHTML;
103                domStyle.set(node, {
104                        height: originalHeight,
105                        width: originalWidth
106                });
107        });
108        if(args.onPlay){
109                connectUtil.connect(anim, "onPlay", anim, args.onPlay);
110        }
111        if(args.onEnd){
112                connectUtil.connect(anim, "onEnd", anim, args.onEnd);
113        }
114        return anim; // dojo.Animation
115};
116
117textFx.explode = function(/*Object*/ args){
118        // summary: Explode a block of text into words or letters
119        //
120        // description:
121        //              Returns an animation that will split the text into a spans
122        //              of words or characters that fly away from the center.
123        //
124        //      args:
125        //              args.crop: Boolean - If true, pieces will be positioned relatively rather than absolutely
126        //              args.words: Boolean - If true, text will be split into words rather than characters
127        //              args.random: Float - If set, pieces fly to random distances, for random durations,
128        //                                                         and in slightly random directions. The value defines how much
129        //                                                         randomness is introduced.
130        //              args.distance: Float - Multiplier for the distance the pieces fly (even when random)
131        //              args.fade: Boolean - If true, pieces fade out while in motion (default is true)
132        //              args.fadeEasing: Function - If args.fade is true, the fade animations use this easing function
133        //              args.unhide: Boolean - If true, the animation is reversed
134        //              args.sync: Boolean - If args.unhide is true, all the pieces converge at the same time
135        //                                                       (default is true)
136
137        var node = args.node = dom.byId(args.node);
138        var s = node.style;
139
140        args.distance = args.distance || 1;
141        args.duration = args.duration || 1000;
142        args.random = args.random || 0;
143        if(typeof(args.fade) == "undefined"){
144                args.fade = true;
145        }
146        if(typeof(args.sync) == "undefined"){
147                args.sync = true;
148        }
149        args.random = Math.abs(args.random);
150
151        // Returns the animation object for each piece
152        args.pieceAnimation = function(piece, pieceCoords, coords, number, numPieces){
153                var pieceHeight = pieceCoords.h;
154                var pieceWidth = pieceCoords.w;
155                var distance = args.distance * 2;
156                var duration = args.duration;
157                var startTop = parseFloat(piece.style.top);
158                var startLeft = parseFloat(piece.style.left);
159                var delay = 0;
160                var randomX = 0;
161                var randomY = 0;
162                if(args.random){
163                        var seed = (Math.random() * args.random) + Math.max(1 - args.random, 0);
164                        distance *= seed;
165                        duration *= seed;
166                        // To syncronize, give each piece an appropriate delay so they end together
167                        delay = ((args.unhide && args.sync) || (!args.unhide && !args.sync)) ? (args.duration - duration) : 0;
168
169                        // Slightly randomize the direction of each piece
170                        randomX = Math.random() - 0.5;
171                        randomY = Math.random() - 0.5;
172                }
173                var distanceY = ((coords.h - pieceHeight) / 2 - (pieceCoords.y - coords.y));
174                var distanceX = ((coords.w - pieceWidth) / 2 - (pieceCoords.x - coords.x));
175                var distanceXY = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
176                var endTop = startTop - distanceY * distance + distanceXY * randomY;
177                var endLeft = startLeft - distanceX * distance + distanceXY * randomX;
178
179                // Create the animation objects for the piece
180                // These are separate anim objects so they can have different curves
181                var pieceSlide = baseFx.animateProperty({
182                        node: piece,
183                        duration: duration,
184                        delay: delay,
185                        easing: (args.easing || (args.unhide ? easingLib.sinOut : easingLib.circOut)),
186                        beforeBegin: (args.unhide ? function(){
187                                        if(args.fade){
188                                                //piece.style.opacity = 0;
189                                                domStyle.set(piece,"opacity", 0);
190                                        }
191                                        piece.style.position = args.crop ? "relative" : "absolute";
192                                        piece.style.top = endTop + "px";
193                                        piece.style.left = endLeft + "px";
194                                } : function(){piece.style.position = args.crop ? "relative" : "absolute";}),
195                        properties: {
196                                top: (args.unhide ? { start: endTop, end: startTop } : { start: startTop, end: endTop }),
197                                left: (args.unhide ? { start: endLeft, end: startLeft } : { start: startLeft, end: endLeft })
198                        }
199                });
200
201                if(args.fade){
202                        var pieceFade = baseFx.animateProperty({
203                                node: piece,
204                                duration: duration,
205                                delay: delay,
206                                easing: (args.fadeEasing || easingLib.quadOut),
207                                properties: {
208                                        opacity: (args.unhide ? {start: 0, end: 1} : {end: 0})
209                                }
210                        });
211
212                        // return both animations as an array
213                        return (args.unhide ? [pieceFade, pieceSlide] : [pieceSlide, pieceFade]);
214                }else{
215                        // Otherwise return only the slide animation
216                        return pieceSlide;
217                }
218        };
219
220        var anim = textFx._split(args);
221        return anim; // dojo.Animation
222};
223
224textFx.converge = function(/*Object*/ args){
225        args.unhide = true;
226        return textFx.explode(args);
227};
228
229textFx.disintegrate = function(/*Object*/ args){
230        // summary: Split a block of text into words or letters and let them fall
231        //
232        // description:
233        //              Returns an animation that will split the text into spans of words
234        //              or characters that drop.
235        //
236        //      args:
237        //              args.crop: Boolean - If true, pieces will be positioned relatively rather than absolutely
238        //              args.words: Boolean - If true, text will be split into words rather than characters
239        //              args.interval: Float - The number of milliseconds between each piece's animation
240        //              args.distance: Float - The number of the node's heights to drop (default is 1.5)
241        //              args.fade: Boolean - If true, pieces fade out while in motion (default is true)
242        //              args.random: Float - If set, pieces fall in random order. The value defines how much
243        //                                                         randomness is introduced
244        //              args.reverseOrder: Boolean - If true, pieces animate in reversed order
245        //              args.unhide: Boolean - If true, the peices fall from above and land in place
246
247        var node = args.node = dom.byId(args.node);
248        var s = node.style;
249
250        args.duration = args.duration || 1500;
251        args.distance = args.distance || 1.5;
252        args.random = args.random || 0;
253        if(!args.fade){
254                args.fade = true;
255        }
256        var random = Math.abs(args.random);
257
258        // Returns the animation object for each piece
259        args.pieceAnimation = function(piece, pieceCoords, coords, number, numPieces){
260                var pieceHeight = pieceCoords.h;
261                var pieceWidth = pieceCoords.w;
262
263                var interval = args.interval || (args.duration / (1.5 * numPieces));
264                var duration = (args.duration - numPieces * interval);
265
266                var randomDelay = Math.random() * numPieces * interval;
267                // If distance is negative, start from the top right instead of bottom left
268                var uniformDelay = (args.reverseOrder || args.distance < 0) ?
269                        (number * interval) : ((numPieces - number) * interval);
270                var delay = randomDelay * random + Math.max(1 - random, 0) * uniformDelay;
271
272                // Create the animation object for the piece
273                var properties = {};
274                if(args.unhide){
275                        properties.top = {
276                                start: (parseFloat(piece.style.top) - coords.h * args.distance),
277                                end: parseFloat(piece.style.top)
278                        };
279                        if(args.fade){
280                                properties.opacity = {start: 0, end: 1};
281                        }
282                }else{
283                        properties.top = {end: (parseFloat(piece.style.top) + coords.h * args.distance)};
284                        if(args.fade){
285                                properties.opacity = {end: 0};
286                        }
287                }
288                var pieceAnimation = baseFx.animateProperty({
289                        node: piece,
290                        duration: duration,
291                        delay: delay,
292                        easing: (args.easing || (args.unhide ? easingLib.sinIn : easingLib.circIn)),
293                        properties: properties,
294                        beforeBegin: (args.unhide ? function(){
295                                if(args.fade){
296                                        // piece.style.opacity = 0;
297                                        domStyle.set(piece, "opacity", 0);
298                                }
299                                piece.style.position = args.crop ? "relative" : "absolute";
300                                piece.style.top = properties.top.start + "px";
301                        } : function(){ piece.style.position = args.crop ? "relative" : "absolute";})
302                });
303
304                return pieceAnimation;
305        };
306
307        var anim = textFx._split(args);
308        return anim; // dojo.Animation
309};
310
311textFx.build = function(/*Object*/ args){
312        args.unhide = true;
313        return textFx.disintegrate(args);
314};
315
316textFx.blockFadeOut = function(/*Object*/ args){
317        // summary: Split a block of text into words or letters and fade them
318        //
319        // description:
320        //              Returns an animation that will split the text into spans of words
321        //              or characters that fade in or out.
322        //
323        //      args:
324        //              args.words: Boolean - If true, text will be split into words rather than characters
325        //              args.interval: Float - The number of milliseconds between each piece's animation (default is 0)
326        //              args.random: Float - If true, pieces have a random delay. The value defines how much
327        //                                                         randomness is introduced
328        //              args.reverseOrder: Boolean - If true, pieces animate in reversed order
329        //              args.unhide: Boolean - If true, the animation is reversed
330
331        var node = args.node = dom.byId(args.node);;
332        var s = node.style;
333
334        args.duration = args.duration || 1000;
335        args.random = args.random || 0;
336        var random = Math.abs(args.random);
337
338        // Returns the animation object for each piece
339        args.pieceAnimation = function(piece, pieceCoords, coords, number, numPieces){
340                var interval = args.interval || (args.duration / (1.5 * numPieces));
341                var duration = (args.duration - numPieces * interval);
342
343                var randomDelay = Math.random() * numPieces * interval;
344                // If interval or random is negative, start from the bottom instead of top
345                var uniformDelay = (args.reverseOrder) ?
346                        ((numPieces - number) * interval) : (number * interval);
347                var delay = randomDelay * random + Math.max(1 - random, 0) * uniformDelay;
348
349                // Create the animation object for the piece
350                var pieceAnimation = baseFx.animateProperty({
351                        node: piece,
352                        duration: duration,
353                        delay: delay,
354                        easing: (args.easing || easingLib.sinInOut),
355                        properties: {
356                                opacity: (args.unhide ? {start: 0, end: 1} : {end:0})
357                        },
358                        beforeBegin: (args.unhide ? function(){ domStyle.set(piece,"opacity",0); } : undefined)
359                });
360
361                return pieceAnimation;
362        };
363
364        var anim = textFx._split(args);
365        return anim; // dojo.Animation
366};
367
368textFx.blockFadeIn = function(/*Object*/ args){
369        args.unhide = true;
370        return textFx.blockFadeOut(args);
371};
372
373textFx.backspace = function(/*Object*/ args){
374        // summary: Split a block of text into words or letters and backspace them in sequence
375        //
376        // description:
377        //              Returns an animation that will split the text into spans of words
378        //              or characters that appear as if they were being backspaced (or typed) in real-time.
379        //
380        //      args:
381        //              args.interval: Float - The number of milliseconds between each piece's animation
382        //                                                         (default is determined by text length and args.duration);
383        //              args.wordDelay: Integer - The number of milliseconds between each word
384        //                                                                (only effective when args.unhide = true)
385        //              args.fixed: Boolean - If true, only style.opacity changes; otherwise, style.display
386        //                                                        changes between none and inline, adding realism (default = false)
387        //              args.random: Float - If true, pieces have a random delay. The value defines how much
388        //                                                         randomness is introduced (only effective when args.unhide = true)
389        //              args.unhide: Boolean - If true, the animation is reversed
390
391        var node = args.node = dom.byId(args.node);
392        var s = node.style;
393
394        args.words = false;
395        args.duration = args.duration || 2000;
396        args.random = args.random || 0;
397        var random = Math.abs(args.random);
398        var delay = 10;
399
400        // Returns the animation object for each piece
401        args.pieceAnimation = function(piece, pieceCoords, coords, number, numPieces){
402                var interval = args.interval || (args.duration / (1.5 * numPieces)),
403                        text = ("textContent" in piece) ? piece.textContent : piece.innerText,
404                        whitespace = text.match(/\s/g);
405
406                if(typeof(args.wordDelay) == "undefined"){
407                        args.wordDelay = interval * 2;
408                }
409
410                if(!args.unhide){
411                        delay = (numPieces - number - 1) * interval;
412                }
413
414                var beforeBegin, onEnd;
415
416                if(args.fixed){
417                        if(args.unhide){
418                                var beforeBegin = function(){ domStyle.set(piece,"opacity",0); };
419                        }
420                }else{
421                        if(args.unhide){
422                                var beforeBegin = function(){piece.style.display = "none";};
423                                var onEnd = function(){piece.style.display = "inline";};
424                        }else{
425                                var onEnd = function(){piece.style.display = "none";};
426                        }
427                }
428
429                // Create the animation object for the piece
430                var pieceAnimation = baseFx.animateProperty({
431                        node: piece,
432                        duration: 1,
433                        delay: delay,
434                        easing: (args.easing || easingLib.sinInOut),
435                        properties: {
436                                opacity: (args.unhide ? {start: 0, end: 1} : {end:0})
437                        },
438                        beforeBegin: beforeBegin,
439                        onEnd: onEnd
440                });
441
442                if(args.unhide){
443                        var randomDelay = Math.random() * text.length * interval;
444                        var wordDelay = randomDelay * random / 2 + Math.max(1 - random / 2, 0) * args.wordDelay;
445
446                        delay += randomDelay * random + Math.max(1 - random, 0) * interval * text.length +
447                                (wordDelay * (whitespace && text.lastIndexOf(whitespace[whitespace.length-1]) == text.length - 1));
448                }
449
450                return pieceAnimation;
451        };
452
453        var anim = textFx._split(args);
454        return anim; // dojo.Animation
455};
456
457textFx.type = function(/*Object*/ args){
458        args.unhide = true;
459        return textFx.backspace(args);
460};
461return textFx;
462});
Note: See TracBrowser for help on using the repository browser.