Ext.applyIf(Ext.core.Element.prototype, {
animate: function(args, duration, onComplete, easing) {
if (arguments.length > 1) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("animate should only be called with a single configuration object.");
}
}
var me = this;
if (!me.id) {
me = Ext.get(me.dom);
}
if (me.hasFxBlock()) {
return me;
}
args = args || {};
if (duration) {
args.duration = duration;
}
if (onComplete) {
args.callback = onComplete;
}
if (easing) {
args.easing = easing;
}
me.queueFx(Ext.create('Ext.fx.Anim', me.anim(args)));
return this;
},
// @private - process the passed fx configuration
anim: function(config) {
if (arguments.length > 1) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("anim now only accepts one configuration object.");
}
}
if (!Ext.isObject(config)) {
return (config) ? {} : false;
}
var me = this,
duration = config.duration || 250,
easing = config.easing || 'ease',
animConfig;
if (config.stopFx) {
me.stopFx();
}
me.fxDefaults = me.fxDefaults || {};
Ext.applyIf(config, me.fxDefaults);
// Clear any 'paused' defaults.
delete me.fxDefaults.delay;
// Convert durations in seconds to milliseconds
if (config.delay && config.delay < 10) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Detected an extremely small animation delay, assuming the deprecated unit 'seconds were used. Please change to milliseconds.");
}
config.delay *= 1000;
}
// Convert durations in seconds to milliseconds
if (duration < 10) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Detected an extremely small animation duration, assuming the deprecated unit seconds were used. Please change to milliseconds.");
}
duration *= 1000;
}
// Convert endOpacity to opacity
if (config.endOpacity) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Detected the deprecated endOpacity property. Please use opacity.");
}
config.opacity = config.endOpacity;
delete config.endOpacity;
}
animConfig = {
target: me,
remove: config.remove,
duration: duration,
easing: easing,
callback: config.callback,
listeners: config.listeners,
iterations: config.iterations || 1,
scope: config.scope,
block: config.block,
concurrent: config.concurrent,
delay: config.delay || 0,
paused: true,
keyframes: config.keyframes,
from: config.from || {},
to: Ext.apply({}, config)
};
Ext.apply(animConfig.to, config.to);
// TODO CLEAN ME!
delete animConfig.to.to;
delete animConfig.to.from;
delete animConfig.to.remove;
delete animConfig.to.keyframes;
delete animConfig.to.iterations;
delete animConfig.to.listeners;
delete animConfig.to.target;
delete animConfig.to.paused;
delete animConfig.to.callback;
delete animConfig.to.scope;
delete animConfig.to.duration;
delete animConfig.to.easing;
delete animConfig.to.concurrent;
delete animConfig.to.block;
delete animConfig.to.stopFx;
delete animConfig.to.delay;
return animConfig;
},
/**
* Ensures that all effects queued after syncFx is called on the element are
* run concurrently. This is the opposite of {@link #sequenceFx}.
* @return {Ext.Element} The Element
*/
syncFx: function() {
var me = this;
me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
concurrent: true
});
return me;
},
/**
* Ensures that all effects queued after sequenceFx is called on the element are
* run in sequence. This is the opposite of {@link #syncFx}.
* @return {Ext.Element} The Element
*/
sequenceFx: function() {
var me = this;
me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
concurrent: false
});
return me;
},
/**
* Stops any running effects and clears the element's internal effects queue if it contains
* any additional effects that haven't started yet.
* @return {Ext.Element} The Element
*/
stopFx: function() {
var me = this,
queue = Ext.cache[me.id].fxStack,
ln = queue.length;
while (ln) {
queue[ln - 1].end();
ln--;
}
},
/**
* Returns true if the element has any effects actively running or queued, else returns false.
* @return {Boolean} True if element has active effects, else false
*/
hasActiveFx: function() {
var queue = Ext.cache[this.id].fxStack;
return queue && !!queue.length;
},
// @private
hasFxBlock: function() {
var queue = Ext.cache[this.id].fxStack;
return queue && queue[0] && queue[0].block;
},
// @private
queueFx: function(anim) {
var queue = Ext.cache[this.id].fxStack,
ln = queue.length;
if (ln) {
if (anim.concurrent) {
anim.paused = false;
}
else {
queue[ln - 1].on('afteranimate', function() {
anim.paused = false;
});
}
}
else {
anim.paused = false;
}
anim.on('afteranimate', function() {
Ext.Array.remove(queue, anim);
if (anim.remove) {
this.remove();
}
}, this);
queue.push(anim);
},
/**
* Slides the element into view. An anchor point can be optionally passed to set the point of
* origin for the slide effect. This function automatically handles wrapping the element with
* a fixed-size container if needed. See the Fx class overview for valid anchor point options.
* Usage:
*
// default: slide the element in from the top
el.slideIn();
// custom: slide the element in from the right with a 2-second duration
el.slideIn('r', { duration: 2 });
// common config options shown with default values
el.slideIn('t', {
easing: 'easeOut',
duration: .5
});
* @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
slideIn: function(anchor, obj, slideOut) {
var me = this;
anchor = anchor || "t";
obj = obj || {};
beforeAnim = function() {
var animScope = this,
box, position, r, wrap, wrapAnim, anim;
if (!slideOut) {
me.fixDisplay();
}
box = me.getBox();
position = me.getPositioning();
r = getRestoreSize();
me.setSize(box.width, box.height);
wrap = me.wrap({
style: {
visibility: slideOut ? 'visible' : 'hidden'
}
});
wrap.setPositioning(position);
if (wrap.isStyle('position', 'static')) {
wrap.position('relative');
}
me.clearPositioning('auto');
wrap.clip();
me.setStyle({
visibility: 'visible',
position: 'absolute'
});
if (slideOut) {
wrap.setSize(box.width, box.height);
}
switch (anchor) {
case 't':
anim = {
from: {
width: box.width,
height: 0
},
to: {
width: box.width,
height: box.height
}
};
break;
case 'l':
anim = {
from: {
width: 0,
height: box.height
},
to: {
width: box.width,
height: box.height
}
};
break;
case 'r':
anim = {
from: {
x: box.x + box.width,
width: 0,
height: box.height
},
to: {
x: box.x,
width: box.width,
height: box.height
}
};
break;
case 'b':
anim = {
from: {
y: box.y + box.height,
width: box.width,
height: 0
},
to: {
y: box.y,
width: box.width,
height: box.height
}
};
break;
case 'tl':
anim = {
from: {
x: box.x,
y: box.y,
width: 0,
height: 0
},
to: {
width: box.width,
height: box.height
}
};
break;
case 'bl':
anim = {
from: {
x: box.x + box.width,
width: 0,
height: 0
},
to: {
x: box.x,
width: box.width,
height: box.height
}
};
break;
case 'br':
anim = {
from: {
x: box.x + box.width,
y: box.y + box.height,
width: 0,
height: 0
},
to: {
x: box.x,
y: box.y,
width: box.width,
height: box.height
}
};
break;
case 'tr':
anim = {
from: {
y: box.y + box.height,
width: 0,
height: 0
},
to: {
y: box.y,
width: box.width,
height: box.height
}
};
break;
};
wrap.show();
wrapAnim = Ext.create('Ext.fx.Anim', {
target: wrap,
duration: obj.duration || 500,
easing: obj.easing || 'ease-out',
from: slideOut ? anim.to : anim.from,
to: slideOut ? anim.from : anim.to
});
wrapAnim.on('afteranimate', function() {
if (slideOut) {
me.setPositioning(position);
obj.useDisplay ? me.setDisplayed(false) : me.hide();
}
else {
me.clearPositioning();
me.setPositioning(position);
}
if (wrap.dom) {
var pn = wrap.dom.parentNode;
pn.insertBefore(me.dom, wrap.dom);
wrap.remove();
}
me.setSize(box.width, box.height);
animScope.end();
});
};
getRestoreSize = function(){
var st = me.dom.style;
return {width: st.width, height : st.height};
};
me.animate({
duration: (obj.duration * 2) || 1000,
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
});
return me;
},
/**
* Slides the element out of view. An anchor point can be optionally passed to set the end point
* for the slide effect. When the effect is completed, the element will be hidden (visibility =
* 'hidden') but block elements will still take up space in the document. The element must be removed
* from the DOM using the 'remove' config option if desired. This function automatically handles
* wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
* Usage:
*
// default: slide the element out to the top
el.slideOut();
// custom: slide the element out to the right with a 2-second duration
el.slideOut('r', { duration: 2 });
// common config options shown with default values
el.slideOut('t', {
easing: 'easeOut',
duration: .5,
remove: false,
useDisplay: false
});
* @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
slideOut: function(anchor, o) {
return this.slideIn(anchor, o, true);
},
/**
* Fades the element out while slowly expanding it in all directions. When the effect is completed, the
* element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
* Usage:
*
// default
el.puff();
// common config options shown with default values
el.puff({
easing: 'easeOut',
duration: .5,
useDisplay: false
});
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
puff: function(obj) {
var me = this;
obj = Ext.applyIf(obj || {}, {
easing: 'ease-out',
duration: 500,
useDisplay: false
});
beforeAnim = function() {
me.clearOpacity();
me.show();
var box = me.getBox(),
fontSize = me.getStyle('fontSize');
position = me.getPositioning();
this.to = {
width: box.width * 2,
height: box.height * 2,
x: box.x - (box.width / 2),
y: box.y - (box.height /2),
opacity: 0,
fontSize: '200%'
};
this.on('afteranimate',function() {
if (me.dom) {
obj.useDisplay ? me.setDisplayed(false) : me.hide();
me.clearOpacity();
me.setPositioning(position);
me.setStyle({fontSize: fontSize});
}
});
};
me.animate({
duration: obj.duration,
easing: obj.easing,
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
});
return me;
},
/**
* Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
* When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
* take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
* Usage:
*
// default
el.switchOff();
// all config options shown with default values
el.switchOff({
easing: 'easeIn',
duration: .3,
remove: false,
useDisplay: false
});
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
switchOff: function(obj) {
var me = this;
obj = Ext.applyIf(obj || {}, {
easing: 'ease-in',
duration: 500,
remove: false,
useDisplay: false
});
beforeAnim = function() {
var animScope = this,
size = me.getSize(),
xy = me.getXY(),
keyframe;
me.clearOpacity();
me.clip();
position = me.getPositioning();
keyframe = Ext.create('Ext.fx.Animator', {
target: me,
duration: obj.duration,
easing: obj.easing,
keyframes: {
33: {
opacity: 0.3
},
66: {
height: 1,
y: xy[1] + size.height / 2
},
100: {
width: 1,
x: xy[0] + size.width / 2
}
}
});
keyframe.on('afteranimate', function() {
obj.useDisplay ? me.setDisplayed(false) : me.hide();
me.clearOpacity();
me.setPositioning(position);
me.setSize(size);
animScope.end();
});
};
me.animate({
duration: (obj.duration * 2),
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
});
return me;
},
/**
* Shows a ripple of exploding, attenuating borders to draw attention to an Element.
* Usage:
// default: a single light blue ripple
el.frame();
// custom: 3 red ripples lasting 3 seconds total
el.frame("#ff0000", 3, { duration: 3 });
// common config options shown with default values
el.frame("#C3DAF9", 1, {
duration: 1 //duration of each individual ripple.
// Note: Easing is not configurable and will be ignored if included
});
* @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
* @param {Number} count (optional) The number of ripples to display (defaults to 1)
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
frame : function(color, count, obj){
var me = this;
color = color || '#C3DAF9';
count = count || 1;
obj = obj || {};
beforeAnim = function() {
me.show();
var animScope = this,
box = me.getBox(),
proxy = Ext.getBody().createChild({
style: {
position : 'absolute',
'pointer-events': 'none',
'z-index': 35000,
border : '0px solid ' + color
}
}),
proxyAnim;
proxyAnim = Ext.create('Ext.fx.Anim', {
target: proxy,
duration: obj.duration || 1000,
iterations: count,
from: {
top: box.y,
left: box.x,
borderWidth: 0,
opacity: 1,
height: box.height,
width: box.width
},
to: {
top: box.y - 20,
left: box.x - 20,
borderWidth: 10,
opacity: 0,
height: box.height + 40,
width: box.width + 40
}
});
proxyAnim.on('afteranimate', function() {
proxy.remove();
animScope.end();
});
};
me.animate({
duration: (obj.duration * 2) || 2000,
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
});
return me;
},
/**
* Slides the element while fading it out of view. An anchor point can be optionally passed to set the
* ending point of the effect.
* Usage:
*
// default: slide the element downward while fading out
el.ghost();
// custom: slide the element out to the right with a 2-second duration
el.ghost('r', { duration: 2 });
// common config options shown with default values
el.ghost('b', {
easing: 'easeOut',
duration: .5
});
* @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
ghost: function(anchor, obj) {
var me = this;
anchor = anchor || "b";
beforeAnim = function() {
var width = me.getWidth(),
height = me.getHeight(),
xy = me.getXY(),
position = me.getPositioning(),
to = {
opacity: 0
};
switch (anchor) {
case 't':
to.y = xy[1] - height;
break;
case 'l':
to.x = xy[0] - width;
break;
case 'r':
to.x = xy[0] + width;
break;
case 'b':
to.y = xy[1] + height;
break;
case 'tl':
to.x = xy[0] - width;
to.y = xy[1] - height;
break;
case 'bl':
to.x = xy[0] - width;
to.y = xy[1] + height;
break;
case 'br':
to.x = xy[0] + width;
to.y = xy[1] + height;
break;
case 'tr':
to.x = xy[0] + width;
to.y = xy[1] - height;
break;
};
this.to = to;
this.on('afteranimate', function () {
if (me.dom) {
me.hide();
me.clearOpacity();
me.setPositioning(position);
}
});
};
me.animate(Ext.applyIf(obj || {}, {
duration: 500,
easing: 'ease-out',
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
}));
return me;
},
/**
* Highlights the Element by setting a color (applies to the background-color by default, but can be
* changed using the "attr" config option) and then fading back to the original color. If no original
* color is available, you should provide the "endColor" config option which will be cleared after the animation.
* Usage:
// default: highlight background to yellow
el.highlight();
// custom: highlight foreground text to blue for 2 seconds
el.highlight("0000ff", { attr: 'color', duration: 2 });
// common config options shown with default values
el.highlight("ffff9c", {
attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
endColor: (current color) or "ffffff",
easing: 'easeIn',
duration: 1
});
* @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
highlight: function(color, o) {
var me = this,
dom = me.dom,
from = {},
restore, to, attr;
o = o || {};
attr = o.attr || "backgroundColor";
if (!o.to) {
to = {};
to[attr] = o.endColor || "#ffff9c";
}
else {
to = o.to;
}
me.animate(Ext.apply({}, o, {
duration: 1000,
easing: 'ease-in',
to: to,
listeners: {
beforeanimate: {
fn: function() {
restore = dom.style[attr];
me.clearOpacity();
me.show();
}
},
afteranimate: {
fn: function() {
if (dom) {
dom.style[attr] = restore;
}
}
}
}
}));
return me;
},
/**
* Creates a pause before any subsequent queued effects begin. If there are
* no effects queued after the pause it will have no effect.
* Usage:
el.pause(1);
* @param {Number} ms The length of time to pause (in milliseconds)
* @return {Ext.Element} The Element
*/
pause: function(ms) {
var me = this;
if (Ext.isDefined(Ext.global.console)) {
console.warn("Operation: Element.pause has been deprecated. Please use animate with a delay parameter.");
}
me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
delay: ms
});
return me;
},
/**
* Fade an element in (from transparent to opaque). The ending opacity can be specified
* using the {@link #endOpacity} config option.
* Usage:
// default: fade in from opacity 0 to 100%
el.fadeIn();
// custom: fade in from opacity 0 to 75% over 2 seconds
el.fadeIn({ endOpacity: .75, duration: 2});
// common config options shown with default values
el.fadeIn({
endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
easing: 'easeOut',
duration: .5
});
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
fadeIn: function(o) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Operation: Element.fadeIn has been deprecated. Please use animate with an opacity parameter.");
}
this.animate(Ext.apply({}, o, {
opacity: 1
}));
return this;
},
/**
* Fade an element out (from opaque to transparent). The ending opacity can be specified
* using the {@link #endOpacity} config option. Note that IE may require
* {@link #useDisplay}:true in order to redisplay correctly.
* Usage:
// default: fade out from the element's current opacity to 0
el.fadeOut();
// custom: fade out from the element's current opacity to 25% over 2 seconds
el.fadeOut({ endOpacity: .25, duration: 2});
// common config options shown with default values
el.fadeOut({
endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
easing: 'easeOut',
duration: .5,
remove: false,
useDisplay: false
});
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
fadeOut: function(o) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Operation: Element.fadeOut has been deprecated. Please use animate with an opacity parameter.");
}
this.animate(Ext.apply({}, o, {
opacity: 0
}));
return this;
},
/**
* Animates the transition of an element's dimensions from a starting height/width
* to an ending height/width. This method is a convenience implementation of {@link shift}.
* Usage:
// change height and width to 100x100 pixels
el.scale(100, 100);
// common config options shown with default values. The height and width will default to
// the element's existing values if passed as null.
el.scale(
[element's width],
[element's height], {
easing: 'easeOut',
duration: .35
}
);
* @param {Number} width The new width (pass undefined to keep the original width)
* @param {Number} height The new height (pass undefined to keep the original height)
* @param {Object} options (optional) Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
scale: function(w, h, o) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Operation: Element.scale has been deprecated. Please use animate.");
}
this.animate(Ext.apply({}, o, {
width: w,
height: h
}));
return this;
},
/**
* Animates the transition of any css property.
* Usage:
// slide the element horizontally to x position 200 while changing the height and opacity
el.shift({ x: 200, height: 50, opacity: .8 });
// common config options shown with default values.
el.shift({
width: [element's width],
height: [element's height],
x: [element's x position],
y: [element's y position],
opacity: [element's opacity],
easing: 'easeOut',
duration: .35
});
* @param {Object} options Object literal with any of the Fx config options
* @return {Ext.Element} The Element
*/
shift: function(config) {
if (Ext.isDefined(Ext.global.console)) {
console.warn("Operation: Element.shift has been deprecated. Please use animate.");
}
this.animate(config);
return this;
}
});