Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: sky/engine/core/animation/css/CSSAnimations.cpp

Issue 1229273004: Remove Animations and Transitions. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "sky/engine/core/animation/css/CSSAnimations.h"
32
33 #include "gen/sky/core/StylePropertyShorthand.h"
34 #include "sky/engine/core/animation/ActiveAnimations.h"
35 #include "sky/engine/core/animation/AnimationTimeline.h"
36 #include "sky/engine/core/animation/KeyframeEffectModel.h"
37 #include "sky/engine/core/animation/LegacyStyleInterpolation.h"
38 #include "sky/engine/core/animation/css/CSSAnimatableValueFactory.h"
39 #include "sky/engine/core/animation/css/CSSPropertyEquality.h"
40 #include "sky/engine/core/css/CSSPropertyMetadata.h"
41 #include "sky/engine/core/css/CSSValueList.h"
42 #include "sky/engine/core/dom/Element.h"
43 #include "sky/engine/core/events/AnimationEvent.h"
44 #include "sky/engine/core/events/TransitionEvent.h"
45 #include "sky/engine/core/rendering/RenderLayer.h"
46 #include "sky/engine/core/rendering/RenderObject.h"
47 #include "sky/engine/core/rendering/style/KeyframeList.h"
48 #include "sky/engine/platform/animation/TimingFunction.h"
49 #include "sky/engine/public/platform/Platform.h"
50 #include "sky/engine/wtf/BitArray.h"
51 #include "sky/engine/wtf/HashSet.h"
52
53 namespace blink {
54
55 namespace {
56
57 CSSPropertyID propertyForAnimation(CSSPropertyID property)
58 {
59 switch (property) {
60 case CSSPropertyWebkitPerspective:
61 return CSSPropertyPerspective;
62 case CSSPropertyWebkitTransform:
63 return CSSPropertyTransform;
64 case CSSPropertyWebkitPerspectiveOriginX:
65 case CSSPropertyWebkitPerspectiveOriginY:
66 return CSSPropertyPerspectiveOrigin;
67 case CSSPropertyWebkitTransformOriginX:
68 case CSSPropertyWebkitTransformOriginY:
69 case CSSPropertyWebkitTransformOriginZ:
70 return CSSPropertyTransformOrigin;
71 default:
72 break;
73 }
74 return property;
75 }
76
77 } // namespace
78
79 CSSAnimations::CSSAnimations()
80 {
81 }
82
83 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle )
84 {
85 OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
86 calculateAnimationActiveInterpolations(update.get(), element, parentElement. document().timeline().currentTimeInternal());
87 calculateTransitionUpdate(update.get(), element, style);
88 calculateTransitionActiveInterpolations(update.get(), element, parentElement .document().timeline().currentTimeInternal());
89 return update->isEmpty() ? nullptr : update.release();
90 }
91
92 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
93 {
94 if (!m_pendingUpdate) {
95 m_previousActiveInterpolationsForAnimations.clear();
96 return;
97 }
98
99 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
100
101 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolation sForAnimations());
102
103 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationN ames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
104 RefPtr<AnimationPlayer> player = m_animations.take(*iter);
105 player->cancel();
106 player->update(TimingUpdateOnDemand);
107 }
108
109 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPause Toggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
110 AnimationPlayer* player = m_animations.get(*iter);
111 if (player->paused())
112 player->unpause();
113 else
114 player->pause();
115 if (player->outdated())
116 player->update(TimingUpdateOnDemand);
117 }
118
119 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update- >newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
120 const InertAnimation* inertAnimation = iter->animation.get();
121 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEve ntDelegate(element, iter->name));
122 RefPtr<Animation> animation = Animation::create(element, inertAnimation- >effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventD elegate.release());
123 RefPtr<AnimationPlayer> player = element->document().timeline().createAn imationPlayer(animation.get());
124 if (inertAnimation->paused())
125 player->pause();
126 player->update(TimingUpdateOnDemand);
127 m_animations.set(iter->name, player.get());
128 }
129
130 // Transitions that are run on the compositor only update main-thread state
131 // lazily. However, we need the new state to know what the from state shoud
132 // be when transitions are retargeted. Instead of triggering complete style
133 // recalculation, we find these cases by searching for new transitions that
134 // have matching cancelled animation property IDs on the compositor.
135 HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedComp ositorTransitions;
136 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions(). begin(); iter != update->cancelledTransitions().end(); ++iter) {
137 CSSPropertyID id = *iter;
138 ASSERT(m_transitions.contains(id));
139
140 RefPtr<AnimationPlayer> player = m_transitions.take(id).player;
141 player->cancel();
142 player->update(TimingUpdateOnDemand);
143 }
144
145 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->new Transitions().begin(); iter != update->newTransitions().end(); ++iter) {
146 const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
147
148 RunningTransition runningTransition;
149 runningTransition.from = newTransition.from;
150 runningTransition.to = newTransition.to;
151
152 CSSPropertyID id = newTransition.id;
153 InertAnimation* inertAnimation = newTransition.animation.get();
154 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionE ventDelegate(element, newTransition.eventId));
155
156 RefPtr<AnimationEffect> effect = inertAnimation->effect();
157 RefPtr<Animation> transition = Animation::create(element, effect, inertA nimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.releas e());
158 RefPtr<AnimationPlayer> player = element->document().timeline().createAn imationPlayer(transition.get());
159 player->update(TimingUpdateOnDemand);
160 runningTransition.player = player;
161 m_transitions.set(id, runningTransition);
162 ASSERT(id != CSSPropertyInvalid);
163 }
164 }
165
166 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, CSSPr opertyID eventId, const CSSTransitionData& transitionData, size_t transitionInde x, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* a ctiveTransitions, CSSAnimationUpdate* update, const Element* element)
167 {
168 RefPtr<AnimatableValue> to = nullptr;
169 if (activeTransitions) {
170 TransitionMap::const_iterator activeTransitionIter = activeTransitions-> find(id);
171 if (activeTransitionIter != activeTransitions->end()) {
172 to = CSSAnimatableValueFactory::create(id, style);
173 const AnimatableValue* activeTo = activeTransitionIter->value.to;
174 if (to->equals(activeTo))
175 return;
176 update->cancelTransition(id);
177 ASSERT(!element->activeAnimations() || !element->activeAnimations()- >isAnimationStyleChange());
178 }
179 }
180
181 if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
182 return;
183 if (!to)
184 to = CSSAnimatableValueFactory::create(id, style);
185
186 RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyl e);
187 // If we have multiple transitions on the same property, we will use the
188 // last one since we iterate over them in order.
189 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
190 return;
191
192 Timing timing = transitionData.convertToTiming(transitionIndex);
193 if (timing.startDelay + timing.iterationDuration <= 0)
194 return;
195
196 AnimatableValueKeyframeVector keyframes;
197
198 RefPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::cre ate();
199 startKeyframe->setPropertyValue(id, from.get());
200 startKeyframe->setOffset(0);
201 startKeyframe->setEasing(timing.timingFunction.release());
202 timing.timingFunction = LinearTimingFunction::shared();
203 keyframes.append(startKeyframe);
204
205 RefPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::creat e();
206 endKeyframe->setPropertyValue(id, to.get());
207 endKeyframe->setOffset(1);
208 keyframes.append(endKeyframe);
209
210 RefPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeE ffectModel::create(keyframes);
211 update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::c reate(effect, timing, false));
212 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnima tionStyleChange());
213 }
214
215 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
216 {
217 if (!element)
218 return;
219
220 ActiveAnimations* activeAnimations = element->activeAnimations();
221 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimation s->cssAnimations().m_transitions : 0;
222 const CSSTransitionData* transitionData = style.transitions();
223
224 #if ENABLE(ASSERT)
225 // In debug builds we verify that it would have been safe to avoid populatin g and testing listedProperties if the style recalc is due to animation.
226 const bool animationStyleRecalc = false;
227 #else
228 // In release builds we avoid the cost of checking for new and interrupted t ransitions if the style recalc is due to animation.
229 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAn imationStyleChange();
230 #endif
231
232 BitArray<numCSSProperties> listedProperties;
233 bool anyTransitionHadTransitionAll = false;
234 const RenderObject* renderer = element->renderer();
235 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer ->style() && transitionData) {
236 const RenderStyle& oldStyle = *renderer->style();
237
238 for (size_t i = 0; i < transitionData->propertyList().size(); ++i) {
239 const CSSTransitionData::TransitionProperty& transitionProperty = tr ansitionData->propertyList()[i];
240 CSSTransitionData::TransitionPropertyType mode = transitionProperty. propertyType;
241 CSSPropertyID property = transitionProperty.propertyId;
242 if (mode == CSSTransitionData::TransitionNone || mode == CSSTransiti onData::TransitionUnknown)
243 continue;
244
245 bool animateAll = mode == CSSTransitionData::TransitionAll;
246 ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProp erty);
247 if (animateAll)
248 anyTransitionHadTransitionAll = true;
249 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimati ons::animatableProperties() : shorthandForProperty(property);
250 // If not a shorthand we only execute one iteration of this loop, an d refer to the property directly.
251 for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
252 CSSPropertyID id = propertyList.length() ? propertyList.properti es()[j] : property;
253 CSSPropertyID eventId = id;
254
255 if (!animateAll) {
256 id = propertyForAnimation(id);
257 if (CSSPropertyMetadata::isAnimatableProperty(id))
258 listedProperties.set(id);
259 else
260 continue;
261 }
262
263 if (!update->activeInterpolationsForAnimations().contains(id)
264 && (!activeAnimations || !activeAnimations->cssAnimations(). m_previousActiveInterpolationsForAnimations.contains(id))) {
265 calculateTransitionUpdateForProperty(id, eventId, *transitio nData, i, oldStyle, style, activeTransitions, update, element);
266 }
267 }
268 }
269 }
270
271 if (activeTransitions) {
272 for (TransitionMap::const_iterator iter = activeTransitions->begin(); it er != activeTransitions->end(); ++iter) {
273 const AnimationPlayer& player = *iter->value.player;
274 CSSPropertyID id = iter->key;
275 if (player.finishedInternal() || (!anyTransitionHadTransitionAll && !animationStyleRecalc && !listedProperties.get(id))) {
276 // TODO: Figure out why this fails on Chrome OS login page. crbu g.com/365507
277 // ASSERT(player.finishedInternal() || !(activeAnimations && act iveAnimations->isAnimationStyleChange()));
278 update->cancelTransition(id);
279 }
280 }
281 }
282 }
283
284 void CSSAnimations::cancel()
285 {
286 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animation s.end(); ++iter) {
287 iter->value->cancel();
288 iter->value->update(TimingUpdateOnDemand);
289 }
290
291 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transit ions.end(); ++iter) {
292 iter->value.player->cancel();
293 iter->value.player->update(TimingUpdateOnDemand);
294 }
295
296 m_animations.clear();
297 m_transitions.clear();
298 m_pendingUpdate = nullptr;
299 }
300
301 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* u pdate, const Element* element, double timelineCurrentTime)
302 {
303 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
304 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : 0;
305
306 HashMap<CSSPropertyID, RefPtr<Interpolation> > activeInterpolationsForAnimat ions(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::Defau ltPriority, timelineCurrentTime));
307 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimat ions);
308 }
309
310 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
311 {
312 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
313 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : 0;
314
315 HashMap<CSSPropertyID, RefPtr<Interpolation> > activeInterpolationsForTransi tions;
316 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isE mpty()) {
317 activeInterpolationsForTransitions = AnimationStack::activeInterpolation s(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime);
318 } else {
319 Vector<RawPtr<InertAnimation> > newTransitions;
320 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update- >newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
321 newTransitions.append(iter->value.animation.get());
322
323 HashSet<RawPtr<const AnimationPlayer> > cancelledAnimationPlayers;
324 if (!update->cancelledTransitions().isEmpty()) {
325 ASSERT(activeAnimations);
326 const TransitionMap& transitionMap = activeAnimations->cssAnimations ().m_transitions;
327 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransi tions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
328 ASSERT(transitionMap.contains(*iter));
329 cancelledAnimationPlayers.add(transitionMap.get(*iter).player.ge t());
330 }
331 }
332
333 activeInterpolationsForTransitions = AnimationStack::activeInterpolation s(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::Transi tionPriority, timelineCurrentTime);
334 }
335
336 // Properties being animated by animations don't get values from transitions applied.
337 if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpo lationsForTransitions.isEmpty()) {
338 for (HashMap<CSSPropertyID, RefPtr<Interpolation> >::const_iterator iter = update->activeInterpolationsForAnimations().begin(); iter != update->activeIn terpolationsForAnimations().end(); ++iter)
339 activeInterpolationsForTransitions.remove(iter->key);
340 }
341 update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTrans itions);
342 }
343
344 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
345 {
346 if (m_target->document().hasListenerType(listenerType)) {
347 RefPtr<AnimationEvent> event = AnimationEvent::create(eventName, m_name, elapsedTime);
348 event->setTarget(m_target);
349 m_target->document().enqueueAnimationFrameEvent(event);
350 }
351 }
352
353 void CSSAnimations::AnimationEventDelegate::onEventCondition(const AnimationNode * animationNode)
354 {
355 const AnimationNode::Phase currentPhase = animationNode->phase();
356 const double currentIteration = animationNode->currentIteration();
357
358 if (m_previousPhase != currentPhase
359 && (currentPhase == AnimationNode::PhaseActive || currentPhase == Animat ionNode::PhaseAfter)
360 && (m_previousPhase == AnimationNode::PhaseNone || m_previousPhase == An imationNode::PhaseBefore)) {
361 // The spec states that the elapsed time should be
362 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
363 // implementation. See crbug.com/279611
364 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animati onstart, 0);
365 }
366
367 if (currentPhase == AnimationNode::PhaseActive && m_previousPhase == current Phase && m_previousIteration != currentIteration) {
368 // We fire only a single event for all iterations thast terminate
369 // between a single pair of samples. See http://crbug.com/275263. For
370 // compatibility with the existing implementation, this event uses
371 // the elapsedTime for the first iteration in question.
372 ASSERT(!std::isnan(animationNode->specifiedTiming().iterationDuration));
373 const double elapsedTime = animationNode->specifiedTiming().iterationDur ation * (m_previousIteration + 1);
374 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::ani mationiteration, elapsedTime);
375 }
376
377 if (currentPhase == AnimationNode::PhaseAfter && m_previousPhase != Animatio nNode::PhaseAfter)
378 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animation end, animationNode->activeDurationInternal());
379
380 m_previousPhase = currentPhase;
381 m_previousIteration = currentIteration;
382 }
383
384 void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNod e* animationNode)
385 {
386 const AnimationNode::Phase currentPhase = animationNode->phase();
387 if (currentPhase == AnimationNode::PhaseAfter && currentPhase != m_previousP hase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
388 String propertyName = getPropertyNameString(m_property);
389 const Timing& timing = animationNode->specifiedTiming();
390 double elapsedTime = timing.iterationDuration;
391 const AtomicString& eventType = EventTypeNames::transitionend;
392 RefPtr<TransitionEvent> event = TransitionEvent::create(eventType, prope rtyName, elapsedTime);
393 event->setTarget(m_target);
394 m_target->document().enqueueAnimationFrameEvent(event);
395 }
396
397 m_previousPhase = currentPhase;
398 }
399
400 const StylePropertyShorthand& CSSAnimations::animatableProperties()
401 {
402 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
403 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
404 if (properties.isEmpty()) {
405 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
406 CSSPropertyID id = convertToCSSPropertyID(i);
407 if (CSSPropertyMetadata::isAnimatableProperty(id))
408 properties.append(id);
409 }
410 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, propertie s.begin(), properties.size());
411 }
412 return propertyShorthand;
413 }
414
415 // Animation properties are not allowed to be affected by Web Animations.
416 // http://dev.w3.org/fxtf/web-animations/#not-animatable
417 bool CSSAnimations::isAllowedAnimation(CSSPropertyID property)
418 {
419 switch (property) {
420 case CSSPropertyAnimation:
421 case CSSPropertyAnimationDelay:
422 case CSSPropertyAnimationDirection:
423 case CSSPropertyAnimationDuration:
424 case CSSPropertyAnimationFillMode:
425 case CSSPropertyAnimationIterationCount:
426 case CSSPropertyAnimationName:
427 case CSSPropertyAnimationPlayState:
428 case CSSPropertyAnimationTimingFunction:
429 case CSSPropertyDisplay:
430 case CSSPropertyTransition:
431 case CSSPropertyTransitionDelay:
432 case CSSPropertyTransitionDuration:
433 case CSSPropertyTransitionProperty:
434 case CSSPropertyTransitionTimingFunction:
435 return false;
436 default:
437 return true;
438 }
439 }
440
441 } // namespace blink
OLDNEW
« no previous file with comments | « sky/engine/core/animation/css/CSSAnimations.h ('k') | sky/engine/core/animation/css/CSSPropertyEquality.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698