mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
CPAnimatablePropertyContainer protocol (CPView) CPAnimationContext _CPObjectAnimator CPViewAnimator CSS Animations wrapper CPAnimatablePropertyContainerTest : animations of views with custom Layout / Regular Layout / Custom DrawRect: / No DrawRect: / Subviews / No Subviews and combinations of these situations. controls animation (text field, button, popup, slider ...). They animate their size via their action.
286 lines
8.9 KiB
Plaintext
286 lines
8.9 KiB
Plaintext
|
|
var ANIMATIONS_GLOBAL_ID = 0,
|
|
CURRENT_ANIMATIONS = {},
|
|
|
|
ANIMATION_END_EVENT_NAME,
|
|
ANIMATION__PROPERTY,
|
|
ANIMATION_NAME_PROPERTY,
|
|
ANIMATION_DURATION_PROPERTY,
|
|
ANIMATION_TIMING_FUNCTION_PROPERTY,
|
|
ANIMATION_FILL_MODE_PROPERTY,
|
|
ANIMATION_KEYFRAMES_RULE;
|
|
|
|
var defineCSSProperties = function()
|
|
{
|
|
if (this.done)
|
|
return;
|
|
|
|
ANIMATION_END_EVENT_NAME = CPBrowserStyleProperty("animationend"),
|
|
ANIMATION_PROPERTY = CPBrowserCSSProperty("animation"),
|
|
ANIMATION_NAME_PROPERTY = CPBrowserCSSProperty("animation-name"),
|
|
ANIMATION_DURATION_PROPERTY = CPBrowserCSSProperty("animation-duration"),
|
|
ANIMATION_TIMING_FUNCTION_PROPERTY = CPBrowserCSSProperty("animation-timing-function"),
|
|
ANIMATION_FILL_MODE_PROPERTY = CPBrowserCSSProperty("animation-fill-mode"),
|
|
ANIMATION_KEYFRAMES_RULE = "@" + ANIMATION_PROPERTY.substring(0, ANIMATION_PROPERTY.indexOf("animation")) + "keyframes";
|
|
|
|
this.done = true;
|
|
}
|
|
|
|
CSSAnimation = function(aTarget/*DOM Element*/, anIdentifier)
|
|
{
|
|
defineCSSProperties();
|
|
|
|
if (!anIdentifier)
|
|
anIdentifier = ANIMATIONS_GLOBAL_ID++;
|
|
|
|
var animationName = "anim_" + anIdentifier,
|
|
animation = CURRENT_ANIMATIONS[anIdentifier];
|
|
|
|
if (animation)
|
|
console.warn("Animation "+ anIdentifier + " is already in use. Ignoring.");
|
|
else
|
|
{
|
|
this.target = aTarget;
|
|
this.identifier = anIdentifier;
|
|
this.animationName = animationName;
|
|
this.listener = null;
|
|
this.styleElement = null;
|
|
this.propertyanimations = [];
|
|
this.animationsnames = [];
|
|
this.animationstimingfunctions = [];
|
|
this.animationsdurations = [];
|
|
this.islive = false;
|
|
this.didBuildDOMElements = false;
|
|
this.removeAnimationPropertyOnCompletion = true;
|
|
|
|
animation = this;
|
|
CURRENT_ANIMATIONS[anIdentifier] = animation;
|
|
}
|
|
|
|
return animation;
|
|
}
|
|
|
|
CSSAnimation.prototype.addPropertyAnimation = function(propertyName/*String*/, valueFunction/*Function*/, aDuration/*float*/, aKeyTimes/*d, [d]*/, aValues/*Array*/, aTimingFunctions/*[d,d,d,d],[[d,d,d,d]]*/, aCompletionfunction/*Function*/)
|
|
{
|
|
if (this.islive)
|
|
return false;
|
|
// TODO: If a property already exist, replace its values & valueFunctions.
|
|
|
|
var name = this.animationName + "_" + propertyName;
|
|
|
|
var animation = {name:name,
|
|
property:propertyName,
|
|
valuefunction:valueFunction,
|
|
keytimes:aKeyTimes,
|
|
values:aValues,
|
|
duration:aDuration,
|
|
completionfunction:aCompletionfunction};
|
|
|
|
var animationTimingFunction;
|
|
if (aTimingFunctions && (aTimingFunctions[0] instanceof Array))
|
|
{
|
|
animation.keyframestimingFunctions = aTimingFunctions;
|
|
// dummy timing function overriden by keyframes timings functions
|
|
animationTimingFunction = "linear";
|
|
}
|
|
else
|
|
animationTimingFunction = "cubic-bezier(" + aTimingFunctions + ")";
|
|
|
|
this.animationstimingfunctions.push(animationTimingFunction);
|
|
|
|
this.propertyanimations.push(animation);
|
|
this.animationsnames.push(name);
|
|
this.animationsdurations.push(aDuration + "s");
|
|
|
|
return true;
|
|
}
|
|
|
|
CSSAnimation.prototype.keyFrames = function()
|
|
{
|
|
var keyframesRules = [];
|
|
|
|
var count = this.propertyanimations.length;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var animation = this.propertyanimations[i],
|
|
property = animation.property,
|
|
valuefunction = animation.valuefunction,
|
|
keytimes = animation.keytimes,
|
|
values = animation.values,
|
|
timingFunctions = animation.keyframestimingFunctions;
|
|
|
|
var keyframes = [],
|
|
keytimescount = keytimes.length;
|
|
|
|
for (var j = 0; j < keytimescount; j++)
|
|
{
|
|
var keytime = keytimes[j],
|
|
value = values[j];
|
|
|
|
var cssvalue = valuefunction ? valuefunction(value) : value;
|
|
var keyframeContent = property + ": " + cssvalue + ";";
|
|
|
|
var timingFunction;
|
|
if (timingFunctions && timingFunctions.length && (timingFunction = timingFunctions[j]))
|
|
{
|
|
keyframeContent += ANIMATION_TIMING_FUNCTION_PROPERTY + ":cubic-bezier(" + timingFunction + ");";
|
|
}
|
|
|
|
var keyframe = "\t" + Math.round(keytime * 100) + "% {\n\t\t" + keyframeContent + "\n\t}\n";
|
|
keyframes.push(keyframe);
|
|
}
|
|
// TODO ! Add keyframe rule to CPCompatibility
|
|
var rule = ANIMATION_KEYFRAMES_RULE + " " + animation.name + " {\n" + keyframes.join(" ") + "}\n";
|
|
keyframesRules.push(rule);
|
|
}
|
|
|
|
return keyframesRules.join("\n");
|
|
}
|
|
|
|
CSSAnimation.prototype.appendKeyFramesRule = function()
|
|
{
|
|
var styleElement = this.createKeyFramesStyleElement(),
|
|
keyframesText = this.keyFrames(),
|
|
nodeText = document.createTextNode(keyframesText);
|
|
|
|
styleElement.appendChild(nodeText);
|
|
document.head.appendChild(styleElement);
|
|
}
|
|
|
|
CSSAnimation.prototype.createKeyFramesStyleElement = function()
|
|
{
|
|
if (!this.styleElement)
|
|
{
|
|
var styleElement = document.createElement("style");
|
|
styleElement.setAttribute("type", "text/css");
|
|
|
|
this.styleElement = styleElement;
|
|
}
|
|
|
|
return this.styleElement;
|
|
}
|
|
|
|
CSSAnimation.prototype.endEventListener = function()
|
|
{
|
|
var animation = this,
|
|
animationsNames = this.animationsnames,
|
|
inFlightAnimationsNames = animationsNames.slice();
|
|
|
|
if (!animation.listener)
|
|
{
|
|
var AnimationEndListener = function(event)
|
|
{
|
|
var idx = inFlightAnimationsNames.indexOf(event.animationName);
|
|
if (idx !== -1)
|
|
inFlightAnimationsNames.splice(idx, 1);
|
|
|
|
if (inFlightAnimationsNames.length == 0)
|
|
{
|
|
for (var i = 0; i < animationsNames.length; i++)
|
|
{
|
|
var completion = animation.completionFunctionForAnimationName(animationsNames[i]);
|
|
if (completion)
|
|
completion();
|
|
}
|
|
|
|
var eventTarget = event.target,
|
|
style = eventTarget.style;
|
|
|
|
if (animation.removeAnimationPropertyOnCompletion)
|
|
style.removeProperty(ANIMATION_NAME_PROPERTY);
|
|
|
|
style.removeProperty(ANIMATION_DURATION_PROPERTY);
|
|
style.removeProperty(ANIMATION_FILL_MODE_PROPERTY);
|
|
style.removeProperty("-webkit-backface-visibility");
|
|
|
|
if (animation.animationstimingfunctions.length)
|
|
style.removeProperty(ANIMATION_TIMING_FUNCTION_PROPERTY);
|
|
|
|
removeFromParent(animation.styleElement);
|
|
|
|
eventTarget.removeEventListener(ANIMATION_END_EVENT_NAME, AnimationEndListener);
|
|
animation.listener = null;
|
|
delete (CURRENT_ANIMATIONS[animation.identifier]);
|
|
}
|
|
};
|
|
|
|
this.listener = AnimationEndListener;
|
|
}
|
|
|
|
return this.listener;
|
|
}
|
|
|
|
CSSAnimation.prototype.completionFunctionForAnimationName = function(aName)
|
|
{
|
|
var propanims = this.propertyanimations,
|
|
count = propanims.length;
|
|
|
|
while (count--)
|
|
{
|
|
var anim = propanims[count];
|
|
if (anim.name == aName)
|
|
return anim.completionfunction;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
CSSAnimation.prototype.addAnimationEndEventListener = function()
|
|
{
|
|
var listener = this.endEventListener();
|
|
this.target.addEventListener(ANIMATION_END_EVENT_NAME, listener, false);
|
|
}
|
|
|
|
CSSAnimation.prototype.setTargetStyleProperties = function()
|
|
{
|
|
var style = this.target.style;
|
|
|
|
if (this.animationstimingfunctions.length)
|
|
style.setProperty(ANIMATION_TIMING_FUNCTION_PROPERTY, this.animationstimingfunctions.join(","));
|
|
|
|
style.setProperty(ANIMATION_DURATION_PROPERTY, this.animationsdurations.join(","));
|
|
|
|
style.setProperty(ANIMATION_FILL_MODE_PROPERTY, "forwards");
|
|
|
|
// http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/css3-animations-the-hiccups-and-bugs-youll-want-to-avoid/
|
|
style.setProperty("-webkit-backface-visibility", "hidden");
|
|
}
|
|
|
|
CSSAnimation.prototype.buildDOMElements = function()
|
|
{
|
|
this.appendKeyFramesRule();
|
|
|
|
this.addAnimationEndEventListener();
|
|
|
|
this.setTargetStyleProperties();
|
|
|
|
this.didBuildDOMElements = true;
|
|
}
|
|
|
|
CSSAnimation.prototype.setRemoveAnimationPropertyOnCompletion = function(flag)
|
|
{
|
|
this.removeAnimationPropertyOnCompletion = flag;
|
|
}
|
|
|
|
CSSAnimation.prototype.start = function()
|
|
{
|
|
if (this.propertyanimations.length == 0 || this.islive)
|
|
return false;
|
|
|
|
if (!this.didBuildDOMElements)
|
|
this.buildDOMElements();
|
|
|
|
this.target.style.setProperty(ANIMATION_NAME_PROPERTY, this.animationsnames.join(","));
|
|
|
|
this.islive = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
var removeFromParent= function(aNode)
|
|
{
|
|
var parentNode = aNode.parentNode;
|
|
if (parentNode)
|
|
parentNode.removeChild(aNode);
|
|
}
|