Skip to content

Commit

Permalink
Adds AnimationContainer, which can be used to group a set of
Browse files Browse the repository at this point in the history
animations to have the same timer. By default each animation has one
animationcontainer, but I'm going to change the tab renderer to share
the animationcontainer so that the pulse effects happen in unison.

Also updated the BoundsAnimator so that I can use it by the TabStrip.

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/1575011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43407 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
sky@chromium.org committed Apr 1, 2010
1 parent e3f4cc6 commit 059e7a5
Show file tree
Hide file tree
Showing 19 changed files with 742 additions and 184 deletions.
97 changes: 57 additions & 40 deletions app/animation.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "app/animation.h"

#include "base/message_loop.h"
#include "app/animation_container.h"
#include "gfx/rect.h"

#if defined(OS_WIN)
Expand Down Expand Up @@ -37,10 +37,8 @@ Animation::Animation(int duration,
}

Animation::~Animation() {
}

void Animation::Reset() {
start_time_ = Time::Now();
if (animating_)
container_->Stop(this);
}

double Animation::GetCurrentValue() const {
Expand Down Expand Up @@ -69,20 +67,25 @@ gfx::Rect Animation::CurrentValueBetween(const gfx::Rect& start_bounds,

void Animation::Start() {
if (!animating_) {
start_time_ = Time::Now();
timer_.Start(timer_interval_, this, &Animation::Run);
if (!container_.get())
container_ = new AnimationContainer();

animating_ = true;

container_->Start(this);

if (delegate_)
delegate_->AnimationStarted(this);
}
}

void Animation::Stop() {
if (animating_) {
timer_.Stop();

animating_ = false;

// Notify the container first as the delegate may delete us.
container_->Stop(this);

if (delegate_) {
if (state_ >= 1.0)
delegate_->AnimationEnded(this);
Expand All @@ -94,9 +97,11 @@ void Animation::Stop() {

void Animation::End() {
if (animating_) {
timer_.Stop();

animating_ = false;

// Notify the container first as the delegate may delete us.
container_->Stop(this);

AnimateToState(1.0);
if (delegate_)
delegate_->AnimationEnded(this);
Expand All @@ -111,34 +116,8 @@ void Animation::SetDuration(int duration) {
duration_ = TimeDelta::FromMilliseconds(duration);
if (duration_ < timer_interval_)
duration_ = timer_interval_;
start_time_ = Time::Now();
}

void Animation::Step() {
TimeDelta elapsed_time = Time::Now() - start_time_;
state_ = static_cast<double>(elapsed_time.InMicroseconds()) /
static_cast<double>(duration_.InMicroseconds());

if (state_ >= 1.0)
state_ = 1.0;

AnimateToState(state_);
if (delegate_)
delegate_->AnimationProgressed(this);

if (state_ == 1.0)
Stop();
}

void Animation::Run() {
Step();
}

TimeDelta Animation::CalculateInterval(int frame_rate) {
int timer_interval = 1000000 / frame_rate;
if (timer_interval < 10000)
timer_interval = 10000;
return TimeDelta::FromMicroseconds(timer_interval);
if (animating_)
start_time_ = container_->last_tick_time();
}

// static
Expand All @@ -160,3 +139,41 @@ bool Animation::ShouldRenderRichAnimation() {
return true;
}

void Animation::SetContainer(AnimationContainer* container) {
if (container == container_.get())
return;

if (animating_)
container_->Stop(this);

if (container)
container_ = container;
else
container_ = new AnimationContainer();

if (animating_)
container_->Start(this);
}

void Animation::Step(base::TimeTicks time_now) {
TimeDelta elapsed_time = time_now - start_time_;
state_ = static_cast<double>(elapsed_time.InMicroseconds()) /
static_cast<double>(duration_.InMicroseconds());
if (state_ >= 1.0)
state_ = 1.0;

AnimateToState(state_);

if (delegate_)
delegate_->AnimationProgressed(this);

if (state_ == 1.0)
Stop();
}

TimeDelta Animation::CalculateInterval(int frame_rate) {
int timer_interval = 1000000 / frame_rate;
if (timer_interval < 10000)
timer_interval = 10000;
return TimeDelta::FromMicroseconds(timer_interval);
}
34 changes: 21 additions & 13 deletions app/animation.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Inspired by NSAnimation

#ifndef APP_ANIMATION_H_
#define APP_ANIMATION_H_

#include "base/ref_counted.h"
#include "base/time.h"
#include "base/timer.h"

class Animation;
class AnimationContainer;

namespace gfx {
class Rect;
Expand Down Expand Up @@ -69,9 +70,6 @@ class Animation {
Animation(int duration, int frame_rate, AnimationDelegate* delegate);
virtual ~Animation();

// Reset state so that the animation can be started again.
virtual void Reset();

// Called when the animation progresses. Subclasses override this to
// efficiently update their state.
virtual void AnimateToState(double state) = 0;
Expand Down Expand Up @@ -112,13 +110,27 @@ class Animation {
// Sets the delegate.
void set_delegate(AnimationDelegate* delegate) { delegate_ = delegate; }

// Sets the container used to manage the timer. A value of NULL results in
// creating a new AnimationContainer.
void SetContainer(AnimationContainer* container);

base::TimeDelta timer_interval() const { return timer_interval_; }

protected:
// Overriddable, called by Run.
virtual void Step();
// Invoked by the AnimationContainer when the animation is running to advance
// the animation. Use |time_now| rather than Time::Now to avoid multiple
// animations running at the same time diverging.
virtual void Step(base::TimeTicks time_now);

// Calculates the timer interval from the constructor list.
base::TimeDelta CalculateInterval(int frame_rate);

private:
friend class AnimationContainer;

// Invoked from AnimationContainer when started.
void set_start_time(base::TimeTicks start_time) { start_time_ = start_time; }

// Whether or not we are currently animating.
bool animating_;

Expand All @@ -129,15 +141,11 @@ class Animation {
// Current state, on a scale from 0.0 to 1.0.
double state_;

base::Time start_time_;
base::TimeTicks start_time_;

AnimationDelegate* delegate_;

base::RepeatingTimer<Animation> timer_;

private:
// Called when the animation's timer expires, calls Step.
void Run();
scoped_refptr<AnimationContainer> container_;

DISALLOW_COPY_AND_ASSIGN(Animation);
};
Expand Down
93 changes: 93 additions & 0 deletions app/animation_container.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "app/animation_container.h"

#include "app/animation.h"

using base::TimeDelta;
using base::TimeTicks;

AnimationContainer::AnimationContainer()
: last_tick_time_(TimeTicks::Now()),
observer_(NULL) {
}

AnimationContainer::~AnimationContainer() {
// The animations own us and stop themselves before being deleted. If
// animations_ is not empty, something is wrong.
DCHECK(animations_.empty());
}

void AnimationContainer::Start(Animation* animation) {
DCHECK(animations_.count(animation) == 0); // Start should only be invoked
// if the animation isn't running.

if (animations_.empty()) {
last_tick_time_ = TimeTicks::Now();
SetMinTimerInterval(animation->timer_interval());
} else if (animation->timer_interval() < min_timer_interval_) {
SetMinTimerInterval(animation->timer_interval());
}

animation->set_start_time(last_tick_time_);
animations_.insert(animation);
}

void AnimationContainer::Stop(Animation* animation) {
DCHECK(animations_.count(animation) > 0); // The animation must be running.

animations_.erase(animation);

if (animations_.empty()) {
timer_.Stop();
if (observer_)
observer_->AnimationContainerEmpty(this);
} else {
TimeDelta min_timer_interval = GetMinInterval();
if (min_timer_interval > min_timer_interval_)
SetMinTimerInterval(min_timer_interval);
}
}

void AnimationContainer::Run() {
TimeTicks current_time = TimeTicks::Now();

last_tick_time_ = current_time;

// Make a copy of the animations to iterate over so that if any animations
// are removed as part of invoking Step there aren't any problems.
Animations animations = animations_;

for (Animations::const_iterator i = animations.begin();
i != animations.end(); ++i) {
// Make sure the animation is still valid.
if (animations_.find(*i) != animations_.end())
(*i)->Step(current_time);
}

if (observer_)
observer_->AnimationContainerProgressed(this);
}

void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) {
// This doesn't take into account how far along current animation is, but that
// shouldn't be a problem for uses of Animation/AnimationContainer.
timer_.Stop();
min_timer_interval_ = delta;
timer_.Start(min_timer_interval_, this, &AnimationContainer::Run);
}

TimeDelta AnimationContainer::GetMinInterval() {
DCHECK(!animations_.empty());

TimeDelta min;
Animations::const_iterator i = animations_.begin();
min = (*i)->timer_interval();
for (++i; i != animations_.end(); ++i) {
if ((*i)->timer_interval() < min)
min = (*i)->timer_interval();
}
return min;
}
Loading

0 comments on commit 059e7a5

Please sign in to comment.