3 * http://larsjung.de/twinkle
5 * provided under the terms of the MIT License
11 var TwinkleEvent = function ( offX
, offY
, element
, posX
, posY
) {
13 this.offset
= { "left": offX
, "top": offY
};
14 this.element
= element
;
15 this.position
= { "left": posX
, "top": posY
};
19 var Twinkler = function () {
27 "effectOptions": undefined
32 this.twinkle = function ( event
, options
) {
34 var settings
= $.extend( {}, this.defaults
, options
);
36 var effect
= this.effects
[settings
.effect
];
37 if ( effect
!== undefined ) {
38 event
.element
= event
.element
|| "body";
39 effect
.run( event
, settings
.effectOptions
);
44 this.twinkleAtElement = function ( htmlElement
, options
) {
46 var settings
= $.extend( {}, this.defaults
, options
);
48 var $ele
= $( htmlElement
);
50 var off
= $ele
.offset();
51 var offX
= off
.left
+ $ele
.outerWidth( true ) * settings
.widthRatio
;
52 var offY
= off
.top
+ $ele
.outerHeight( true ) * settings
.heightRatio
;
54 var pos
= $ele
.position();
55 var posX
= pos
.left
+ $ele
.outerWidth( true ) * settings
.widthRatio
;
56 var posY
= pos
.top
+ $ele
.outerHeight( true ) * settings
.heightRatio
;
58 var event
= new TwinkleEvent( offX
, offY
, htmlElement
, posX
, posY
);
60 this.twinkle( event
, options
);
64 this.twinkleAtElements = function ( htmlElements
, options
) {
67 var settings
= $.extend( {}, this.defaults
, options
);
69 var delay
= settings
.delay
;
70 $( htmlElements
).each( function () {
71 var htmlElement
= this;
72 setTimeout( function () {
73 THIS
.twinkleAtElement( htmlElement
, options
);
75 delay
+= settings
.gap
;
82 var twinkler
= new Twinkler();
83 var namespace = "twinkle";
88 twinkle: function ( element
, left
, top
, options
) {
90 var event
= new TwinkleEvent( 0, 0, element
, left
, top
);
91 twinkler
.twinkle( event
, options
);
95 add: function ( effect
) {
97 if ( twinkler
.effects
[effect
.id
] === undefined ) {
98 twinkler
.effects
[effect
.id
] = effect
;
103 remove: function ( effect
) {
105 if ( twinkler
.effects
[effect
.id
] !== undefined ) {
106 delete twinkler
.effects
[effect
.id
];
115 twinkle: function ( options
) {
117 twinkler
.twinkleAtElements( this, options
);
123 $[namespace] = globals
;
124 $.fn
[namespace] = function( method
) {
126 if ( methods
[method
] ) {
127 return methods
[method
].apply( this, Array
.prototype.slice
.call( arguments
, 1 ) );
128 } else if ( method
=== undefined || method
instanceof Object
) {
129 return methods
.twinkle
.apply( this, arguments
);
131 $.error( "Method " + method
+ " does not exist on jQuery." + namespace );
142 * http://larsjung.de/twinkle
144 * provided under the terms of the MIT License
150 function animation( css
, event
, settings
) {
152 var $dot
= $( "<div />" ).css( css
).appendTo( event
.element
);
154 var fadeIn = function() {
157 "left": event
.position
.left
- settings
.radius
* 0.5,
158 "top": event
.position
.top
- settings
.radius
* 0.5,
159 "width": settings
.radius
,
160 "height": settings
.radius
,
163 settings
.duration
* 0.5,
168 var fadeOut = function () {
171 "left": event
.position
.left
- settings
.radius
,
172 "top": event
.position
.top
- settings
.radius
,
173 "width": settings
.radius
* 2,
174 "height": settings
.radius
* 2,
177 settings
.duration
* 0.5,
182 var cleanUp = function () {
190 var SplashEffect = function () {
193 "color": "rgba(255,0,0,0.5)",
198 this.id
= "splash-css";
200 this.run = function ( event
, options
) {
202 var settings
= $.extend( {}, defaults
, options
);
204 "position": "absolute",
207 "border-radius": settings
.radius
,
208 "background-color": settings
.color
,
209 "box-shadow": "0 0 30px " + settings
.color
,
210 "left": event
.position
.left
,
211 "top": event
.position
.top
,
217 animation( css
, event
, settings
);
222 var DropEffect = function () {
224 var drops
= new DropsEffect();
226 this.id
= "drop-css";
228 this.run = function ( event
, options
) {
230 drops
.run( event
, $.extend( options
, { count
: 1 } ) );
235 var DropsEffect = function () {
238 "color": "rgba(255,0,0,0.5)",
246 this.id
= "drops-css";
248 this.run = function ( event
, options
) {
250 var settings
= $.extend( {}, defaults
, options
);
252 "position": "absolute",
255 "border-radius": settings
.radius
,
256 "border": "" + settings
.width
+ "px solid " + settings
.color
,
257 "left": event
.position
.left
,
258 "top": event
.position
.top
,
265 for ( var i
= 0; i
< settings
.count
; i
++ ) {
266 setTimeout( function () {
267 animation( css
, event
, settings
);
269 delay
+= settings
.delay
;
275 $.twinkle
.add( new SplashEffect() );
276 $.twinkle
.add( new DropEffect() );
277 $.twinkle
.add( new DropsEffect() );
286 * http://larsjung.de/twinkle
288 * provided under the terms of the MIT License
294 var Interpolator = function ( values
) {
296 var equiDist = function( values
) {
298 var dist
= 1 / ( values
.length
- 1 );
300 for ( var i
= 0; i
< values
.length
; i
++ ) {
301 points
.push( { x
: dist
* i
, y
: values
[i
] } );
306 var interpolate = function ( p1
, p2
, x
) {
308 var m
= ( p2
.y
- p1
.y
) / ( p2
.x
- p1
.x
);
309 var y
= p1
.y
+ m
* ( x
- p1
.x
);
313 var findSection = function ( x
) {
315 for ( var i
= 0; i
< points
.length
; i
++ ) {
321 var prev
= points
[i
-1];
322 var current
= points
[i
];
323 if ( x
>= prev
.x
&& x
<= current
.x
) {
324 return [ prev
, current
];
330 var points
= equiDist( values
);
332 this.get = function ( x
) {
334 x
= Math
.max( 0, Math
.min( 1, x
) );
335 var secPts
= findSection( x
);
336 return interpolate( secPts
[0], secPts
[1], x
);
340 Interpolator
.scale = function ( x
, scale
, offset
) {
343 offset
= offset
|| 0;
344 x
= ( x
- offset
) / scale
;
345 return x
>= 0 && x
<= 1 ? x
: undefined;
349 var FrameEvent = function ( ctx
, frac
, millis
) {
353 this.millis
= millis
;
357 var CanvasEffect = function ( twinkleEvent
, width
, height
, frame
) {
359 this.element
= twinkleEvent
.element
;
360 this.x
= twinkleEvent
.position
.left
;
361 this.y
= twinkleEvent
.position
.top
;
363 this.height
= height
;
365 this.$canvas
= undefined;
366 this.ctx
= undefined;
368 this.init = function () {
371 "position": "absolute",
374 "left": this.x
- this.width
* 0.5,
375 "top": this.y
- this.height
* 0.5,
377 "height": this.height
380 this.$canvas
= $( "<canvas width='" + this.width
+ "' height='" + this.height
+ "' />" ).css( css
).appendTo( this.element
);
381 this.ctx
= new Ctx( this.$canvas
.get( 0 ).getContext( "2d" ) );
384 this.destroy = function () {
386 this.$canvas
.remove();
387 this.$canvas
= undefined;
388 this.ctx
= undefined;
391 this.run = function ( duration
, fps
) {
396 var frameCount
= duration
/ 1000 * fps
;
397 var delta
= 1 / frameCount
;
398 for ( var i
= 0; i
< frameCount
+ 1; i
++ ) {
399 ( function ( frac
) {
400 setTimeout( function () {
402 THIS
.frame( new FrameEvent( THIS
.ctx
, frac
, duration
* frac
) );
404 }, duration
* frac
);
408 setTimeout( $.proxy( this.destroy
, this ), duration
);
414 var Path = function ( ctx
) {
416 var context
= ctx
.context
;
419 this.circle = function ( x
, y
, radius
) {
421 context
.arc( x
, y
, radius
, 0, 2 * Math
.PI
, false );
425 this.stroke = function ( strokeWidth
, strokeStyle
) {
427 context
.lineWidth
= strokeWidth
;
428 context
.strokeStyle
= strokeStyle
;
433 this.fill = function ( fillStyle
) {
435 context
.fillStyle
= fillStyle
;
440 this.draw = function ( strokeWidth
, strokeStyle
, fillStyle
) {
442 this.stroke( strokeWidth
, strokeStyle
);
443 this.fill( fillStyle
);
449 var Ctx = function ( context
) {
451 this.context
= context
;
452 this.width
= $( context
.canvas
).width();
453 this.height
= $( context
.canvas
).height();
455 this.clear = function () {
457 this.resetTransform();
458 this.context
.clearRect( 0, 0, this.width
, this.height
);
462 this.resetTransform = function () {
464 this.context
.setTransform( 1, 0, 0, 1, 0, 0 );
468 this.translate = function ( x
, y
) {
470 this.context
.translate( x
, y
);
474 this.rotate = function ( alpha
) {
476 this.context
.rotate( Math
.PI
* alpha
/ 180 );
480 this.opacity = function ( opacity
) {
482 this.context
.globalAlpha
= opacity
;
486 this.path = function () {
488 return new Path( this );
494 var SplashEffect = function () {
497 "color": "rgba(255,0,0,0.5)",
504 this.run = function ( twinkleEvent
, options
) {
506 var settings
= $.extend( {}, defaults
, options
);
507 var size
= settings
.radius
* 2;
508 var opacityIpl
= new Interpolator( [ 0.4, 1, 0 ] );
509 var radiusIpl
= new Interpolator( [ 0, settings
.radius
] );
510 var frame = function ( frameEvent
) {
512 var radius
= radiusIpl
.get( frameEvent
.frac
);
513 var opacity
= opacityIpl
.get( frameEvent
.frac
);
519 .circle( this.width
* 0.5, this.height
* 0.5, radius
)
520 .fill( settings
.color
);
523 new CanvasEffect( twinkleEvent
, size
, size
, frame
).run( settings
.duration
, 25 );
527 $.twinkle
.add( new SplashEffect() );
531 var DropEffect = function () {
534 "color": "rgba(255,0,0,0.5)",
542 this.run = function ( twinkleEvent
, options
) {
544 var settings
= $.extend( {}, defaults
, options
);
545 var size
= settings
.radius
* 2;
546 var opacityIpl
= new Interpolator( [ 0.4, 1, 0 ] );
547 var radiusIpl
= new Interpolator( [ 0, settings
.radius
] );
548 var frame = function ( frameEvent
) {
550 var radius
= radiusIpl
.get( frameEvent
.frac
);
551 var opacity
= opacityIpl
.get( frameEvent
.frac
);
557 .circle( this.width
* 0.5, this.height
* 0.5, radius
)
558 .stroke( settings
.width
, settings
.color
);
561 new CanvasEffect( twinkleEvent
, size
, size
, frame
).run( settings
.duration
, 25 );
565 $.twinkle
.add( new DropEffect() );
569 var DropsEffect = function () {
572 "color": "rgba(255,0,0,0.5)",
582 this.run = function ( twinkleEvent
, options
) {
584 var settings
= $.extend( {}, defaults
, options
);
585 var size
= settings
.radius
* 2;
586 var opacityIpl
= new Interpolator( [ 0.4, 1, 0 ] );
587 var radiusIpl
= new Interpolator( [ 0, settings
.radius
] );
588 var scale
= ( settings
.duration
- ( settings
.count
- 1 ) * settings
.delay
) / settings
.duration
;
589 var offset
= settings
.delay
/ settings
.duration
;
591 var frame = function ( frameEvent
) {
594 for ( var i
= 0; i
< settings
.count
; i
++ ) {
595 var frac
= Interpolator
.scale( frameEvent
.frac
, scale
, offset
* i
);
597 if ( frac
!== undefined ) {
598 var radius
= radiusIpl
.get( frac
);
599 var opacity
= opacityIpl
.get( frac
);
603 .circle( this.width
* 0.5, this.height
* 0.5, radius
)
604 .stroke( settings
.width
, settings
.color
);
609 new CanvasEffect( twinkleEvent
, size
, size
, frame
).run( settings
.duration
, 25 );
613 $.twinkle
.add( new DropsEffect() );
617 var PulseEffect = function () {
620 "color": "rgba(255,0,0,0.5)",
627 this.run = function ( twinkleEvent
, options
) {
629 var settings
= $.extend( {}, defaults
, options
);
630 var size
= settings
.radius
* 2;
631 var opacityIpl
= new Interpolator( [ 0, 1, 0.6, 1, 0.6, 1, 0 ] );
632 var radiusIpl
= new Interpolator( [ 0, settings
.radius
, settings
.radius
* 0.6, settings
.radius
, settings
.radius
* 0.6, settings
.radius
, 0 ] );
633 var frame = function ( frameEvent
) {
635 var x
= this.width
* 0.5;
636 var y
= this.height
* 0.5;
637 var radius
= radiusIpl
.get( frameEvent
.frac
);
638 var opacity
= opacityIpl
.get( frameEvent
.frac
);
644 .circle( this.width
* 0.5, this.height
* 0.5, radius
)
645 .fill( settings
.color
);
648 new CanvasEffect( twinkleEvent
, size
, size
, frame
).run( settings
.duration
, 25 );
652 $.twinkle
.add( new PulseEffect() );
656 var OrbitEffect = function () {
659 "color": "rgba(255,0,0,0.5)",
663 "satellitesRadius": 10,
669 this.run = function ( twinkleEvent
, options
) {
671 var settings
= $.extend( {}, defaults
, options
);
672 var size
= settings
.radius
* 2;
673 var opacityIpl
= new Interpolator( [ 0.4, 1, 1, 0.4 ] );
674 var r
= settings
.radius
- settings
.satellitesRadius
;
675 var radiusIpl
= new Interpolator( [ 0, r
, r
, 0 ] );
676 var frame = function ( frameEvent
) {
678 var radius
= radiusIpl
.get( frameEvent
.frac
);
679 var opacity
= opacityIpl
.get( frameEvent
.frac
);
680 var bog
= Math
.PI
* 2 * settings
.circulations
* frameEvent
.frac
;
685 .translate( this.width
* 0.5, this.height
* 0.5 );
687 var path
= this.ctx
.path();
688 for ( var i
= 0; i
< settings
.satellites
; i
++ ) {
690 bog
+= Math
.PI
* 2 / settings
.satellites
;
691 var x
= Math
.cos( bog
) * radius
;
692 var y
= Math
.sin( bog
) * radius
;
693 path
.circle( x
, y
, settings
.satellitesRadius
);
695 path
.fill( settings
.color
);
698 new CanvasEffect( twinkleEvent
, size
, size
, frame
).run( settings
.duration
, 25 );
702 $.twinkle
.add( new OrbitEffect() );