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