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

Unified Diff: Source/core/animation/css/CSSAnimations.cpp

Issue 26382004: Web Animations CSS: Implement CSS Transitions backed on Web Animations model (Closed) Base URL: https://chromium.googlesource.com/chromium/blink@master
Patch Set: store animatablevalues explicitly Created 7 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/animation/css/CSSAnimations.h ('k') | Source/core/css/resolver/StyleAdjuster.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/animation/css/CSSAnimations.cpp
diff --git a/Source/core/animation/css/CSSAnimations.cpp b/Source/core/animation/css/CSSAnimations.cpp
index 97ed93de6e40f9e9712348a13d94ad5be4184f6a..15384058642905120dae3bfe540cb35bc15a79b2 100644
--- a/Source/core/animation/css/CSSAnimations.cpp
+++ b/Source/core/animation/css/CSSAnimations.cpp
@@ -31,21 +31,24 @@
#include "config.h"
#include "core/animation/css/CSSAnimations.h"
+#include "StylePropertyShorthand.h"
#include "core/animation/ActiveAnimations.h"
#include "core/animation/DocumentTimeline.h"
#include "core/animation/KeyframeAnimationEffect.h"
+#include "core/animation/css/CSSAnimatableValueFactory.h"
#include "core/css/CSSKeyframeRule.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/Element.h"
#include "core/events/ThreadLocalEventNames.h"
+#include "core/events/TransitionEvent.h"
#include "core/events/WebKitAnimationEvent.h"
#include "core/platform/animation/CSSAnimationDataList.h"
#include "core/platform/animation/TimingFunction.h"
#include "wtf/HashSet.h"
-namespace {
+namespace WebCore {
-using namespace WebCore;
+namespace {
bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference)
{
@@ -61,10 +64,6 @@ bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
return target > reference;
}
-} // namespace
-
-namespace WebCore {
-
// Returns the default timing function.
const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing)
{
@@ -119,6 +118,62 @@ const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData*
return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
}
+struct TransitionDetails {
dstockwell 2013/10/09 21:03:51 CandidateTransition?
Timothy Loh 2013/10/10 02:10:19 Done.
+ TransitionDetails(PassRefPtr<AnimatableValue> from, PassRefPtr<AnimatableValue> to, const CSSAnimationData* anim)
+ : from(from)
+ , to(to)
+ , anim(anim)
+ {
+ }
+ TransitionDetails() { } // The HashMap calls the default ctor
+ RefPtr<AnimatableValue> from;
+ RefPtr<AnimatableValue> to;
+ const CSSAnimationData* anim;
+};
+typedef HashMap<CSSPropertyID, TransitionDetails> StyleChange;
dstockwell 2013/10/09 21:03:51 CandidateTransitionMap
Timothy Loh 2013/10/10 02:10:19 Done.
+
+void updateIfPropertyChanged(const CSSAnimationData* anim, CSSPropertyID id, const RenderStyle* oldStyle, const RenderStyle* newStyle, StyleChange& styleChange)
+{
+ RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
+ RefPtr<AnimatableValue> to = CSSAnimatableValueFactory::create(id, newStyle);
+ // If we have multiple transitions on the same property, we will use the
+ // last one since we iterate over them in order and this will override
+ // a previously set TransitionDetails.
+ if (!from->equals(to.get()))
+ styleChange.add(id, TransitionDetails(from, to, anim));
+}
+
+void computeStyleChange(const RenderStyle* oldStyle, const RenderStyle* newStyle, StyleChange& styleChange, HashSet<CSSPropertyID>& listedProperties)
+{
+ if (!newStyle->transitions())
+ return;
+
+ for (size_t i = 0; i < newStyle->transitions()->size(); ++i) {
+ const CSSAnimationData* anim = newStyle->transitions()->animation(i);
+ CSSAnimationData::AnimationMode mode = anim->animationMode();
+ if (anim->duration() + anim->delay() <= 0 || mode == CSSAnimationData::AnimateNone)
+ continue;
+
+ bool animateAll = mode == CSSAnimationData::AnimateAll;
+ ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
+ const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
+ if (!propertyList.length()) {
+ listedProperties.add(anim->property());
+ updateIfPropertyChanged(anim, anim->property(), oldStyle, newStyle, styleChange);
+ } else {
+ for (unsigned i = 0; i < propertyList.length(); ++i) {
+ CSSPropertyID id = propertyList.properties()[i];
+ if (!animateAll && !CSSAnimations::isAnimatableProperty(id))
+ continue;
+ listedProperties.add(id);
+ updateIfPropertyChanged(anim, id, oldStyle, newStyle, styleChange);
+ }
+ }
+ }
+}
+
+} // namespace
+
CSSAnimationUpdateScope::CSSAnimationUpdateScope(Element* target)
: m_target(target)
{
@@ -143,18 +198,21 @@ CSSAnimationUpdateScope::~CSSAnimationUpdateScope()
cssAnimations->maybeApplyPendingUpdate(m_target);
}
-bool CSSAnimations::needsUpdate(const Element* element, const RenderStyle* style)
+PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const RenderStyle* style, StyleResolver* resolver)
{
- ActiveAnimations* activeAnimations = element->activeAnimations();
- const CSSAnimationDataList* animations = style->animations();
- const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnimations() : 0;
- EDisplay display = style->display();
- return (display != NONE && animations && animations->size()) || (cssAnimations && !cssAnimations->isEmpty());
+ ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled());
+ OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
+ calculateAnimationUpdate(update.get(), element, style, resolver);
+ calculateTransitionUpdate(update.get(), element, style);
+ return update->isEmpty() ? nullptr : update.release();
}
-PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const RenderStyle* style, const CSSAnimations* cssAnimations, const CSSAnimationDataList* animationDataList, StyleResolver* resolver)
+void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const RenderStyle* style, StyleResolver* resolver)
{
- OwnPtr<CSSAnimationUpdate> update;
+ ActiveAnimations* activeAnimations = element->activeAnimations();
+ const CSSAnimationDataList* animationDataList = style->animations();
+ const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnimations() : 0;
+
HashSet<AtomicString> inactive;
if (cssAnimations)
for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
@@ -182,8 +240,6 @@ PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element,
Vector<std::pair<KeyframeAnimationEffect::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
resolver->resolveKeyframes(element, style, animationName, defaultTimingFunction.get(), keyframesAndTimingFunctions);
if (!keyframesAndTimingFunctions.isEmpty()) {
- if (!update)
- update = adoptPtr(new CSSAnimationUpdate());
HashSet<RefPtr<InertAnimation> > animations;
for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j) {
ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty());
@@ -196,12 +252,8 @@ PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element,
}
}
- if (!inactive.isEmpty() && !update)
- update = adoptPtr(new CSSAnimationUpdate());
for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter)
update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter));
-
- return update.release();
}
void CSSAnimations::maybeApplyPendingUpdate(Element* element)
@@ -223,7 +275,7 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
// FIXME: Apply updates to play-state.
for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
- OwnPtr<CSSAnimations::EventDelegate> eventDelegate = adoptPtr(new EventDelegate(element, iter->name));
+ OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
HashSet<RefPtr<Player> > players;
for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = iter->animations.begin(); animationsIter != iter->animations.end(); ++animationsIter) {
const InertAnimation* inertAnimation = animationsIter->get();
@@ -234,6 +286,93 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
}
m_animations.set(iter->name, players);
}
+
+ for (HashSet<CSSPropertyID>::iterator iter = update->endedTransitions().begin(); iter != update->endedTransitions().end(); ++iter) {
+ ASSERT(m_transitions.contains(*iter));
+ m_transitions.take(*iter).player->cancel();
+ }
+
+ for (size_t i = 0; i < update->newTransitions().size(); ++i) {
+ const CSSAnimationUpdate::NewTransition& newTransition = update->newTransitions()[i];
+
+ RunningTransition runningTransition;
+ runningTransition.from = newTransition.from;
+ runningTransition.to = newTransition.to;
+
+ CSSPropertyID id = newTransition.id;
+ InertAnimation* inertAnimation = newTransition.animation.get();
+ OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
+ RefPtr<Animation> transition = Animation::create(element, inertAnimation->effect(), inertAnimation->specified(), eventDelegate.release());
+ // FIXME: Transitions need to be added to a separate timeline.
+ runningTransition.player = element->document().timeline()->play(transition.get());
+ m_transitions.set(id, runningTransition);
+ }
+}
+
+void CSSAnimations::calculateTransitionUpdateForProperty(CSSAnimationUpdate* update, CSSPropertyID id, const AnimatableValue* from, const AnimatableValue* to, const CSSAnimationData* anim, const TransitionMap* transitions)
+{
+ // FIXME: Skip the rest of this if there is a running animation on this property
+
+ Timing timing;
+ RefPtr<TimingFunction> timingFunction = timingFromAnimationData(anim, timing);
+ timing.timingFunction = timingFunction;
+ timing.fillMode = Timing::FillModeBoth;
dstockwell 2013/10/09 21:03:51 We should discuss this, I'm not sure why this is n
Timothy Loh 2013/10/10 02:10:19 We still need at least FillModeBackwards so delay
+
+ if (transitions) {
+ TransitionMap::const_iterator existingTransitionIter = transitions->find(id);
+
+ if (existingTransitionIter != transitions->end() && !update->endedTransitions().contains(id)) {
+ const AnimatableValue* existingTo = existingTransitionIter->value.to;
+ if (existingTo->equals(to))
+ return;
+ update->endTransition(id);
+ }
+ }
+
+ KeyframeAnimationEffect::KeyframeVector keyframes;
+
+ RefPtr<Keyframe> startKeyframe = Keyframe::create();
+ startKeyframe->setPropertyValue(id, from);
+ startKeyframe->setOffset(0);
+ keyframes.append(startKeyframe);
+
+ RefPtr<Keyframe> endKeyframe = Keyframe::create();
+ endKeyframe->setPropertyValue(id, to);
+ endKeyframe->setOffset(1);
+ keyframes.append(endKeyframe);
+
+ RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes);
+
+ update->startTransition(id, from, to, InertAnimation::create(effect, timing));
+}
+
+void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle* style)
+{
+ ActiveAnimations* activeAnimations = element->activeAnimations();
+ const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnimations() : 0;
+ const TransitionMap* transitions = cssAnimations ? &cssAnimations->m_transitions : 0;
+
+ HashSet<CSSPropertyID> listedProperties;
+ if (style->display() != NONE && element->renderer() && element->renderer()->style()) {
+ StyleChange styleChange;
+ computeStyleChange(element->renderer()->style(), style, styleChange, listedProperties);
+ for (StyleChange::const_iterator iter = styleChange.begin(); iter != styleChange.end(); ++iter) {
+ CSSPropertyID id = iter->key;
dstockwell 2013/10/09 21:03:51 Inline these, there are no line limits in blink.
Timothy Loh 2013/10/10 02:10:19 Done.
+ AnimatableValue* from = iter->value.from.get();
+ AnimatableValue* to = iter->value.to.get();
+ const CSSAnimationData* anim = iter->value.anim;
+ calculateTransitionUpdateForProperty(update, id, from, to, anim, transitions);
+ }
+ }
+
+ if (transitions) {
+ for (TransitionMap::const_iterator iter = transitions->begin(); iter != transitions->end(); ++iter) {
+ const TimedItem* timedItem = iter->value.player->source();
+ CSSPropertyID id = iter->key;
+ if (timedItem->phase() == TimedItem::PhaseAfter || !listedProperties.contains(id))
+ update->endTransition(id);
+ }
+ }
}
void CSSAnimations::cancel()
@@ -244,17 +383,21 @@ void CSSAnimations::cancel()
(*animationsIter)->cancel();
}
+ for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter)
+ iter->value.player->cancel();
+
m_animations.clear();
+ m_transitions.clear();
m_pendingUpdate = nullptr;
}
-void CSSAnimations::EventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
+void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
{
if (m_target->document().hasListenerType(listenerType))
m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnimationEvent::create(eventName, m_name, elapsedTime));
}
-void CSSAnimations::EventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
{
// Events for a single document are queued and dispatched as a group at
// the end of DocumentTimeline::serviceAnimations.
@@ -287,6 +430,26 @@ void CSSAnimations::EventDelegate::onEventCondition(const TimedItem* timedItem,
maybeDispatch(Document::ANIMATIONEND_LISTENER, EventNames::animationend, timedItem->activeDuration());
}
+void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+{
+ // Events for a single document are queued and dispatched as a group at
+ // the end of DocumentTimeline::serviceAnimations.
+ // FIXME: Events which are queued outside of serviceAnimations should
+ // trigger a timer to dispatch when control is released.
+ const TimedItem::Phase currentPhase = timedItem->phase();
+ if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase)) {
+ if (m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
+ String propertyName = getPropertyNameString(m_property);
+ const Timing& timing = timedItem->specified();
+ double elapsedTime = timing.iterationDuration;
+ const AtomicString& eventType = EventNames::transitionend;
+ String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
+ m_target->document().timeline()->addEventToDispatch(m_target, TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement));
+ }
+ }
+}
+
+
bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
{
switch (property) {
@@ -404,4 +567,19 @@ bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
}
}
+const StylePropertyShorthand& CSSAnimations::animatableProperties()
+{
+ DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
+ DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
+ if (properties.isEmpty()) {
+ for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
+ CSSPropertyID id = convertToCSSPropertyID(i);
+ if (isAnimatableProperty(id))
+ properties.append(id);
+ }
+ propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
+ }
+ return propertyShorthand;
+}
+
} // namespace WebCore
« no previous file with comments | « Source/core/animation/css/CSSAnimations.h ('k') | Source/core/css/resolver/StyleAdjuster.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698