blob: 6e91f68b9d0ff26568eeaa30b240e59e1e4720cf [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/css_transitions_adapter.h"
#include <memory>
#include <vector>
#include "cobalt/base/tokens.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_animatable.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/html_element.h"
#include "cobalt/dom/pseudo_element.h"
#include "cobalt/dom/transition_event.h"
#include "cobalt/web_animations/keyframe.h"
#include "cobalt/web_animations/keyframe_effect_read_only.h"
namespace cobalt {
namespace dom {
CSSTransitionsAdapter::CSSTransitionsAdapter(
const scoped_refptr<dom::DOMAnimatable>& target)
: animatable_(target) {}
CSSTransitionsAdapter::~CSSTransitionsAdapter() {
for (PropertyValueAnimationMap::iterator iter = animation_map_.begin();
iter != animation_map_.end(); ++iter) {
delete iter->second;
}
}
void CSSTransitionsAdapter::OnTransitionStarted(
const cssom::Transition& transition, cssom::TransitionSet* transition_set) {
// The process for constructing web animations from CSS transitions is
// described here:
// https://drafts.csswg.org/date/2015-03-02/web-animations-css-integration/#css-transitions
// Create our timing structure using the timing information from the
// transition.
scoped_refptr<web_animations::AnimationEffectTimingReadOnly> timing_input(
new web_animations::AnimationEffectTimingReadOnly(
transition.delay(), base::TimeDelta(),
web_animations::AnimationEffectTimingReadOnly::kBoth, 0.0, 1.0,
transition.duration(),
web_animations::AnimationEffectTimingReadOnly::kNormal,
cssom::TimingFunction::GetLinear()));
// Setup a KeyframeEffect with 2 keyframes, a start and end frame that hold
// the start and end transition property values. In general keyframes can
// contain values for many properties, but for transitions they will only
// ever have the transition's target property.
std::vector<web_animations::Keyframe::Data> frames;
web_animations::Keyframe::Data start_frame(0.0);
start_frame.set_easing(transition.timing_function());
start_frame.AddPropertyValue(transition.target_property(),
transition.start_value());
frames.push_back(start_frame);
web_animations::Keyframe::Data end_frame(1.0);
end_frame.AddPropertyValue(transition.target_property(),
transition.end_value());
frames.push_back(end_frame);
scoped_refptr<web_animations::KeyframeEffectReadOnly> keyframe_effect(
new web_animations::KeyframeEffectReadOnly(timing_input, animatable_,
frames));
// Finally setup and play our animation.
scoped_refptr<web_animations::Animation> animation(
new web_animations::Animation(keyframe_effect,
animatable_->GetDefaultTimeline()));
// Setup an event handler on the animation so we can watch for when it enters
// the after phase, allowing us to then trigger the transitionend event.
std::unique_ptr<web_animations::Animation::EventHandler> event_handler =
animation->AttachEventHandler(
base::Bind(&CSSTransitionsAdapter::HandleAnimationEnterAfterPhase,
base::Unretained(this), transition_set));
// Track the animation in our map of all CSS Transitions-created animations.
DCHECK(animation_map_.find(transition.target_property()) ==
animation_map_.end());
animation_map_.insert(std::make_pair(
transition.target_property(),
new AnimationWithEventHandler(animation, std::move(event_handler))));
animation->Play();
}
void CSSTransitionsAdapter::OnTransitionRemoved(
const cssom::Transition& transition,
cssom::Transition::IsCanceled is_canceled) {
// As described in the process for cancelling/stopping a transition here:
// https://dr.csswg.org/date/2015-03-02/web-animations-css-integration/Overview.html#css-transitions
// We find the animation corresponding to the removed transition, cancel it,
// and then remove it from our record of animations.
if (is_canceled == cssom::Transition::kIsNotCanceled) {
// An animation corresponding to a transition has entered the "after phase",
// so we should correspondingly fire the transitionend event.
// https://drafts.csswg.org/date/2015-03-02/web-animations-css-integration/#css-transitions-events
// Cobalt assumes that the CSSOM does not change during computed style
// calculation. Post to dispatch the event asynchronously here to avoid
// calling event handlers during computed style calculation, which in turn
// may modify CSSOM and require restart of the computed style calculation.
animatable_->GetEventTarget()->PostToDispatchEvent(
FROM_HERE,
new TransitionEvent(
base::Tokens::transitionend(), transition.target_property(),
static_cast<float>(transition.duration().InMillisecondsF())));
}
PropertyValueAnimationMap::iterator found =
animation_map_.find(transition.target_property());
DCHECK(animation_map_.end() != found);
found->second->animation->Cancel();
delete found->second;
animation_map_.erase(found);
}
void CSSTransitionsAdapter::HandleAnimationEnterAfterPhase(
cssom::TransitionSet* transition_set) {
DCHECK(animatable_->GetDefaultTimeline()->current_time());
base::TimeDelta current_time = base::TimeDelta::FromMillisecondsD(
animatable_->GetDefaultTimeline()->current_time().value_or(0));
transition_set->UpdateTransitions(current_time,
*animatable_->GetComputedStyle(),
*animatable_->GetComputedStyle());
}
} // namespace dom
} // namespace cobalt