585 lines
17 KiB
JavaScript
585 lines
17 KiB
JavaScript
|
if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
||
|
dojo._hasResource["dojo._base.fx"] = true;
|
||
|
dojo.provide("dojo._base.fx");
|
||
|
dojo.require("dojo._base.Color");
|
||
|
dojo.require("dojo._base.connect");
|
||
|
dojo.require("dojo._base.declare");
|
||
|
dojo.require("dojo._base.lang");
|
||
|
dojo.require("dojo._base.html");
|
||
|
|
||
|
/*
|
||
|
Animation losely package based on Dan Pupius' work, contributed under CLA:
|
||
|
http://pupius.co.uk/js/Toolkit.Drawing.js
|
||
|
*/
|
||
|
(function(){
|
||
|
|
||
|
var d = dojo;
|
||
|
|
||
|
dojo._Line = function(/*int*/ start, /*int*/ end){
|
||
|
// summary:
|
||
|
// dojo._Line is the object used to generate values from a start value
|
||
|
// to an end value
|
||
|
// start: int
|
||
|
// Beginning value for range
|
||
|
// end: int
|
||
|
// Ending value for range
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.getValue = function(/*float*/ n){
|
||
|
// summary: returns the point on the line
|
||
|
// n: a floating point number greater than 0 and less than 1
|
||
|
return ((this.end - this.start) * n) + this.start; // Decimal
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d.declare("dojo._Animation", null, {
|
||
|
// summary
|
||
|
// A generic animation class that fires callbacks into its handlers
|
||
|
// object at various states. Nearly all dojo animation functions
|
||
|
// return an instance of this method, usually without calling the
|
||
|
// .play() method beforehand. Therefore, you will likely need to
|
||
|
// call .play() on instances of dojo._Animation when one is
|
||
|
// returned.
|
||
|
constructor: function(/*Object*/ args){
|
||
|
d.mixin(this, args);
|
||
|
if(d.isArray(this.curve)){
|
||
|
/* curve: Array
|
||
|
pId: a */
|
||
|
this.curve = new d._Line(this.curve[0], this.curve[1]);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// duration: Integer
|
||
|
// The time in milliseonds the animation will take to run
|
||
|
duration: 350,
|
||
|
|
||
|
/*=====
|
||
|
// curve: dojo._Line||Array
|
||
|
// A two element array of start and end values, or a dojo._Line instance to be
|
||
|
// used in the Animation.
|
||
|
curve: null,
|
||
|
|
||
|
// easing: Function
|
||
|
// A Function to adjust the acceleration (or deceleration) of the progress
|
||
|
// across a dojo._Line
|
||
|
easing: null,
|
||
|
=====*/
|
||
|
|
||
|
// repeat: Integer
|
||
|
// The number of times to loop the animation
|
||
|
repeat: 0,
|
||
|
|
||
|
// rate: Integer
|
||
|
// the time in milliseconds to wait before advancing to next frame
|
||
|
// (used as a fps timer: rate/1000 = fps)
|
||
|
rate: 10 /* 100 fps */,
|
||
|
|
||
|
/*=====
|
||
|
// delay: Integer
|
||
|
// The time in milliseconds to wait before starting animation after it has been .play()'ed
|
||
|
delay: null,
|
||
|
|
||
|
// events
|
||
|
//
|
||
|
// beforeBegin: Event
|
||
|
// Synthetic event fired before a dojo._Animation begins playing (synchronous)
|
||
|
beforeBegin: null,
|
||
|
|
||
|
// onBegin: Event
|
||
|
// Synthetic event fired as a dojo._Animation begins playing (useful?)
|
||
|
onBegin: null,
|
||
|
|
||
|
// onAnimate: Event
|
||
|
// Synthetic event fired at each interval of a dojo._Animation
|
||
|
onAnimate: null,
|
||
|
|
||
|
// onEnd: Event
|
||
|
// Synthetic event fired after the final frame of a dojo._Animation
|
||
|
onEnd: null,
|
||
|
|
||
|
// onPlay: Event
|
||
|
// Synthetic event fired any time a dojo._Animation is play()'ed
|
||
|
onPlay: null,
|
||
|
|
||
|
// onPause: Event
|
||
|
// Synthetic event fired when a dojo._Animation is paused
|
||
|
onPause: null,
|
||
|
|
||
|
// onStop: Event
|
||
|
// Synthetic event fires when a dojo._Animation is stopped
|
||
|
onStop: null,
|
||
|
|
||
|
=====*/
|
||
|
|
||
|
_percent: 0,
|
||
|
_startRepeatCount: 0,
|
||
|
|
||
|
_fire: function(/*Event*/ evt, /*Array?*/ args){
|
||
|
// summary:
|
||
|
// Convenience function. Fire event "evt" and pass it the
|
||
|
// arguments specified in "args".
|
||
|
// evt:
|
||
|
// The event to fire.
|
||
|
// args:
|
||
|
// The arguments to pass to the event.
|
||
|
try{
|
||
|
if(this[evt]){
|
||
|
this[evt].apply(this, args||[]);
|
||
|
}
|
||
|
}catch(e){
|
||
|
// squelch and log because we shouldn't allow exceptions in
|
||
|
// synthetic event handlers to cause the internal timer to run
|
||
|
// amuck, potentially pegging the CPU. I'm not a fan of this
|
||
|
// squelch, but hopefully logging will make it clear what's
|
||
|
// going on
|
||
|
console.error("exception in animation handler for:", evt);
|
||
|
console.error(e);
|
||
|
}
|
||
|
return this; // dojo._Animation
|
||
|
},
|
||
|
|
||
|
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
|
||
|
// summary:
|
||
|
// Start the animation.
|
||
|
// delay:
|
||
|
// How many milliseconds to delay before starting.
|
||
|
// gotoStart:
|
||
|
// If true, starts the animation from the beginning; otherwise,
|
||
|
// starts it from its current position.
|
||
|
var _t = this;
|
||
|
if(gotoStart){
|
||
|
_t._stopTimer();
|
||
|
_t._active = _t._paused = false;
|
||
|
_t._percent = 0;
|
||
|
}else if(_t._active && !_t._paused){
|
||
|
return _t; // dojo._Animation
|
||
|
}
|
||
|
|
||
|
_t._fire("beforeBegin");
|
||
|
|
||
|
var de = delay||_t.delay;
|
||
|
var _p = dojo.hitch(_t, "_play", gotoStart);
|
||
|
if(de > 0){
|
||
|
setTimeout(_p, de);
|
||
|
return _t; // dojo._Animation
|
||
|
}
|
||
|
_p();
|
||
|
return _t;
|
||
|
},
|
||
|
|
||
|
_play: function(gotoStart){
|
||
|
var _t = this;
|
||
|
_t._startTime = new Date().valueOf();
|
||
|
if(_t._paused){
|
||
|
_t._startTime -= _t.duration * _t._percent;
|
||
|
}
|
||
|
_t._endTime = _t._startTime + _t.duration;
|
||
|
|
||
|
_t._active = true;
|
||
|
_t._paused = false;
|
||
|
|
||
|
var value = _t.curve.getValue(_t._percent);
|
||
|
if(!_t._percent){
|
||
|
if(!_t._startRepeatCount){
|
||
|
_t._startRepeatCount = _t.repeat;
|
||
|
}
|
||
|
_t._fire("onBegin", [value]);
|
||
|
}
|
||
|
|
||
|
_t._fire("onPlay", [value]);
|
||
|
|
||
|
_t._cycle();
|
||
|
return _t; // dojo._Animation
|
||
|
},
|
||
|
|
||
|
pause: function(){
|
||
|
// summary: Pauses a running animation.
|
||
|
this._stopTimer();
|
||
|
if(!this._active){ return this; /*dojo._Animation*/ }
|
||
|
this._paused = true;
|
||
|
this._fire("onPause", [this.curve.getValue(this._percent)]);
|
||
|
return this; // dojo._Animation
|
||
|
},
|
||
|
|
||
|
gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
|
||
|
// summary:
|
||
|
// Sets the progress of the animation.
|
||
|
// percent:
|
||
|
// A percentage in decimal notation (between and including 0.0 and 1.0).
|
||
|
// andPlay:
|
||
|
// If true, play the animation after setting the progress.
|
||
|
this._stopTimer();
|
||
|
this._active = this._paused = true;
|
||
|
this._percent = percent;
|
||
|
if(andPlay){ this.play(); }
|
||
|
return this; // dojo._Animation
|
||
|
},
|
||
|
|
||
|
stop: function(/*boolean?*/ gotoEnd){
|
||
|
// summary: Stops a running animation.
|
||
|
// gotoEnd: If true, the animation will end.
|
||
|
if(!this._timer){ return this; /* dojo._Animation */ }
|
||
|
this._stopTimer();
|
||
|
if(gotoEnd){
|
||
|
this._percent = 1;
|
||
|
}
|
||
|
this._fire("onStop", [this.curve.getValue(this._percent)]);
|
||
|
this._active = this._paused = false;
|
||
|
return this; // dojo._Animation
|
||
|
},
|
||
|
|
||
|
status: function(){
|
||
|
// summary: Returns a string token representation of the status of
|
||
|
// the animation, one of: "paused", "playing", "stopped"
|
||
|
if(this._active){
|
||
|
return this._paused ? "paused" : "playing"; // String
|
||
|
}
|
||
|
return "stopped"; // String
|
||
|
},
|
||
|
|
||
|
_cycle: function(){
|
||
|
var _t = this;
|
||
|
if(_t._active){
|
||
|
var curr = new Date().valueOf();
|
||
|
var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);
|
||
|
|
||
|
if(step >= 1){
|
||
|
step = 1;
|
||
|
}
|
||
|
_t._percent = step;
|
||
|
|
||
|
// Perform easing
|
||
|
if(_t.easing){
|
||
|
step = _t.easing(step);
|
||
|
}
|
||
|
|
||
|
_t._fire("onAnimate", [_t.curve.getValue(step)]);
|
||
|
|
||
|
if(_t._percent < 1){
|
||
|
_t._startTimer();
|
||
|
}else{
|
||
|
_t._active = false;
|
||
|
|
||
|
if(_t.repeat > 0){
|
||
|
_t.repeat--;
|
||
|
_t.play(null, true);
|
||
|
}else if(_t.repeat == -1){
|
||
|
_t.play(null, true);
|
||
|
}else{
|
||
|
if(_t._startRepeatCount){
|
||
|
_t.repeat = _t._startRepeatCount;
|
||
|
_t._startRepeatCount = 0;
|
||
|
}
|
||
|
}
|
||
|
_t._percent = 0;
|
||
|
_t._fire("onEnd");
|
||
|
_t._stopTimer();
|
||
|
}
|
||
|
}
|
||
|
return _t; // dojo._Animation
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var ctr = 0;
|
||
|
var _globalTimerList = [];
|
||
|
var runner = {
|
||
|
run: function(){ }
|
||
|
};
|
||
|
var timer = null;
|
||
|
dojo._Animation.prototype._startTimer = function(){
|
||
|
// this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);
|
||
|
if(!this._timer){
|
||
|
this._timer = d.connect(runner, "run", this, "_cycle");
|
||
|
ctr++;
|
||
|
}
|
||
|
if(!timer){
|
||
|
timer = setInterval(d.hitch(runner, "run"), this.rate);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
dojo._Animation.prototype._stopTimer = function(){
|
||
|
if(this._timer){
|
||
|
d.disconnect(this._timer);
|
||
|
this._timer = null;
|
||
|
ctr--;
|
||
|
}
|
||
|
if(ctr <= 0){
|
||
|
clearInterval(timer);
|
||
|
timer = null;
|
||
|
ctr = 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var _makeFadeable = (d.isIE) ? function(node){
|
||
|
// only set the zoom if the "tickle" value would be the same as the
|
||
|
// default
|
||
|
var ns = node.style;
|
||
|
if(!ns.zoom.length && d.style(node, "zoom") == "normal"){
|
||
|
// make sure the node "hasLayout"
|
||
|
// NOTE: this has been tested with larger and smaller user-set text
|
||
|
// sizes and works fine
|
||
|
ns.zoom = "1";
|
||
|
// node.style.zoom = "normal";
|
||
|
}
|
||
|
// don't set the width to auto if it didn't already cascade that way.
|
||
|
// We don't want to f anyones designs
|
||
|
if(!ns.width.length && d.style(node, "width") == "auto"){
|
||
|
ns.width = "auto";
|
||
|
}
|
||
|
} : function(){};
|
||
|
|
||
|
dojo._fade = function(/*Object*/ args){
|
||
|
// summary:
|
||
|
// Returns an animation that will fade the node defined by
|
||
|
// args.node from the start to end values passed (args.start
|
||
|
// args.end) (end is mandatory, start is optional)
|
||
|
|
||
|
args.node = d.byId(args.node);
|
||
|
var fArgs = d.mixin({ properties: {} }, args);
|
||
|
var props = (fArgs.properties.opacity = {});
|
||
|
props.start = !("start" in fArgs) ?
|
||
|
function(){
|
||
|
return Number(d.style(fArgs.node, "opacity"));
|
||
|
} : fArgs.start;
|
||
|
props.end = fArgs.end;
|
||
|
|
||
|
var anim = d.animateProperty(fArgs);
|
||
|
d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));
|
||
|
|
||
|
return anim; // dojo._Animation
|
||
|
}
|
||
|
|
||
|
/*=====
|
||
|
dojo.__FadeArgs = function(node, duration, easing){
|
||
|
// node: DOMNode|String
|
||
|
// The node referenced in the animation
|
||
|
// duration: Integer?
|
||
|
// Duration of the animation in milliseconds.
|
||
|
// easing: Function?
|
||
|
// An easing function.
|
||
|
this.node = node;
|
||
|
this.duration = duration;
|
||
|
this.easing = easing;
|
||
|
}
|
||
|
=====*/
|
||
|
|
||
|
dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){
|
||
|
// summary:
|
||
|
// Returns an animation that will fade node defined in 'args' from
|
||
|
// its current opacity to fully opaque.
|
||
|
return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation
|
||
|
}
|
||
|
|
||
|
dojo.fadeOut = function(/*dojo.__FadeArgs*/ args){
|
||
|
// summary:
|
||
|
// Returns an animation that will fade node defined in 'args'
|
||
|
// from its current opacity to fully transparent.
|
||
|
return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation
|
||
|
}
|
||
|
|
||
|
dojo._defaultEasing = function(/*Decimal?*/ n){
|
||
|
// summary: The default easing function for dojo._Animation(s)
|
||
|
return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);
|
||
|
}
|
||
|
|
||
|
var PropLine = function(properties){
|
||
|
// PropLine is an internal class which is used to model the values of
|
||
|
// an a group of CSS properties across an animation lifecycle. In
|
||
|
// particular, the "getValue" function handles getting interpolated
|
||
|
// values between start and end for a particular CSS value.
|
||
|
this._properties = properties;
|
||
|
for(var p in properties){
|
||
|
var prop = properties[p];
|
||
|
if(prop.start instanceof d.Color){
|
||
|
// create a reusable temp color object to keep intermediate results
|
||
|
prop.tempColor = new d.Color();
|
||
|
}
|
||
|
}
|
||
|
this.getValue = function(r){
|
||
|
var ret = {};
|
||
|
for(var p in this._properties){
|
||
|
var prop = this._properties[p];
|
||
|
var start = prop.start;
|
||
|
if(start instanceof d.Color){
|
||
|
ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();
|
||
|
}else if(!d.isArray(start)){
|
||
|
ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*=====
|
||
|
dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], {
|
||
|
// Properties: Object?
|
||
|
// A hash map of style properties to Objects describing the transition,
|
||
|
// such as the properties of dojo._Line with an additional 'unit' property
|
||
|
properties: {}
|
||
|
|
||
|
//TODOC: add event callbacks
|
||
|
});
|
||
|
=====*/
|
||
|
|
||
|
dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){
|
||
|
// summary:
|
||
|
// Returns an animation that will transition the properties of
|
||
|
// node defined in 'args' depending how they are defined in
|
||
|
// 'args.properties'
|
||
|
//
|
||
|
// description:
|
||
|
// dojo.animateProperty is the foundation of most dojo.fx
|
||
|
// animations. It takes an object of "properties" corresponding to
|
||
|
// style properties, and animates them in parallel over a set
|
||
|
// duration.
|
||
|
//
|
||
|
// example:
|
||
|
// A simple animation that changes the width of the specified node.
|
||
|
// | dojo.animateProperty({
|
||
|
// | node: "nodeId",
|
||
|
// | properties: { width: 400 },
|
||
|
// | }).play();
|
||
|
// Dojo figures out the start value for the width and converts the
|
||
|
// integer specified for the width to the more expressive but
|
||
|
// verbose form `{ width: { end: '400', units: 'px' } }` which you
|
||
|
// can also specify directly
|
||
|
// example:
|
||
|
// animate width, height, and padding over 2 seconds...the
|
||
|
// pedantic way:
|
||
|
// | dojo.animateProperty({ node: node, duration:2000,
|
||
|
// | properties: {
|
||
|
// | width: { start: '200', end: '400', unit:"px" },
|
||
|
// | height: { start:'200', end: '400', unit:"px" },
|
||
|
// | paddingTop: { start:'5', end:'50', unit:"px" }
|
||
|
// | }
|
||
|
// | }).play();
|
||
|
//
|
||
|
// example:
|
||
|
// plug in a different easing function and register a callback for
|
||
|
// when the animation ends. Easing functions accept values between
|
||
|
// zero and one and return a value on that basis. In this case, an
|
||
|
// exponential-in curve.
|
||
|
// | dojo.animateProperty({
|
||
|
// | node: "nodeId",
|
||
|
// | // dojo figures out the start value
|
||
|
// | properties: { width: { end: 400 } },
|
||
|
// | easing: function(n){
|
||
|
// | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
|
||
|
// | },
|
||
|
// | onEnd: function(){
|
||
|
// | // called when the animation finishes
|
||
|
// | }
|
||
|
// | }).play(500); // delay playing half a second
|
||
|
|
||
|
args.node = d.byId(args.node);
|
||
|
if(!args.easing){ args.easing = d._defaultEasing; }
|
||
|
|
||
|
var anim = new d._Animation(args);
|
||
|
d.connect(anim, "beforeBegin", anim, function(){
|
||
|
var pm = {};
|
||
|
for(var p in this.properties){
|
||
|
// Make shallow copy of properties into pm because we overwrite
|
||
|
// some values below. In particular if start/end are functions
|
||
|
// we don't want to overwrite them or the functions won't be
|
||
|
// called if the animation is reused.
|
||
|
if(p == "width" || p == "height"){
|
||
|
this.node.display = "block";
|
||
|
}
|
||
|
var prop = this.properties[p];
|
||
|
prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop }));
|
||
|
|
||
|
if(d.isFunction(prop.start)){
|
||
|
prop.start = prop.start();
|
||
|
}
|
||
|
if(d.isFunction(prop.end)){
|
||
|
prop.end = prop.end();
|
||
|
}
|
||
|
var isColor = (p.toLowerCase().indexOf("color") >= 0);
|
||
|
function getStyle(node, p){
|
||
|
// dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:
|
||
|
var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];
|
||
|
if(v !== undefined){ return v; }
|
||
|
v = d.style(node, p);
|
||
|
return (p=="opacity") ? Number(v) : (isColor ? v : parseFloat(v));
|
||
|
}
|
||
|
if(!("end" in prop)){
|
||
|
prop.end = getStyle(this.node, p);
|
||
|
}else if(!("start" in prop)){
|
||
|
prop.start = getStyle(this.node, p);
|
||
|
}
|
||
|
|
||
|
if(isColor){
|
||
|
prop.start = new d.Color(prop.start);
|
||
|
prop.end = new d.Color(prop.end);
|
||
|
}else{
|
||
|
prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);
|
||
|
}
|
||
|
}
|
||
|
this.curve = new PropLine(pm);
|
||
|
});
|
||
|
d.connect(anim, "onAnimate", anim, function(propValues){
|
||
|
// try{
|
||
|
for(var s in propValues){
|
||
|
d.style(this.node, s, propValues[s]);
|
||
|
// this.node.style[s] = propValues[s];
|
||
|
}
|
||
|
});
|
||
|
return anim; // dojo._Animation
|
||
|
}
|
||
|
|
||
|
dojo.anim = function( /*DOMNode|String*/ node,
|
||
|
/*Object*/ properties,
|
||
|
/*Integer?*/ duration,
|
||
|
/*Function?*/ easing,
|
||
|
/*Function?*/ onEnd,
|
||
|
/*Integer?*/ delay){
|
||
|
// summary:
|
||
|
// A simpler interface to `dojo.animateProperty()`, also returns
|
||
|
// an instance of `dojo._Animation` but begins the animation
|
||
|
// immediately, unlike nearly every other Dojo animation API.
|
||
|
// description:
|
||
|
// `dojo.anim` is a simpler (but somewhat less powerful) version
|
||
|
// of `dojo.animateProperty`. It uses defaults for many basic properties
|
||
|
// and allows for positional parameters to be used in place of the
|
||
|
// packed "property bag" which is used for other Dojo animation
|
||
|
// methods.
|
||
|
//
|
||
|
// The `dojo._Animation` object returned from `dojo.anim` will be
|
||
|
// already playing when it is returned from this function, so
|
||
|
// calling play() on it again is (usually) a no-op.
|
||
|
// node:
|
||
|
// a DOM node or the id of a node to animate CSS properties on
|
||
|
// duration:
|
||
|
// The number of milliseconds over which the animation
|
||
|
// should run. Defaults to the global animation default duration
|
||
|
// (350ms).
|
||
|
// easing:
|
||
|
// An easing function over which to calculate acceleration
|
||
|
// and deceleration of the animation through its duration.
|
||
|
// A default easing algorithm is provided, but you may
|
||
|
// plug in any you wish. A large selection of easing algorithms
|
||
|
// are available in `dojox.fx.easing`.
|
||
|
// onEnd:
|
||
|
// A function to be called when the animation finishes
|
||
|
// running.
|
||
|
// delay:
|
||
|
// The number of milliseconds to delay beginning the
|
||
|
// animation by. The default is 0.
|
||
|
// example:
|
||
|
// Fade out a node
|
||
|
// | dojo.anim("id", { opacity: 0 });
|
||
|
// example:
|
||
|
// Fade out a node over a full second
|
||
|
// | dojo.anim("id", { opacity: 0 }, 1000);
|
||
|
return d.animateProperty({
|
||
|
node: node,
|
||
|
duration: duration||d._Animation.prototype.duration,
|
||
|
properties: properties,
|
||
|
easing: easing,
|
||
|
onEnd: onEnd
|
||
|
}).play(delay||0);
|
||
|
}
|
||
|
})();
|
||
|
|
||
|
}
|