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

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: Use vsync ticks to drive overscroll animation 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 {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 Please delete all "static" within this anonymous n
jdduke (slow) 2013/04/23 16:02:13 Done.
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 static const int RECEDE_TIME = 1000;
25
26 // Time it will take before a pulled glow begins receding in ms
27 static const int PULL_TIME = 167;
28
29 // Time it will take in ms for a pulled glow to decay before release
30 static const int PULL_DECAY_TIME = 1000;
31
32 static const float MAX_ALPHA = 1.f;
33 static const float HELD_EDGE_SCALE_Y = .5f;
34
35 static 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 static const float GLOW_IMAGE_ASPECT_RATIO_INVERSE = 0.25f;
41
42 static const float PULL_GLOW_BEGIN = 1.f;
43 static const float PULL_EDGE_BEGIN = 0.6f;
44
45 // Minimum velocity that will be absorbed
46 static const float MIN_VELOCITY = 25.f;
47
48 static 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 static 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 static const int PULL_DISTANCE_GLOW_FACTOR = 7;
57 static const float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
58
59 static const int VELOCITY_EDGE_FACTOR = 8;
60 static 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 static int Depth(const cc::Layer* layer) {
84 int count = 0;
85 while (layer != NULL) {
86 ++count;
87 layer = layer->parent();
88 }
89 return count;
90 }
91
92 static void AttachLayer(scoped_refptr<cc::Layer>& child, cc::Layer* parent) {
93 if (!parent || !child)
94 return;
95
96 if (child->parent() != parent) {
97 child->RemoveFromParent();
98 parent->AddChild(child);
99 }
100 }
101
102 static void DetachLayer(scoped_refptr<cc::Layer>& layer) {
103 if (!layer || !layer->parent())
104 return;
105
106 layer->SetIsDrawable(false);
107 layer->RemoveFromParent();
108 }
109
110 } // namespace
111
112 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
113 scoped_refptr<cc::Layer> glow)
114 : edge_(edge)
115 , glow_(glow)
116 , edge_alpha_(0)
117 , edge_scale_y_(0)
118 , glow_alpha_(0)
119 , glow_scale_y_(0)
120 , edge_alpha_start_(0)
121 , edge_alpha_finish_(0)
122 , edge_scale_y_start_(0)
123 , edge_scale_y_finish_(0)
124 , glow_alpha_start_(0)
125 , glow_alpha_finish_(0)
126 , glow_scale_y_start_(0)
127 , glow_scale_y_finish_(0)
128 , state_(STATE_IDLE)
129 , pull_distance_(0)
130 , base_attachment_depth_(0) {
131 DCHECK(edge_);
132 DCHECK(glow_);
133 base_attachment_depth_ = CurrentAttachmentDepth();
134 }
135
136 EdgeEffect::~EdgeEffect() {
137 Finish();
138 }
139
140 bool EdgeEffect::IsFinished() const {
141 return state_ == STATE_IDLE;
142 }
143
144 void EdgeEffect::Finish() {
145 Detach();
146 state_ = STATE_IDLE;
147 }
148
149 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
150 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
151 return;
152 }
153
154 if (state_ != STATE_PULL) {
155 glow_scale_y_ = PULL_GLOW_BEGIN;
156 }
157 state_ = STATE_PULL;
158
159 start_time_ = current_time;
160 duration_ = base::TimeDelta::FromMilliseconds(PULL_TIME);
161
162 float abs_delta_distance = std::abs(delta_distance);
163 pull_distance_ += delta_distance;
164 float distance = std::abs(pull_distance_);
165
166 edge_alpha_ = edge_alpha_start_ = Clamp(distance, PULL_EDGE_BEGIN, MAX_ALPHA);
167 edge_scale_y_ = edge_scale_y_start_
168 = Clamp(distance * PULL_DISTANCE_EDGE_FACTOR, HELD_EDGE_SCALE_Y, 1.f);
169
170 glow_alpha_ = glow_alpha_start_
171 = std::min(MAX_ALPHA,
172 glow_alpha_ +
173 abs_delta_distance * PULL_DISTANCE_ALPHA_GLOW_FACTOR);
174
175 float glow_change = abs_delta_distance;
176 if (delta_distance > 0 && pull_distance_ < 0) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 No {} here
jdduke (slow) 2013/04/23 16:02:13 Done.
177 glow_change = -glow_change;
178 }
179 if (pull_distance_ == 0) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 No {} here
jdduke (slow) 2013/04/23 16:02:13 Done.
180 glow_scale_y_ = 0;
181 }
182
183 // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
184 glow_scale_y_ = glow_scale_y_start_
185 = Clamp(glow_scale_y_ + glow_change * PULL_DISTANCE_GLOW_FACTOR,
186 0.f, MAX_GLOW_HEIGHT);
187
188 edge_alpha_finish_ = edge_alpha_;
189 edge_scale_y_finish_ = edge_scale_y_;
190 glow_alpha_finish_ = glow_alpha_;
191 glow_scale_y_finish_ = glow_scale_y_;
192 }
193
194 void EdgeEffect::Release(base::TimeTicks current_time) {
195 pull_distance_ = 0;
196
197 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 No {} here
jdduke (slow) 2013/04/23 16:02:13 Done.
198 return;
199 }
200
201 state_ = STATE_RECEDE;
202 edge_alpha_start_ = edge_alpha_;
203 edge_scale_y_start_ = edge_scale_y_;
204 glow_alpha_start_ = glow_alpha_;
205 glow_scale_y_start_ = glow_scale_y_;
206
207 edge_alpha_finish_ = 0.f;
208 edge_scale_y_finish_ = 0.f;
209 glow_alpha_finish_ = 0.f;
210 glow_scale_y_finish_ = 0.f;
211
212 start_time_ = current_time;
213 duration_ = base::TimeDelta::FromMilliseconds(RECEDE_TIME);
214 }
215
216 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
217 state_ = STATE_ABSORB;
218 velocity = std::max(MIN_VELOCITY, std::abs(velocity));
219
220 start_time_ = current_time;
221 // This should never be less than 1 millisecond.
222 duration_ = base::TimeDelta::FromMilliseconds(0.25f + (velocity * 0.03f));
223
224 // The edge should always be at least partially visible, regardless
225 // of velocity.
226 edge_alpha_start_ = 0.f;
227 edge_scale_y_ = edge_scale_y_start_ = 0.f;
228 // The glow depends more on the velocity, and therefore starts out
229 // nearly invisible.
230 glow_alpha_start_ = 0.5f;
231 glow_scale_y_start_ = 0.f;
232
233 // Factor the velocity by 8. Testing on device shows this works best to
234 // reflect the strength of the user's scrolling.
235 edge_alpha_finish_ = Clamp(velocity * VELOCITY_EDGE_FACTOR, 0.f, 1.f);
236 // Edge should never get larger than the size of its asset.
237 edge_scale_y_finish_ = Clamp(velocity * VELOCITY_EDGE_FACTOR,
238 HELD_EDGE_SCALE_Y, 1.f);
239
240 // Growth for the size of the glow should be quadratic to properly
241 // respond
242 // to a user's scrolling speed. The faster the scrolling speed, the more
243 // intense the effect should be for both the size and the saturation.
244 glow_scale_y_finish_
245 = std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
246 // Alpha should change for the glow as well as size.
247 glow_alpha_finish_
248 = Clamp(glow_alpha_start_,
249 velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA);
250 }
251
252 bool EdgeEffect::Update(base::TimeTicks current_time) {
253 const float dt = (current_time - start_time_).InMilliseconds();
254 const float t = std::min(dt / duration_.InMilliseconds(), 1.f);
255 const float interp = Damp(t, 1.f);
256
257 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
258 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
259 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
260 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
261
262 if (t >= 1.f - EPSILON) {
263 switch (state_) {
264 case STATE_ABSORB:
265 state_ = STATE_RECEDE;
266 start_time_ = current_time;
267 duration_ = base::TimeDelta::FromMilliseconds(RECEDE_TIME);
268
269 edge_alpha_start_ = edge_alpha_;
270 edge_scale_y_start_ = edge_scale_y_;
271 glow_alpha_start_ = glow_alpha_;
272 glow_scale_y_start_ = glow_scale_y_;
273
274 // After absorb, the glow and edge should fade to nothing.
aelias_OOO_until_Jul13 2013/04/22 23:46:48 Indent
jdduke (slow) 2013/04/23 16:02:13 Done.
275 edge_alpha_finish_ = 0.f;
276 edge_scale_y_finish_ = 0.f;
277 glow_alpha_finish_ = 0.f;
278 glow_scale_y_finish_ = 0.f;
279 break;
280 case STATE_PULL:
281 state_ = STATE_PULL_DECAY;
282 start_time_ = current_time;
283 duration_ = base::TimeDelta::FromMilliseconds(PULL_DECAY_TIME);
284
285 edge_alpha_start_ = edge_alpha_;
286 edge_scale_y_start_ = edge_scale_y_;
287 glow_alpha_start_ = glow_alpha_;
288 glow_scale_y_start_ = glow_scale_y_;
289
290 // After pull, the glow and edge should fade to nothing.
291 edge_alpha_finish_ = 0.f;
292 edge_scale_y_finish_ = 0.f;
293 glow_alpha_finish_ = 0.f;
294 glow_scale_y_finish_ = 0.f;
295 break;
296 case STATE_PULL_DECAY:
297 {
298 // When receding, we want edge to decrease more slowly
299 // than the glow.
300 float factor = glow_scale_y_finish_ != 0 ?
301 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
302 std::numeric_limits<float>::max();
303 edge_scale_y_ = edge_scale_y_start_ +
304 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
305 state_ = STATE_RECEDE;
306 }
307 break;
308 case STATE_RECEDE:
309 Finish();
310 break;
311 default:
312 break;
313 }
314 }
315
316 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 No {}
jdduke (slow) 2013/04/23 16:02:13 Done.
317 Finish();
318 }
319
320 return !IsFinished();
321 }
322
323 namespace {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 Delete statics here too
jdduke (slow) 2013/04/23 16:02:13 Done.
324 static gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
325 const gfx::SizeF& size, int height) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 gfx::Size
jdduke (slow) 2013/04/23 16:02:13 Done.
326 switch (edge) {
327 default:
328 case EdgeEffect::EDGE_TOP:
329 return gfx::Transform(1, 0, 0, 1, 0, 0);
330 case EdgeEffect::EDGE_LEFT:
331 return gfx::Transform(0, 1, -1, 0,
332 (-size.width() + height) / 2 ,
333 (size.width() - height) / 2);
334 case EdgeEffect::EDGE_BOTTOM:
335 return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
336 case EdgeEffect::EDGE_RIGHT:
337 return gfx::Transform(0, -1, 1, 0,
338 (-size.width() - height) / 2 + size.height(),
339 (size.width() - height) / 2);
340 };
341 }
342
343 static void UpdateLayer(cc::Layer* layer,
344 EdgeEffect::Edge edge,
345 const gfx::SizeF& size,
346 int height,
347 float opacity) {
348 layer->SetIsDrawable(true);
349 layer->SetTransform(ComputeTransform(edge, size, height));
350 layer->SetBounds(gfx::Size(size.width(), height));
351 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
352 }
353 }
354
355 void EdgeEffect::Draw(cc::Layer* parent, const gfx::SizeF& size, Edge edge) {
356 DCHECK(parent);
357
358 if (IsFinished()) {
aelias_OOO_until_Jul13 2013/04/22 23:46:48 No {}
jdduke (slow) 2013/04/23 16:02:13 Done.
359 return;
360 }
361
362 // If additional levels have been added to the layer tree after the animation
aelias_OOO_until_Jul13 2013/04/22 23:46:48 It seems that you're working around a bug in CC he
jdduke (slow) 2013/04/23 16:02:13 Agreed. See my comments in this thread.
363 // started, disable the animation to prevent clipping or transparency issues.
364 if (base_attachment_depth_ != CurrentAttachmentDepth()) {
365 Finish();
366 return;
367 }
368
369 Attach(parent);
370
371 const float dpi_scale =
372 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
373
374 // Glow
375 {
376 gfx::Size glow_image_bounds;
377 float dummy_scale_x, dummy_scale_y;
378 glow_->CalculateContentsScale(1.f, false, &dummy_scale_x, &dummy_scale_y,
379 &glow_image_bounds);
380 const int glow_height = glow_image_bounds.height();
381 const int glow_bottom = (int)(std::min(
aelias_OOO_until_Jul13 2013/04/22 23:46:48 static_cast<int>
jdduke (slow) 2013/04/23 16:02:13 Done.
382 glow_height * glow_scale_y_ * GLOW_IMAGE_ASPECT_RATIO_INVERSE * 0.6f,
383 glow_height * MAX_GLOW_HEIGHT) * dpi_scale + 0.5f);
384 UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
385 }
386
387 // Edge
388 {
389 gfx::Size edge_image_bounds;
390 float dummy_scale_x, dummy_scale_y;
391 edge_->CalculateContentsScale(1.f, false, &dummy_scale_x, &dummy_scale_y,
392 &edge_image_bounds);
393 const int edge_height = edge_image_bounds.height();
394 const int edge_bottom = (int)(edge_height * edge_scale_y_ * dpi_scale);
aelias_OOO_until_Jul13 2013/04/22 23:46:48 static_cast<int>
jdduke (slow) 2013/04/23 16:02:13 Done.
395 UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
396 }
397 }
398
399 void EdgeEffect::Attach(cc::Layer* parent) {
400 AttachLayer(edge_, parent);
401 AttachLayer(glow_, parent);
402 base_attachment_depth_ = CurrentAttachmentDepth();
403 }
404
405 void EdgeEffect::Detach() {
406 DetachLayer(edge_);
407 DetachLayer(glow_);
408 base_attachment_depth_ = CurrentAttachmentDepth();
409 }
410
411 int EdgeEffect::CurrentAttachmentDepth() const {
412 return Depth(edge_.get());
413 }
414
415 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698