1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
// Copyright 2012 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 "config.h"
#include "cc/page_scale_animation.h"
#include "cc/geometry.h"
#include "ui/gfx/rect_f.h"
#include "ui/gfx/vector2d_conversions.h"
#include "ui/gfx/vector2d_f.h"
#include <math.h>
namespace cc {
scoped_ptr<PageScaleAnimation> PageScaleAnimation::create(gfx::Vector2d scrollStart, float pageScaleStart, const gfx::Size& windowSize, const gfx::Size& contentSize, double startTime)
{
return make_scoped_ptr(new PageScaleAnimation(scrollStart, pageScaleStart, windowSize, contentSize, startTime));
}
PageScaleAnimation::PageScaleAnimation(gfx::Vector2d scrollStart, float pageScaleStart, const gfx::Size& windowSize, const gfx::Size& contentSize, double startTime)
: m_scrollStart(scrollStart)
, m_pageScaleStart(pageScaleStart)
, m_windowSize(windowSize)
, m_contentSize(contentSize)
, m_anchorMode(false)
, m_scrollEnd(scrollStart)
, m_pageScaleEnd(pageScaleStart)
, m_startTime(startTime)
, m_duration(0)
{
}
PageScaleAnimation::~PageScaleAnimation()
{
}
void PageScaleAnimation::zoomTo(gfx::Vector2d finalScroll, float finalPageScale, double duration)
{
if (m_pageScaleStart != finalPageScale) {
// For uniform-looking zooming, infer the anchor (point that remains in
// place throughout the zoom) from the start and end rects.
gfx::RectF startRect(gfx::PointAtOffsetFromOrigin(m_scrollStart), m_windowSize);
gfx::RectF endRect(gfx::PointAtOffsetFromOrigin(finalScroll), m_windowSize);
endRect.Scale(m_pageScaleStart / finalPageScale);
// The anchor is the point which is at the same ratio of the sides of
// both startRect and endRect. For example, a zoom-in double-tap to a
// perfectly centered rect will have anchor ratios (0.5, 0.5), while one
// to a rect touching the bottom-right of the screen will have anchor
// ratios (1.0, 1.0). In other words, it obeys the equations:
// anchorX = start_width * ratioX + start_x
// anchorX = end_width * ratioX + end_x
// anchorY = start_height * ratioY + start_y
// anchorY = end_height * ratioY + end_y
// where both anchor{x,y} and ratio{x,y} begin as unknowns. Solving
// for the ratios, we get the following formulas:
float ratioX = (startRect.x() - endRect.x()) / (endRect.width() - startRect.width());
float ratioY = (startRect.y() - endRect.y()) / (endRect.height() - startRect.height());
gfx::Vector2d anchor(m_windowSize.width() * ratioX, m_windowSize.height() * ratioY);
zoomWithAnchor(anchor, finalPageScale, duration);
} else {
// If this is a pure translation, then there exists no anchor. Linearly
// interpolate the scroll offset instead.
m_scrollEnd = finalScroll;
m_pageScaleEnd = finalPageScale;
m_duration = duration;
m_anchorMode = false;
}
}
void PageScaleAnimation::zoomWithAnchor(gfx::Vector2d anchor, float finalPageScale, double duration)
{
m_scrollEnd = m_scrollStart + anchor;
m_scrollEnd = gfx::ToFlooredVector2d(cc::ScaleVector2d(m_scrollEnd, finalPageScale / m_pageScaleStart));
m_scrollEnd -= anchor;
m_scrollEnd = ClampFromBelow(m_scrollEnd, gfx::Vector2d());
gfx::SizeF scaledContentSize = m_contentSize.Scale(finalPageScale / m_pageScaleStart);
gfx::Vector2d maxScrollOffset = gfx::ToRoundedVector2d(BottomRight(gfx::RectF(scaledContentSize)) - BottomRight(gfx::Rect(m_windowSize)));
m_scrollEnd = m_scrollEnd;
m_scrollEnd = ClampFromAbove(m_scrollEnd, maxScrollOffset);
m_anchor = anchor;
m_pageScaleEnd = finalPageScale;
m_duration = duration;
m_anchorMode = true;
}
gfx::Vector2d PageScaleAnimation::scrollOffsetAtTime(double time) const
{
return scrollOffsetAtRatio(progressRatioForTime(time));
}
float PageScaleAnimation::pageScaleAtTime(double time) const
{
return pageScaleAtRatio(progressRatioForTime(time));
}
bool PageScaleAnimation::isAnimationCompleteAtTime(double time) const
{
return time >= endTime();
}
float PageScaleAnimation::progressRatioForTime(double time) const
{
if (isAnimationCompleteAtTime(time))
return 1;
return (time - m_startTime) / m_duration;
}
gfx::Vector2d PageScaleAnimation::scrollOffsetAtRatio(float ratio) const
{
if (ratio <= 0)
return m_scrollStart;
if (ratio >= 1)
return m_scrollEnd;
float currentPageScale = pageScaleAtRatio(ratio);
gfx::Vector2d currentScrollOffset;
if (m_anchorMode) {
// Keep the anchor stable on the screen at the current scale.
gfx::Vector2dF documentAnchor = m_scrollStart + m_anchor;
documentAnchor.Scale(currentPageScale / m_pageScaleStart);
currentScrollOffset = gfx::ToRoundedVector2d(documentAnchor - m_anchor);
} else {
// First move both scroll offsets to the current coordinate space.
gfx::Vector2dF scaledStartScroll(m_scrollStart);
scaledStartScroll.Scale(currentPageScale / m_pageScaleStart);
gfx::Vector2dF scaledEndScroll(m_scrollEnd);
scaledEndScroll.Scale(currentPageScale / m_pageScaleEnd);
// Linearly interpolate between them.
gfx::Vector2dF delta = scaledEndScroll - scaledStartScroll;
delta.Scale(ratio);
currentScrollOffset = gfx::ToRoundedVector2d(scaledStartScroll + delta);
}
return currentScrollOffset;
}
float PageScaleAnimation::pageScaleAtRatio(float ratio) const
{
if (ratio <= 0)
return m_pageScaleStart;
if (ratio >= 1)
return m_pageScaleEnd;
// Linearly interpolate the magnitude in log scale.
// Log scale is needed to maintain the appearance of uniform zoom. For
// example, if we zoom from 0.5 to 4.0 in 3 seconds, then we should
// be zooming by 2x every second.
float diff = m_pageScaleEnd / m_pageScaleStart;
float logDiff = log(diff);
logDiff *= ratio;
diff = exp(logDiff);
return m_pageScaleStart * diff;
}
} // namespace cc
|