void AnimationPlayer::play() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (!playing()) m_startTime = nullValue(); if (playStateInternal() == Idle) { // We may not go into the pending state, but setting it to something other // than Idle here will force an update. ASSERT(isNull(m_startTime)); m_playState = Pending; m_held = true; m_holdTime = 0; } m_finished = false; unpauseInternal(); if (!m_content) return; double currentTime = this->currentTimeInternal(); if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) setCurrentTimeInternal(0, TimingUpdateOnDemand); else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd())) setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); }
void AnimationPlayer::play() { if (!playing()) m_startTime = nullValue(); setCompositorPending(); unpauseInternal(); if (!m_content) return; double currentTime = this->currentTimeInternal(); if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) setCurrentTimeInternal(0, TimingUpdateOnDemand); else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd())) setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); m_finished = false; }
void AnimationPlayer::postCommit(double timelineTime) { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompositorPending); m_compositorPending = false; if (!m_compositorState || m_compositorState->pendingAction == None) return; switch (m_compositorState->pendingAction) { case Start: if (!std::isnan(m_compositorState->startTime)) { ASSERT(m_startTime == m_compositorState->startTime); m_compositorState->pendingAction = None; } break; case Pause: case PauseThenStart: ASSERT(std::isnan(m_startTime)); m_compositorState->pendingAction = None; setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m_playbackRate, TimingUpdateForAnimationFrame); m_currentTimePending = false; break; default: ASSERT_NOT_REACHED(); } }
void AnimationPlayer::setStartTimeInternal(double newStartTime) { ASSERT(!m_paused); ASSERT(std::isfinite(newStartTime)); ASSERT(newStartTime != m_startTime); bool hadStartTime = hasStartTime(); double previousCurrentTime = currentTimeInternal(); m_startTime = newStartTime; if (m_held && m_playbackRate) { // If held, the start time would still be derrived from the hold time. // Force a new, limited, current time. m_held = false; double currentTime = calculateCurrentTime(); if (m_playbackRate > 0 && currentTime > sourceEnd()) { currentTime = sourceEnd(); } else if (m_playbackRate < 0 && currentTime < 0) { currentTime = 0; } setCurrentTimeInternal(currentTime, TimingUpdateOnDemand); } updateCurrentTimingState(TimingUpdateOnDemand); double newCurrentTime = currentTimeInternal(); if (previousCurrentTime != newCurrentTime) { setOutdated(); } else if (!hadStartTime && m_timeline) { // Even though this player is not outdated, time to effect change is // infinity until start time is set. m_timeline->wake(); } }
void AnimationPlayer::unpauseInternal() { if (!m_paused) return; m_paused = false; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
void AnimationPlayer::reverse() { if (!m_playbackRate) { return; } if (m_content) { if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd()) { setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); ASSERT(finished()); } else if (m_playbackRate < 0 && currentTimeInternal() < 0) { setCurrentTimeInternal(0, TimingUpdateOnDemand); ASSERT(finished()); } } setPlaybackRate(-m_playbackRate); unpauseInternal(); }
void AnimationPlayer::pauseForTesting(double pauseTime) { RELEASE_ASSERT(!paused()); setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); if (hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal()); m_isPausedForTesting = true; pause(); }
void AnimationPlayer::finish(ExceptionState& exceptionState) { if (!m_playbackRate) { return; } if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infinity()) { exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity."); return; } if (playing()) { setCompositorPending(); } if (m_playbackRate < 0) { setCurrentTimeInternal(0, TimingUpdateOnDemand); } else { setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); } ASSERT(finished()); }
void Animation::setCurrentTime(double newCurrentTime) { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); m_currentTimePending = false; setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); if (calculatePlayState() == Finished) m_startTime = calculateStartTime(newCurrentTime); }
void AnimationPlayer::pause() { if (m_paused) return; if (playing()) { setCompositorPending(); m_currentTimePending = true; } m_paused = true; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
void AnimationPlayer::setCurrentTime(double newCurrentTime) { UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetCurrentTime); if (!std::isfinite(newCurrentTime)) return; PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); m_currentTimePending = false; setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); }
// Update timing to reflect updated animation clock due to tick void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason) { if (m_held) { setCurrentTimeInternal(m_holdTime, reason); return; } if (!limited(calculateCurrentTime())) return; m_held = true; m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); }
void AnimationPlayer::pause() { if (m_paused) return; PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (playing()) { m_currentTimePending = true; } m_paused = true; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
void AnimationPlayer::setCurrentTime(double newCurrentTime) { if (!std::isfinite(newCurrentTime)) return; setCompositorPending(); // Setting current time while pending forces a start time. if (m_currentTimePending) { m_startTime = 0; m_currentTimePending = false; } setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); }
void AnimationPlayer::setPlaybackRateInternal(double playbackRate) { ASSERT(std::isfinite(playbackRate)); ASSERT(playbackRate != m_playbackRate); if (!finished() && !paused() && hasStartTime()) m_currentTimePending = true; double storedCurrentTime = currentTimeInternal(); if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0)) m_finished = false; m_playbackRate = playbackRate; m_startTime = std::numeric_limits<double>::quiet_NaN(); setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
void AnimationPlayer::setSource(AnimationNode* newSource) { if (m_content == newSource) return; setCompositorPending(true); double storedCurrentTime = currentTimeInternal(); if (m_content) m_content->detach(); m_content = newSource; if (newSource) { // FIXME: This logic needs to be updated once groups are implemented if (newSource->player()) newSource->player()->cancel(); newSource->attach(this); setOutdated(); } setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
void AnimationPlayer::finish(ExceptionState& exceptionState) { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (!m_playbackRate || playStateInternal() == Idle) { return; } if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infinity()) { exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity."); return; } double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd(); setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand); if (!paused()) { m_startTime = calculateStartTime(newCurrentTime); } m_currentTimePending = false; ASSERT(finished()); }
void Animation::setEffect(AnimationEffect* newEffect) { if (m_content == newEffect) return; PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, SetCompositorPendingWithEffectChanged); double storedCurrentTime = currentTimeInternal(); if (m_content) m_content->detach(); m_content = newEffect; if (newEffect) { // FIXME: This logic needs to be updated once groups are implemented if (newEffect->animation()) { newEffect->animation()->cancel(); newEffect->animation()->setEffect(0); } newEffect->attach(this); setOutdated(); } setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
// Update timing to reflect updated animation clock due to tick void Animation::updateCurrentTimingState(TimingUpdateReason reason) { if (m_held) { double newCurrentTime = m_holdTime; if (playStateInternal() == Finished && !isNull(m_startTime) && m_timeline) { // Add hystersis due to floating point error accumulation if (!limited(calculateCurrentTime() + 0.001 * m_playbackRate)) { // The current time became unlimited, eg. due to a backwards // seek of the timeline. newCurrentTime = calculateCurrentTime(); } else if (!limited(m_holdTime)) { // The hold time became unlimited, eg. due to the effect // becoming longer. newCurrentTime = clampTo<double>(calculateCurrentTime(), 0, effectEnd()); } } setCurrentTimeInternal(newCurrentTime, reason); } else if (limited(calculateCurrentTime())) { m_held = true; m_holdTime = m_playbackRate < 0 ? 0 : effectEnd(); } }
void AnimationTimeline::setCurrentTime(double currentTime) { setCurrentTimeInternal(currentTime / 1000); }