Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(561)

Side by Side Diff: content/browser/android/edge_effect.cc

Issue 14268004: Add overscroll edge effect animations for Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review. Properly disable animation on loss of focus. Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "content/browser/android/edge_effect.h"
6
7 #include "cc/layers/layer.h"
8 #include "ui/gfx/screen.h"
9 #include "ui/gfx/size_f.h"
10
11 namespace content {
12
13 namespace {
14
15 enum State {
16 STATE_IDLE = 0,
17 STATE_PULL,
18 STATE_ABSORB,
19 STATE_RECEDE,
20 STATE_PULL_DECAY
21 };
22
23 // Time it will take the effect to fully recede in ms
24 const int RECEDE_TIME = 1000;
Sami 2013/04/24 13:15:55 nit: Constants like this should be of the form con
jdduke (slow) 2013/04/24 15:23:44 Hmm. Typically, yes, though I'm somewhat hesitant
25
26 // Time it will take before a pulled glow begins receding in ms
27 const int PULL_TIME = 167;
28
29 // Time it will take in ms for a pulled glow to decay before release
30 const int PULL_DECAY_TIME = 1000;
31
32 const float MAX_ALPHA = 1.f;
33 const float HELD_EDGE_SCALE_Y = .5f;
34
35 const float MAX_GLOW_HEIGHT = 4.f;
36
37 // Note: The Android version computes the aspect ratio from the source texture;
38 // because we use rescaled images, this is precomputed from the original Android
39 // textures.
40 const float GLOW_IMAGE_ASPECT_RATIO_INVERSE = 0.25f;
41
42 const float PULL_GLOW_BEGIN = 1.f;
43 const float PULL_EDGE_BEGIN = 0.6f;
44
45 // Minimum velocity that will be absorbed
46 const float MIN_VELOCITY = 25.f;
47
48 const float EPSILON = 0.001f;
49
50 // How much dragging should effect the height of the edge image.
51 // Number determined by user testing.
52 const int PULL_DISTANCE_EDGE_FACTOR = 7;
53
54 // How much dragging should effect the height of the glow image.
55 // Number determined by user testing.
56 const int PULL_DISTANCE_GLOW_FACTOR = 7;
57 const float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
58
59 const int VELOCITY_EDGE_FACTOR = 8;
60 const int VELOCITY_GLOW_FACTOR = 16;
61
62 template <typename T>
63 T Lerp(T a, T b, T t) {
64 return a + (b - a) * t;
65 }
66
67 template <typename T>
68 T Clamp(T value, T low, T high) {
69 return value < low ? low : (value > high ? high : value);
70 }
71
72 template <typename T>
73 T Damp(T input, T factor) {
74 T result;
75 if (factor == 1) {
76 result = 1 - (1 - input) * (1 - input);
77 } else {
78 result = 1 - std::pow(1 - input, 2 * factor);
79 }
80 return result;
81 }
82
83 void AttachLayer(scoped_refptr<cc::Layer>& child, cc::Layer* parent) {
84 if (!parent || !child)
85 return;
86
87 if (child->parent() != parent) {
88 child->RemoveFromParent();
89 parent->AddChild(child);
90 }
91 }
92
93 void DetachLayer(scoped_refptr<cc::Layer>& layer) {
94 if (!layer)
95 return;
96
97 layer->SetIsDrawable(false);
98 layer->RemoveFromParent();
99 }
100
101 } // namespace
102
103 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
104 scoped_refptr<cc::Layer> glow)
105 : edge_(edge)
106 , glow_(glow)
107 , edge_alpha_(0)
108 , edge_scale_y_(0)
109 , glow_alpha_(0)
110 , glow_scale_y_(0)
111 , edge_alpha_start_(0)
112 , edge_alpha_finish_(0)
113 , edge_scale_y_start_(0)
114 , edge_scale_y_finish_(0)
115 , glow_alpha_start_(0)
116 , glow_alpha_finish_(0)
117 , glow_scale_y_start_(0)
118 , glow_scale_y_finish_(0)
119 , state_(STATE_IDLE)
120 , pull_distance_(0) {
121 DCHECK(edge_);
122 DCHECK(glow_);
123 }
124
125 EdgeEffect::~EdgeEffect() {
126 Finish();
127 }
128
129 bool EdgeEffect::IsFinished() const {
130 return state_ == STATE_IDLE;
131 }
132
133 void EdgeEffect::Finish() {
134 Detach();
135 pull_distance_ = 0;
136 state_ = STATE_IDLE;
137 }
138
139 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
140 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
141 return;
142 }
143 if (state_ != STATE_PULL) {
144 glow_scale_y_ = PULL_GLOW_BEGIN;
145 }
146 state_ = STATE_PULL;
147
148 start_time_ = current_time;
149 duration_ = base::TimeDelta::FromMilliseconds(PULL_TIME);
150
151 float abs_delta_distance = std::abs(delta_distance);
152 pull_distance_ += delta_distance;
153 float distance = std::abs(pull_distance_);
154
155 edge_alpha_ = edge_alpha_start_ = Clamp(distance, PULL_EDGE_BEGIN, MAX_ALPHA);
156 edge_scale_y_ = edge_scale_y_start_
157 = Clamp(distance * PULL_DISTANCE_EDGE_FACTOR, HELD_EDGE_SCALE_Y, 1.f);
158
159 glow_alpha_ = glow_alpha_start_
160 = std::min(MAX_ALPHA,
161 glow_alpha_ +
162 abs_delta_distance * PULL_DISTANCE_ALPHA_GLOW_FACTOR);
163
164 float glow_change = abs_delta_distance;
165 if (delta_distance > 0 && pull_distance_ < 0)
166 glow_change = -glow_change;
167 if (pull_distance_ == 0)
168 glow_scale_y_ = 0;
169
170 // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
171 glow_scale_y_ = glow_scale_y_start_
172 = Clamp(glow_scale_y_ + glow_change * PULL_DISTANCE_GLOW_FACTOR,
173 0.f, MAX_GLOW_HEIGHT);
174
175 edge_alpha_finish_ = edge_alpha_;
176 edge_scale_y_finish_ = edge_scale_y_;
177 glow_alpha_finish_ = glow_alpha_;
178 glow_scale_y_finish_ = glow_scale_y_;
179 }
180
181 void EdgeEffect::Release(base::TimeTicks current_time) {
182 pull_distance_ = 0;
183
184 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
185 return;
186
187 state_ = STATE_RECEDE;
188 edge_alpha_start_ = edge_alpha_;
189 edge_scale_y_start_ = edge_scale_y_;
190 glow_alpha_start_ = glow_alpha_;
191 glow_scale_y_start_ = glow_scale_y_;
192
193 edge_alpha_finish_ = 0.f;
194 edge_scale_y_finish_ = 0.f;
195 glow_alpha_finish_ = 0.f;
196 glow_scale_y_finish_ = 0.f;
197
198 start_time_ = current_time;
199 duration_ = base::TimeDelta::FromMilliseconds(RECEDE_TIME);
200 }
201
202 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
203 state_ = STATE_ABSORB;
204 velocity = std::max(MIN_VELOCITY, std::abs(velocity));
205
206 start_time_ = current_time;
207 // This should never be less than 1 millisecond.
208 duration_ = base::TimeDelta::FromMilliseconds(0.25f + (velocity * 0.03f));
209
210 // The edge should always be at least partially visible, regardless
211 // of velocity.
212 edge_alpha_start_ = 0.f;
213 edge_scale_y_ = edge_scale_y_start_ = 0.f;
214 // The glow depends more on the velocity, and therefore starts out
215 // nearly invisible.
216 glow_alpha_start_ = 0.5f;
217 glow_scale_y_start_ = 0.f;
218
219 // Factor the velocity by 8. Testing on device shows this works best to
220 // reflect the strength of the user's scrolling.
221 edge_alpha_finish_ = Clamp(velocity * VELOCITY_EDGE_FACTOR, 0.f, 1.f);
222 // Edge should never get larger than the size of its asset.
223 edge_scale_y_finish_ = Clamp(velocity * VELOCITY_EDGE_FACTOR,
224 HELD_EDGE_SCALE_Y, 1.f);
225
226 // Growth for the size of the glow should be quadratic to properly
227 // respond
228 // to a user's scrolling speed. The faster the scrolling speed, the more
229 // intense the effect should be for both the size and the saturation.
230 glow_scale_y_finish_
231 = std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
232 // Alpha should change for the glow as well as size.
233 glow_alpha_finish_
234 = Clamp(glow_alpha_start_,
235 velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA);
236 }
237
238 bool EdgeEffect::Update(base::TimeTicks current_time) {
239 if (IsFinished())
240 return false;
241
242 const double dt = (current_time - start_time_).InMilliseconds();
243 const double t = std::min(dt / duration_.InMilliseconds(), 1.);
244 const float interp = static_cast<float>(Damp(t, 1.));
245
246 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
247 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
248 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
249 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
250
251 if (t >= 1.f - EPSILON) {
252 switch (state_) {
253 case STATE_ABSORB:
254 state_ = STATE_RECEDE;
255 start_time_ = current_time;
256 duration_ = base::TimeDelta::FromMilliseconds(RECEDE_TIME);
257
258 edge_alpha_start_ = edge_alpha_;
259 edge_scale_y_start_ = edge_scale_y_;
260 glow_alpha_start_ = glow_alpha_;
261 glow_scale_y_start_ = glow_scale_y_;
262
263 // After absorb, the glow and edge should fade to nothing.
264 edge_alpha_finish_ = 0.f;
265 edge_scale_y_finish_ = 0.f;
266 glow_alpha_finish_ = 0.f;
267 glow_scale_y_finish_ = 0.f;
268 break;
269 case STATE_PULL:
270 state_ = STATE_PULL_DECAY;
271 start_time_ = current_time;
272 duration_ = base::TimeDelta::FromMilliseconds(PULL_DECAY_TIME);
273
274 edge_alpha_start_ = edge_alpha_;
275 edge_scale_y_start_ = edge_scale_y_;
276 glow_alpha_start_ = glow_alpha_;
277 glow_scale_y_start_ = glow_scale_y_;
278
279 // After pull, the glow and edge should fade to nothing.
280 edge_alpha_finish_ = 0.f;
281 edge_scale_y_finish_ = 0.f;
282 glow_alpha_finish_ = 0.f;
283 glow_scale_y_finish_ = 0.f;
284 break;
285 case STATE_PULL_DECAY:
286 {
287 // When receding, we want edge to decrease more slowly
288 // than the glow.
289 float factor = glow_scale_y_finish_ != 0 ?
290 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
291 std::numeric_limits<float>::max();
292 edge_scale_y_ = edge_scale_y_start_ +
293 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
294 state_ = STATE_RECEDE;
295 }
296 break;
297 case STATE_RECEDE:
298 Finish();
299 break;
300 default:
301 break;
302 }
303 }
304
305 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
306 Finish();
307
308 return !IsFinished();
309 }
310
311 namespace {
312 gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
313 gfx::SizeF size, int height) {
314 switch (edge) {
315 default:
316 case EdgeEffect::EDGE_TOP:
317 return gfx::Transform(1, 0, 0, 1, 0, 0);
318 case EdgeEffect::EDGE_LEFT:
319 return gfx::Transform(0, 1, -1, 0,
320 (-size.width() + height) / 2 ,
321 (size.width() - height) / 2);
322 case EdgeEffect::EDGE_BOTTOM:
323 return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
324 case EdgeEffect::EDGE_RIGHT:
325 return gfx::Transform(0, -1, 1, 0,
326 (-size.width() - height) / 2 + size.height(),
327 (size.width() - height) / 2);
328 };
329 }
330
331 void UpdateLayer(cc::Layer* layer,
332 EdgeEffect::Edge edge,
333 gfx::SizeF size,
334 int height,
335 float opacity) {
336 layer->SetIsDrawable(true);
337 layer->SetTransform(ComputeTransform(edge, size, height));
338 layer->SetBounds(gfx::Size(size.width(), height));
339 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
340 }
341 }
Sami 2013/04/24 13:15:55 nit: "} // namespace" so it's clear what this bra
jdduke (slow) 2013/04/24 15:23:44 Done.
342
343 void EdgeEffect::Draw(cc::Layer* parent, gfx::SizeF size, Edge edge) {
344 if (IsFinished())
345 return;
346
347 Attach(parent);
348
349 const float dpi_scale =
350 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
351
352 // Glow
353 {
354 gfx::Size glow_image_bounds;
355 float dummy_scale_x, dummy_scale_y;
356 glow_->CalculateContentsScale(1.f, false, &dummy_scale_x, &dummy_scale_y,
357 &glow_image_bounds);
358 const int glow_height = glow_image_bounds.height();
359 const int glow_bottom = static_cast<int>(std::min(
360 glow_height * glow_scale_y_ * GLOW_IMAGE_ASPECT_RATIO_INVERSE * 0.6f,
361 glow_height * MAX_GLOW_HEIGHT) * dpi_scale + 0.5f);
362 UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
363 }
364
365 // Edge
366 {
367 gfx::Size edge_image_bounds;
368 float dummy_scale_x, dummy_scale_y;
369 edge_->CalculateContentsScale(1.f, false, &dummy_scale_x, &dummy_scale_y,
370 &edge_image_bounds);
371 const int edge_height = edge_image_bounds.height();
372 const int edge_bottom = static_cast<int>(
373 edge_height * edge_scale_y_ * dpi_scale);
374 UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
375 }
376 }
377
378 void EdgeEffect::Attach(cc::Layer* parent) {
379 DCHECK(parent);
380 AttachLayer(edge_, parent);
381 AttachLayer(glow_, parent);
382 }
383
384 void EdgeEffect::Detach() {
385 DetachLayer(edge_);
386 DetachLayer(glow_);
387 }
388
389 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698