Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 import 'dart:sky' as sky; | |
| 6 | |
| 7 import 'package:vector_math/vector_math.dart'; | |
| 8 import 'package:sky/animation/animation_performance.dart'; | |
| 9 import 'package:sky/widgets/animation_builder.dart'; | |
| 10 import 'package:sky/widgets/basic.dart'; | |
| 11 import 'package:sky/widgets/widget.dart'; | |
| 12 | |
| 13 const int _kCardDismissFadeoutMS = 300; | |
| 14 const double _kMinFlingVelocity = 700.0; | |
| 15 const double _kMinFlingVelocityDelta = 400.0; | |
| 16 const double _kDismissCardThreshold = 0.6; | |
| 17 | |
| 18 typedef void DismissedCallback(); | |
| 19 | |
| 20 class Dismissable extends StatefulComponent { | |
|
abarth-chromium
2015/07/13 19:20:17
Don't we want to subclass AnimatedComponent?
hansmuller
2015/07/13 19:34:38
I hadn't noticed that. Yes.
| |
| 21 | |
| 22 Dismissable({ | |
| 23 String key, | |
| 24 this.child, | |
| 25 this.onDismissed | |
| 26 // TODO(hansmuller): direction | |
| 27 }) : super(key: key); | |
| 28 | |
| 29 Widget child; | |
| 30 DismissedCallback onDismissed; | |
| 31 | |
| 32 AnimationBuilder _transform; | |
| 33 AnimationPerformance _performance; | |
| 34 double _width; | |
| 35 double _dragX = 0.0; | |
| 36 bool _dragUnderway = false; | |
| 37 | |
| 38 void initState() { | |
| 39 _transform = new AnimationBuilder() | |
| 40 ..position = new AnimatedType<Point>(Point.origin) | |
| 41 ..opacity = new AnimatedType<double>(1.0, end: 0.0); | |
| 42 | |
| 43 _performance = _transform.createPerformance( | |
| 44 [_transform.position, _transform.opacity], | |
| 45 duration: new Duration(milliseconds: _kCardDismissFadeoutMS)); | |
| 46 _performance.addListener(_handleAnimationProgressChanged); | |
|
abarth-chromium
2015/07/13 19:20:16
watchPerformance(_performance)
That handles addin
hansmuller
2015/07/13 19:34:38
Done.
| |
| 47 } | |
| 48 | |
| 49 void syncFields(Dismissable source) { | |
| 50 child = source.child; | |
| 51 onDismissed = source.onDismissed; | |
| 52 } | |
| 53 | |
| 54 Point get _activeCardDragEndPoint { | |
| 55 assert(_width != null); | |
| 56 return new Point(_dragX.sign * _width * _kDismissCardThreshold, 0.0); | |
| 57 } | |
| 58 | |
| 59 bool get _isActive { | |
| 60 return _width != null && (_dragUnderway || _performance.isAnimating); | |
| 61 } | |
| 62 | |
| 63 void _maybeCallOnDismissed() { | |
| 64 if (onDismissed != null) | |
| 65 onDismissed(); | |
| 66 } | |
| 67 | |
| 68 void _handleAnimationProgressChanged() { | |
| 69 setState(() { | |
| 70 if (_performance.isCompleted && !_dragUnderway) | |
| 71 _maybeCallOnDismissed(); | |
| 72 }); | |
| 73 } | |
| 74 | |
| 75 void _handleSizeChanged(Size newSize) { | |
| 76 _width = newSize.width; | |
| 77 _transform.position.end = _activeCardDragEndPoint; | |
| 78 } | |
| 79 | |
| 80 void _handlePointerDown(sky.PointerEvent event) { | |
| 81 setState(() { | |
| 82 _dragUnderway = true; | |
| 83 _dragX = 0.0; | |
| 84 _performance.progress = 0.0; | |
| 85 }); | |
| 86 } | |
| 87 | |
| 88 void _handlePointerMove(sky.PointerEvent event) { | |
| 89 if (!_isActive) | |
| 90 return; | |
| 91 | |
| 92 double oldDragX = _dragX; | |
| 93 _dragX += event.dx; | |
| 94 setState(() { | |
| 95 if (!_performance.isAnimating) { | |
| 96 if (oldDragX.sign != _dragX.sign) | |
| 97 _transform.position.end = _activeCardDragEndPoint; | |
| 98 _performance.progress = _dragX.abs() / (_width * _kDismissCardThreshold) ; | |
| 99 } | |
| 100 }); | |
| 101 } | |
| 102 | |
| 103 void _handlePointerUpOrCancel(_) { | |
| 104 if (!_isActive) | |
| 105 return; | |
| 106 | |
| 107 setState(() { | |
| 108 _dragUnderway = false; | |
| 109 if (_performance.isCompleted) | |
| 110 _maybeCallOnDismissed(); | |
| 111 else if (!_performance.isAnimating) | |
| 112 _performance.progress = 0.0; | |
| 113 }); | |
| 114 } | |
| 115 | |
| 116 bool _isHorizontalFlingGesture(sky.GestureEvent event) { | |
| 117 double vx = event.velocityX.abs(); | |
| 118 double vy = event.velocityY.abs(); | |
| 119 return vx - vy > _kMinFlingVelocityDelta && vx > _kMinFlingVelocity; | |
| 120 } | |
| 121 | |
| 122 void _handleFlingStart(sky.GestureEvent event) { | |
| 123 if (!_isActive) | |
| 124 return; | |
| 125 | |
| 126 if (_isHorizontalFlingGesture(event)) { | |
| 127 _dragUnderway = false; | |
| 128 double distance = 1.0 - _performance.progress; | |
| 129 if (distance > 0.0) { | |
| 130 double duration = _kCardDismissFadeoutMS * 1000.0 * distance / event.vel ocityX.abs(); | |
| 131 _dragX = event.velocityX.sign; | |
| 132 _performance.timeline.animateTo(1.0, duration: duration); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 Widget build() { | |
| 138 // TODO(hansmuller) The code below changes the widget tree when | |
| 139 // the user starts dragging. Currently this causes Sky to drop the | |
| 140 // rest of the pointer gesture, see https://github.com/domokit/mojo/issues/3 12. | |
| 141 // As a workaround, always create the Transform and Opacity nodes. | |
| 142 Widget transformedChild = child; | |
| 143 if (_isActive) { | |
| 144 transformedChild = _transform.build(transformedChild); | |
| 145 } else { | |
| 146 transformedChild = new Transform(child: transformedChild, transform: new M atrix4.identity()); | |
| 147 transformedChild = new Opacity(child: transformedChild, opacity: 1.0); | |
| 148 } | |
| 149 | |
| 150 return new Listener( | |
| 151 onPointerDown: _handlePointerDown, | |
| 152 onPointerMove: _handlePointerMove, | |
| 153 onPointerUp: _handlePointerUpOrCancel, | |
| 154 onPointerCancel: _handlePointerUpOrCancel, | |
| 155 onGestureFlingStart: _handleFlingStart, | |
| 156 child: new SizeObserver( | |
| 157 child: transformedChild, | |
| 158 callback: _handleSizeChanged | |
| 159 ) | |
| 160 ); | |
| 161 } | |
| 162 } | |
| OLD | NEW |