API  0.9.10
CSSAnimation.j
Go to the documentation of this file.
1 
4 
12 
13 var defineCSSProperties = function()
14 {
15  if (this.done)
16  return;
17 
21  ANIMATION_DURATION_PROPERTY = CPBrowserCSSProperty("animation-duration");
22  ANIMATION_TIMING_FUNCTION_PROPERTY = CPBrowserCSSProperty("animation-timing-function");
23  ANIMATION_FILL_MODE_PROPERTY = CPBrowserCSSProperty("animation-fill-mode");
24  ANIMATION_KEYFRAMES_RULE = "@" + ANIMATION_PROPERTY.substring(0, ANIMATION_PROPERTY.indexOf("animation")) + "keyframes";
25 
26  this.done = true;
27 };
28 
29 var removeFromParent = function(aNode)
30 {
31  var parentNode = aNode.parentNode;
32 
33  if (parentNode)
34  parentNode.removeChild(aNode);
35 };
36 
37 CSSAnimation = function(aTarget/* DOM Element */, anIdentifier, aTargetName)
38 {
40 
41  if (!anIdentifier)
42  anIdentifier = ANIMATIONS_GLOBAL_ID++;
43 
44  var animationName = "anim_" + anIdentifier,
45  animation = CURRENT_ANIMATIONS[anIdentifier];
46 
47  if (animation)
48  {
49  console.warn("Animation " + anIdentifier + " is already in use. Ignoring.");
50  }
51  else
52  {
53  this.target = aTarget;
54  this.identifier = anIdentifier;
55  this.targetName = aTargetName;
56  this.animationName = animationName;
57  this.listener = null;
58  this.styleElement = null;
59  this.propertyanimations = [];
60  this.animationsnames = [];
61  this.animationstimingfunctions = [];
62  this.animationsdurations = [];
63  this.islive = false;
64  this.didBuildDOMElements = false;
65 
66  animation = this;
67  CURRENT_ANIMATIONS[anIdentifier] = animation;
68  }
69 
70  return animation;
71 };
72 
73 CSSAnimation.prototype.description = function()
74 {
75  return "<animation " + this.identifier + " target=" + this.targetName + " properties=" + this.propertyanimations.map(function(anim){return anim.property;}).join(",") + " >";
76 }
77 
78 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 */)
79 {
80  if (this.islive)
81  return false;
82 // TODO: If a property already exist, replace its values & valueFunctions.
83 
84  var fullAnimationName = this.animationName + "_" + propertyName;
85 
86  var animation = {
87  name: fullAnimationName,
88  property: propertyName,
89  valuefunction: valueFunction,
90  keytimes: aKeyTimes,
91  values: aValues,
92  duration: aDuration,
93  completionfunction: aCompletionfunction };
94 
95  var animationTimingFunction;
96 
97  if (aTimingFunctions && (aTimingFunctions[0] instanceof Array))
98  {
99  animation.keyframestimingFunctions = aTimingFunctions;
100  // dummy timing function overriden by keyframes timings functions
101  animationTimingFunction = "linear";
102  }
103  else
104  {
105  animationTimingFunction = "cubic-bezier(" + aTimingFunctions + ")";
106  }
107 
108  this.animationstimingfunctions.push(animationTimingFunction);
109 
110  this.propertyanimations.push(animation);
111  this.animationsnames.push(fullAnimationName);
112  this.animationsdurations.push(aDuration + "s");
113 
114  return true;
115 };
116 
117 CSSAnimation.prototype.keyFrames = function()
118 {
119  var keyframesRules = [];
120 
121  var count = this.propertyanimations.length;
122 
123  for (var i = 0; i < count; i++)
124  {
125  var animation = this.propertyanimations[i],
126  property = animation.property,
127  valuefunction = animation.valuefunction,
128  keytimes = animation.keytimes,
129  values = animation.values,
130  timingFunctions = animation.keyframestimingFunctions;
131 
132  var keyframes = [],
133  keytimescount = keytimes.length,
134  start_value = values[0];
135 
136  for (var j = 0; j < keytimescount; j++)
137  {
138  var keytime = keytimes[j],
139  value = values[j],
140  timingFunction;
141 
142  if (valuefunction !== null)
143  value = valuefunction(start_value, value);
144 
145  var keyframeContent = property + ": " + value + ";";
146 
147  if (timingFunctions && timingFunctions.length && (timingFunction = timingFunctions[j]))
148  {
149  keyframeContent += ANIMATION_TIMING_FUNCTION_PROPERTY + ":cubic-bezier(" + timingFunction + ");";
150  }
151 
152  var keyframe = "\t" + Math.round(keytime * 100) + "% {\n\t\t" + keyframeContent + "\n\t}\n";
153 
154  keyframes.push(keyframe);
155  }
156  // TODO ! Add keyframe rule to CPCompatibility
157  var rule = ANIMATION_KEYFRAMES_RULE + " " + animation.name + " {\n" + keyframes.join(" ") + "}\n";
158 
159  keyframesRules.push(rule);
160  }
161 
162  return keyframesRules.join("\n");
163 };
164 
165 CSSAnimation.prototype.appendKeyFramesRule = function()
166 {
167  var styleElement = this.createKeyFramesStyleElement(),
168  keyframesText = this.keyFrames(),
169  nodeText = document.createTextNode(keyframesText);
170 
171  styleElement.appendChild(nodeText);
172  document.head.appendChild(styleElement);
173 };
174 
175 CSSAnimation.prototype.createKeyFramesStyleElement = function()
176 {
177  if (!this.styleElement)
178  {
179  var styleElement = document.createElement("style");
180 
181  styleElement.setAttribute("type", "text/css");
182 
183  this.styleElement = styleElement;
184  }
185 
186  return this.styleElement;
187 };
188 
189 CSSAnimation.prototype.endEventListener = function()
190 {
191  var animation = this,
192  animationsNames = this.animationsnames,
193  inFlightAnimationsNames = animationsNames.slice();
194 
195  if (!animation.listener)
196  {
197  var AnimationEndListener = function(event)
198  {
199  var idx = inFlightAnimationsNames.indexOf(event.animationName);
200 
201  if (idx !== -1)
202  inFlightAnimationsNames.splice(idx, 1);
203 
204  if (inFlightAnimationsNames.length > 0)
205  return;
206 
207  for (var i = 0; i < animationsNames.length; i++)
208  {
209  var completion = animation.completionFunctionForAnimationName(animationsNames[i]);
210 
211  if (completion)
212  completion();
213  }
214 
215  var eventTarget = event.target,
216  style = eventTarget.style;
217 
218  try
219  {
220  style.removeProperty(ANIMATION_NAME_PROPERTY);
221  style.removeProperty(ANIMATION_DURATION_PROPERTY);
222  style.removeProperty(ANIMATION_FILL_MODE_PROPERTY);
223  style.removeProperty("-webkit-backface-visibility");
224 
225  if (animation.animationstimingfunctions.length)
226  style.removeProperty(ANIMATION_TIMING_FUNCTION_PROPERTY);
227 
228  removeFromParent(animation.styleElement);
229 
230  eventTarget.removeEventListener(ANIMATION_END_EVENT_NAME, AnimationEndListener);
231  animation.listener = null;
232  delete (CURRENT_ANIMATIONS[animation.identifier]);
233  }
234  catch (err)
235  {
236  console.warn("CSSAnimation.j - " + err);
237  }
238  };
239 
240  this.listener = AnimationEndListener;
241  }
242 
243  return this.listener;
244 };
245 
246 CSSAnimation.prototype.completionFunctionForAnimationName = function(aName)
247 {
248  var propanims = this.propertyanimations,
249  count = propanims.length;
250 
251  while (count--)
252  {
253  var anim = propanims[count];
254 
255  if (anim.name === aName)
256  return anim.completionfunction;
257  }
258 
259  return null;
260 };
261 
262 CSSAnimation.prototype.addAnimationEndEventListener = function()
263 {
264  var listener = this.endEventListener();
265 
266  this.target.addEventListener(ANIMATION_END_EVENT_NAME, listener, false);
267 };
268 
269 CSSAnimation.prototype.setTargetStyleProperties = function()
270 {
271  var style = this.target.style;
272 
273  if (this.animationstimingfunctions.length)
274  style.setProperty(ANIMATION_TIMING_FUNCTION_PROPERTY, this.animationstimingfunctions.join(","));
275 
276  style.setProperty(ANIMATION_DURATION_PROPERTY, this.animationsdurations.join(","));
277 
278  style.setProperty(ANIMATION_FILL_MODE_PROPERTY, "forwards");
279 
280 // http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/css3-animations-the-hiccups-and-bugs-youll-want-to-avoid/
281  style.setProperty("-webkit-backface-visibility", "hidden");
282 };
283 
284 CSSAnimation.prototype.buildDOMElements = function()
285 {
286  this.appendKeyFramesRule();
287 
289 
291 
292  this.didBuildDOMElements = true;
293 };
294 
295 CSSAnimation.prototype.start = function()
296 {
297  if (this.propertyanimations.length === 0 || this.islive)
298  return false;
299 
300  if (!this.didBuildDOMElements)
301  this.buildDOMElements();
302 
303  this.target.style.setProperty(ANIMATION_NAME_PROPERTY, this.animationsnames.join(","));
304 
305  this.islive = true;
306 
307  return true;
308 };
var defineCSSProperties
Definition: CSSAnimation.j:13
var ANIMATION_NAME_PROPERTY
Definition: CSSAnimation.j:3
function CPBrowserCSSProperty(aProperty)
FrameUpdater prototype identifier
CSSAnimation prototype endEventListener
Definition: CSSAnimation.j:189
CSSAnimation prototype addAnimationEndEventListener
Definition: CSSAnimation.j:262
var ANIMATION_FILL_MODE_PROPERTY
Definition: CSSAnimation.j:3
CSSAnimation prototype createKeyFramesStyleElement
Definition: CSSAnimation.j:175
var ANIMATION_TIMING_FUNCTION_PROPERTY
Definition: CSSAnimation.j:3
CSSAnimation
Definition: CSSAnimation.j:37
CSSAnimation prototype keyFrames
Definition: CSSAnimation.j:117
var ANIMATION_PROPERTY
Definition: CSSAnimation.j:3
var ANIMATIONS_GLOBAL_ID
Definition: CSSAnimation.j:2
CPString name
Definition: CPException.j:47
function CPBrowserStyleProperty(aProperty)
CSSAnimation prototype buildDOMElements
Definition: CSSAnimation.j:284
var ANIMATION_END_EVENT_NAME
Definition: CSSAnimation.j:3
CSSAnimation prototype appendKeyFramesRule
Definition: CSSAnimation.j:165
var ANIMATION_DURATION_PROPERTY
Definition: CSSAnimation.j:3
var CURRENT_ANIMATIONS
Definition: CSSAnimation.j:3
var removeFromParent
Definition: CSSAnimation.j:29
CSSAnimation prototype setTargetStyleProperties
Definition: CSSAnimation.j:269
var ANIMATION_KEYFRAMES_RULE
Definition: CSSAnimation.j:3