1 // Copyright (c) 2013 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 "content/browser/android/overscroll_glow.h"
7 #include "cc/layers/layer.h"
8 #include "content/browser/android/edge_effect_base.h"
9 #include "content/public/browser/android/compositor.h"
18 const float kEpsilon
= 1e-3f
;
20 bool IsApproxZero(float value
) {
21 return std::abs(value
) < kEpsilon
;
24 gfx::Vector2dF
ZeroSmallComponents(gfx::Vector2dF vector
) {
25 if (IsApproxZero(vector
.x()))
27 if (IsApproxZero(vector
.y()))
34 OverscrollGlow::OverscrollGlow(OverscrollGlowClient
* client
)
38 allow_horizontal_overscroll_(true),
39 allow_vertical_overscroll_(true) {
43 OverscrollGlow::~OverscrollGlow() {
47 void OverscrollGlow::Reset() {
51 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
52 edge_effects_
[i
]->Finish();
55 bool OverscrollGlow::IsActive() const {
58 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
59 if (!edge_effects_
[i
]->IsFinished())
65 float OverscrollGlow::GetVisibleAlpha() const {
67 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
68 if (!edge_effects_
[i
]->IsFinished())
69 max_alpha
= std::max(max_alpha
, edge_effects_
[i
]->GetAlpha());
71 return std::min(max_alpha
, 1.f
);
74 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time
,
75 const gfx::Vector2dF
& accumulated_overscroll
,
76 gfx::Vector2dF overscroll_delta
,
77 gfx::Vector2dF velocity
,
78 const gfx::Vector2dF
& overscroll_location
) {
79 // The size of the glow determines the relative effect of the inputs; an
80 // empty-sized effect is effectively disabled.
81 if (viewport_size_
.IsEmpty())
84 if (!allow_horizontal_overscroll_
) {
85 overscroll_delta
.set_x(0);
88 if (!allow_vertical_overscroll_
) {
89 overscroll_delta
.set_y(0);
93 // Ignore sufficiently small values that won't meaningfuly affect animation.
94 overscroll_delta
= ZeroSmallComponents(overscroll_delta
);
95 if (overscroll_delta
.IsZero()) {
97 Release(current_time
);
98 return CheckNeedsAnimate();
101 if (!InitializeIfNecessary())
104 gfx::Vector2dF old_overscroll
= accumulated_overscroll
- overscroll_delta
;
105 bool x_overscroll_started
=
106 !IsApproxZero(overscroll_delta
.x()) && IsApproxZero(old_overscroll
.x());
107 bool y_overscroll_started
=
108 !IsApproxZero(overscroll_delta
.y()) && IsApproxZero(old_overscroll
.y());
110 velocity
= ZeroSmallComponents(velocity
);
111 if (!velocity
.IsZero())
112 Absorb(current_time
, velocity
, x_overscroll_started
, y_overscroll_started
);
114 Pull(current_time
, overscroll_delta
, overscroll_location
);
116 return CheckNeedsAnimate();
119 bool OverscrollGlow::Animate(base::TimeTicks current_time
,
120 cc::Layer
* parent_layer
) {
121 DCHECK(parent_layer
);
122 if (!CheckNeedsAnimate())
125 UpdateLayerAttachment(parent_layer
);
127 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
128 if (edge_effects_
[i
]->Update(current_time
)) {
129 EdgeEffectBase::Edge edge
= static_cast<EdgeEffectBase::Edge
>(i
);
130 edge_effects_
[i
]->ApplyToLayers(edge
, viewport_size_
, edge_offsets_
[i
]);
134 return CheckNeedsAnimate();
137 void OverscrollGlow::OnFrameUpdated(
138 const gfx::SizeF
& viewport_size
,
139 const gfx::SizeF
& content_size
,
140 const gfx::Vector2dF
& content_scroll_offset
) {
141 viewport_size_
= viewport_size
;
142 edge_offsets_
[EdgeEffectBase::EDGE_TOP
] = -content_scroll_offset
.y();
143 edge_offsets_
[EdgeEffectBase::EDGE_LEFT
] = -content_scroll_offset
.x();
144 edge_offsets_
[EdgeEffectBase::EDGE_BOTTOM
] = content_size
.height() -
145 content_scroll_offset
.y() -
146 viewport_size
.height();
147 edge_offsets_
[EdgeEffectBase::EDGE_RIGHT
] =
148 content_size
.width() - content_scroll_offset
.x() - viewport_size
.width();
150 // Only allow overscroll on scrollable axes, matching platform behavior.
151 allow_horizontal_overscroll_
=
152 std::ceil(viewport_size_
.width()) < std::floor(content_size
.width());
153 allow_vertical_overscroll_
=
154 std::ceil(viewport_size_
.height()) < std::floor(content_size
.height());
157 bool OverscrollGlow::CheckNeedsAnimate() {
170 void OverscrollGlow::UpdateLayerAttachment(cc::Layer
* parent
) {
172 if (!root_layer_
.get())
175 if (!CheckNeedsAnimate())
178 if (root_layer_
->parent() != parent
)
179 parent
->AddChild(root_layer_
);
181 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
182 edge_effects_
[i
]->SetParent(root_layer_
.get());
185 void OverscrollGlow::Detach() {
186 if (root_layer_
.get())
187 root_layer_
->RemoveFromParent();
190 bool OverscrollGlow::InitializeIfNecessary() {
194 DCHECK(!root_layer_
.get());
195 root_layer_
= cc::Layer::Create(Compositor::LayerSettings());
196 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
197 edge_effects_
[i
] = client_
->CreateEdgeEffect();
198 DCHECK(edge_effects_
[i
]);
205 void OverscrollGlow::Pull(base::TimeTicks current_time
,
206 const gfx::Vector2dF
& overscroll_delta
,
207 const gfx::Vector2dF
& overscroll_location
) {
208 DCHECK(initialized_
);
209 DCHECK(!overscroll_delta
.IsZero());
210 DCHECK(!viewport_size_
.IsEmpty());
211 const float inv_width
= 1.f
/ viewport_size_
.width();
212 const float inv_height
= 1.f
/ viewport_size_
.height();
214 gfx::Vector2dF overscroll_pull
=
215 gfx::ScaleVector2d(overscroll_delta
, inv_width
, inv_height
);
216 const float edge_pull
[EDGE_COUNT
] = {
217 min(overscroll_pull
.y(), 0.f
), // Top
218 min(overscroll_pull
.x(), 0.f
), // Left
219 max(overscroll_pull
.y(), 0.f
), // Bottom
220 max(overscroll_pull
.x(), 0.f
) // Right
223 gfx::Vector2dF displacement
=
224 gfx::ScaleVector2d(overscroll_location
, inv_width
, inv_height
);
225 displacement
.set_x(max(0.f
, min(1.f
, displacement
.x())));
226 displacement
.set_y(max(0.f
, min(1.f
, displacement
.y())));
227 const float edge_displacement
[EDGE_COUNT
] = {
228 1.f
- displacement
.x(), // Top
229 displacement
.y(), // Left
230 displacement
.x(), // Bottom
231 1.f
- displacement
.y() // Right
234 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
238 edge_effects_
[i
]->Pull(
239 current_time
, std::abs(edge_pull
[i
]), edge_displacement
[i
]);
240 GetOppositeEdge(i
)->Release(current_time
);
244 void OverscrollGlow::Absorb(base::TimeTicks current_time
,
245 const gfx::Vector2dF
& velocity
,
246 bool x_overscroll_started
,
247 bool y_overscroll_started
) {
248 DCHECK(initialized_
);
249 DCHECK(!velocity
.IsZero());
251 // Only trigger on initial overscroll at a non-zero velocity
252 const float overscroll_velocities
[EDGE_COUNT
] = {
253 y_overscroll_started
? min(velocity
.y(), 0.f
) : 0, // Top
254 x_overscroll_started
? min(velocity
.x(), 0.f
) : 0, // Left
255 y_overscroll_started
? max(velocity
.y(), 0.f
) : 0, // Bottom
256 x_overscroll_started
? max(velocity
.x(), 0.f
) : 0 // Right
259 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
260 if (!overscroll_velocities
[i
])
263 edge_effects_
[i
]->Absorb(current_time
, std::abs(overscroll_velocities
[i
]));
264 GetOppositeEdge(i
)->Release(current_time
);
268 void OverscrollGlow::Release(base::TimeTicks current_time
) {
269 DCHECK(initialized_
);
270 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
271 edge_effects_
[i
]->Release(current_time
);
274 EdgeEffectBase
* OverscrollGlow::GetOppositeEdge(int edge_index
) {
275 DCHECK(initialized_
);
276 return edge_effects_
[(edge_index
+ 2) % EDGE_COUNT
].get();
279 } // namespace content