1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/input/page_scale_animation.h"
9 #include "base/logging.h"
10 #include "cc/animation/timing_function.h"
11 #include "ui/gfx/point_f.h"
12 #include "ui/gfx/rect_f.h"
13 #include "ui/gfx/vector2d_conversions.h"
17 // This takes a viewport-relative vector and returns a vector whose values are
18 // between 0 and 1, representing the percentage position within the viewport.
19 gfx::Vector2dF
NormalizeFromViewport(gfx::Vector2dF denormalized
,
20 gfx::SizeF viewport_size
) {
21 return gfx::ScaleVector2d(denormalized
,
22 1.f
/ viewport_size
.width(),
23 1.f
/ viewport_size
.height());
26 gfx::Vector2dF
DenormalizeToViewport(gfx::Vector2dF normalized
,
27 gfx::SizeF viewport_size
) {
28 return gfx::ScaleVector2d(normalized
,
29 viewport_size
.width(),
30 viewport_size
.height());
33 gfx::Vector2dF
InterpolateBetween(gfx::Vector2dF start
,
36 return start
+ gfx::ScaleVector2d(end
- start
, interp
);
43 scoped_ptr
<PageScaleAnimation
> PageScaleAnimation::Create(
44 gfx::Vector2dF start_scroll_offset
,
45 float start_page_scale_factor
,
46 gfx::SizeF viewport_size
,
47 gfx::SizeF root_layer_size
,
49 scoped_ptr
<TimingFunction
> timing_function
) {
50 return make_scoped_ptr(new PageScaleAnimation(start_scroll_offset
,
51 start_page_scale_factor
,
55 timing_function
.Pass()));
58 PageScaleAnimation::PageScaleAnimation(
59 gfx::Vector2dF start_scroll_offset
,
60 float start_page_scale_factor
,
61 gfx::SizeF viewport_size
,
62 gfx::SizeF root_layer_size
,
64 scoped_ptr
<TimingFunction
> timing_function
)
65 : start_page_scale_factor_(start_page_scale_factor
),
66 target_page_scale_factor_(0.f
),
67 start_scroll_offset_(start_scroll_offset
),
70 viewport_size_(viewport_size
),
71 root_layer_size_(root_layer_size
),
72 start_time_(start_time
),
74 timing_function_(timing_function
.Pass()) {}
76 PageScaleAnimation::~PageScaleAnimation() {}
78 void PageScaleAnimation::ZoomTo(gfx::Vector2dF target_scroll_offset
,
79 float target_page_scale_factor
,
81 target_page_scale_factor_
= target_page_scale_factor
;
82 target_scroll_offset_
= target_scroll_offset
;
83 ClampTargetScrollOffset();
86 if (start_page_scale_factor_
== target_page_scale_factor
) {
87 start_anchor_
= start_scroll_offset_
;
88 target_anchor_
= target_scroll_offset
;
92 // For uniform-looking zooming, infer an anchor from the start and target
94 InferTargetAnchorFromScrollOffsets();
95 start_anchor_
= target_anchor_
;
98 void PageScaleAnimation::ZoomWithAnchor(gfx::Vector2dF anchor
,
99 float target_page_scale_factor
,
101 start_anchor_
= anchor
;
102 target_page_scale_factor_
= target_page_scale_factor
;
103 duration_
= duration
;
105 // We start zooming out from the anchor tapped by the user. But if
106 // the target scale is impossible to attain without hitting the root layer
107 // edges, then infer an anchor that doesn't collide with the edges.
108 // We will interpolate between the two anchors during the animation.
109 InferTargetScrollOffsetFromStartAnchor();
110 ClampTargetScrollOffset();
112 if (start_page_scale_factor_
== target_page_scale_factor_
) {
113 target_anchor_
= start_anchor_
;
116 InferTargetAnchorFromScrollOffsets();
119 void PageScaleAnimation::InferTargetScrollOffsetFromStartAnchor() {
120 gfx::Vector2dF normalized
= NormalizeFromViewport(
121 start_anchor_
- start_scroll_offset_
, StartViewportSize());
122 target_scroll_offset_
=
123 start_anchor_
- DenormalizeToViewport(normalized
, TargetViewportSize());
126 void PageScaleAnimation::InferTargetAnchorFromScrollOffsets() {
127 // The anchor is the point which is at the same normalized relative position
128 // within both start viewport rect and target viewport rect. For example, a
129 // zoom-in double-tap to a perfectly centered rect will have normalized
130 // anchor (0.5, 0.5), while one to a rect touching the bottom-right of the
131 // screen will have normalized anchor (1.0, 1.0). In other words, it obeys
133 // anchor = start_size * normalized + start_offset
134 // anchor = target_size * normalized + target_offset
135 // where both anchor and normalized begin as unknowns. Solving
136 // for the normalized, we get the following:
138 1.f
/ (TargetViewportSize().width() - StartViewportSize().width());
140 1.f
/ (TargetViewportSize().height() - StartViewportSize().height());
141 gfx::Vector2dF normalized
= gfx::ScaleVector2d(
142 start_scroll_offset_
- target_scroll_offset_
, width_scale
, height_scale
);
144 target_scroll_offset_
+ DenormalizeToViewport(normalized
,
145 TargetViewportSize());
148 void PageScaleAnimation::ClampTargetScrollOffset() {
149 gfx::Vector2dF max_scroll_offset
=
150 gfx::RectF(root_layer_size_
).bottom_right() -
151 gfx::RectF(TargetViewportSize()).bottom_right();
152 target_scroll_offset_
.SetToMax(gfx::Vector2dF());
153 target_scroll_offset_
.SetToMin(max_scroll_offset
);
156 gfx::SizeF
PageScaleAnimation::StartViewportSize() const {
157 return gfx::ScaleSize(viewport_size_
, 1.f
/ start_page_scale_factor_
);
160 gfx::SizeF
PageScaleAnimation::TargetViewportSize() const {
161 return gfx::ScaleSize(viewport_size_
, 1.f
/ target_page_scale_factor_
);
164 gfx::SizeF
PageScaleAnimation::ViewportSizeAt(float interp
) const {
165 return gfx::ScaleSize(viewport_size_
, 1.f
/ PageScaleFactorAt(interp
));
168 gfx::Vector2dF
PageScaleAnimation::ScrollOffsetAtTime(double time
) const {
169 return ScrollOffsetAt(InterpAtTime(time
));
172 float PageScaleAnimation::PageScaleFactorAtTime(double time
) const {
173 return PageScaleFactorAt(InterpAtTime(time
));
176 bool PageScaleAnimation::IsAnimationCompleteAtTime(double time
) const {
177 return time
>= end_time();
180 float PageScaleAnimation::InterpAtTime(double time
) const {
181 DCHECK_GE(time
, start_time_
);
182 if (IsAnimationCompleteAtTime(time
))
185 const double normalized_time
= (time
- start_time_
) / duration_
;
186 return timing_function_
->GetValue(normalized_time
);
189 gfx::Vector2dF
PageScaleAnimation::ScrollOffsetAt(float interp
) const {
191 return start_scroll_offset_
;
193 return target_scroll_offset_
;
195 return AnchorAt(interp
) - ViewportRelativeAnchorAt(interp
);
198 gfx::Vector2dF
PageScaleAnimation::AnchorAt(float interp
) const {
199 // Interpolate from start to target anchor in absolute space.
200 return InterpolateBetween(start_anchor_
, target_anchor_
, interp
);
203 gfx::Vector2dF
PageScaleAnimation::ViewportRelativeAnchorAt(
204 float interp
) const {
205 // Interpolate from start to target anchor in normalized space.
206 gfx::Vector2dF start_normalized
=
207 NormalizeFromViewport(start_anchor_
- start_scroll_offset_
,
208 StartViewportSize());
209 gfx::Vector2dF target_normalized
=
210 NormalizeFromViewport(target_anchor_
- target_scroll_offset_
,
211 TargetViewportSize());
212 gfx::Vector2dF interp_normalized
=
213 InterpolateBetween(start_normalized
, target_normalized
, interp
);
215 return DenormalizeToViewport(interp_normalized
, ViewportSizeAt(interp
));
218 float PageScaleAnimation::PageScaleFactorAt(float interp
) const {
220 return start_page_scale_factor_
;
222 return target_page_scale_factor_
;
224 // Linearly interpolate the magnitude in log scale.
225 float diff
= target_page_scale_factor_
/ start_page_scale_factor_
;
226 float log_diff
= log(diff
);
228 diff
= exp(log_diff
);
229 return start_page_scale_factor_
* diff
;