OLD | NEW |
| (Empty) |
1 part of widgets; | |
2 | |
3 const double _kWidth = 256.0; | |
4 const double _kMinFlingVelocity = 0.4; | |
5 const double _kBaseSettleDurationMS = 246.0; | |
6 const double _kMaxSettleDurationMS = 600.0; | |
7 const Cubic _kAnimationCurve = easeOut; | |
8 | |
9 class DrawerAnimation extends Animation { | |
10 | |
11 Stream<double> get onPositionChanged => onValueChanged; | |
12 | |
13 bool get _isMostlyClosed => value <= -_kWidth / 2; | |
14 | |
15 DrawerAnimation() { | |
16 value = -_kWidth; | |
17 } | |
18 | |
19 void toggle(_) => _isMostlyClosed ? _open() : _close(); | |
20 | |
21 void handleMaskTap(_) => _close(); | |
22 | |
23 void handlePointerDown(_) => stop(); | |
24 | |
25 void handlePointerMove(sky.PointerEvent event) { | |
26 assert(!isAnimating); | |
27 value = math.min(0.0, math.max(value + event.dx, -_kWidth)); | |
28 } | |
29 | |
30 void handlePointerUp(_) { | |
31 if (!isAnimating) | |
32 _settle(); | |
33 } | |
34 | |
35 void handlePointerCancel(_) { | |
36 if (!isAnimating) | |
37 _settle(); | |
38 } | |
39 | |
40 void _open() => _animateToPosition(0.0); | |
41 | |
42 void _close() => _animateToPosition(-_kWidth); | |
43 | |
44 void _settle() => _isMostlyClosed ? _close() : _open(); | |
45 | |
46 void _animateToPosition(double targetPosition) { | |
47 double distance = (targetPosition - value).abs(); | |
48 if (distance != 0) { | |
49 double targetDuration = distance / _kWidth * _kBaseSettleDurationMS; | |
50 double duration = math.min(targetDuration, _kMaxSettleDurationMS); | |
51 animateTo(targetPosition, duration, curve: _kAnimationCurve); | |
52 } | |
53 } | |
54 | |
55 void handleFlingStart(event) { | |
56 double direction = event.velocityX.sign; | |
57 double velocityX = event.velocityX.abs() / 1000; | |
58 if (velocityX < _kMinFlingVelocity) | |
59 return; | |
60 | |
61 double targetPosition = direction < 0.0 ? -_kWidth : 0.0; | |
62 double distance = (targetPosition - value).abs(); | |
63 double duration = distance / velocityX; | |
64 | |
65 animateTo(targetPosition, duration, curve: linear); | |
66 } | |
67 } | |
68 | |
69 class Drawer extends Component { | |
70 | |
71 static Style _style = new Style(''' | |
72 position: absolute; | |
73 z-index: 2; | |
74 top: 0; | |
75 left: 0; | |
76 bottom: 0; | |
77 right: 0; | |
78 box-shadpw: ${Shadow[3]};''' | |
79 ); | |
80 | |
81 static Style _maskStyle = new Style(''' | |
82 background-color: black; | |
83 will-change: opacity; | |
84 position: absolute; | |
85 top: 0; | |
86 left: 0; | |
87 bottom: 0; | |
88 right: 0;''' | |
89 ); | |
90 | |
91 static Style _contentStyle = new Style(''' | |
92 background-color: ${Grey[50]}; | |
93 will-change: transform; | |
94 position: absolute; | |
95 z-index: 3; | |
96 width: 256px; | |
97 top: 0; | |
98 left: 0; | |
99 bottom: 0;''' | |
100 ); | |
101 | |
102 DrawerAnimation animation; | |
103 List<Node> children; | |
104 | |
105 Drawer({ | |
106 Object key, | |
107 this.animation, | |
108 this.children | |
109 }) : super(key: key); | |
110 | |
111 double _position = -_kWidth; | |
112 | |
113 bool _listening = false; | |
114 | |
115 void _ensureListening() { | |
116 if (_listening) | |
117 return; | |
118 | |
119 _listening = true; | |
120 animation.onPositionChanged.listen((position) { | |
121 setState(() { | |
122 _position = position; | |
123 }); | |
124 }); | |
125 } | |
126 | |
127 Node build() { | |
128 _ensureListening(); | |
129 | |
130 bool isClosed = _position <= -_kWidth; | |
131 String inlineStyle = 'display: ${isClosed ? 'none' : ''}'; | |
132 String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}'; | |
133 String contentInlineStyle = 'transform: translateX(${_position}px)'; | |
134 | |
135 Container mask = new Container( | |
136 key: 'Mask', | |
137 style: _maskStyle, | |
138 inlineStyle: maskInlineStyle | |
139 )..events.listen('gesturetap', animation.handleMaskTap) | |
140 ..events.listen('gestureflingstart', animation.handleFlingStart); | |
141 | |
142 Container content = new Container( | |
143 key: 'Content', | |
144 style: _contentStyle, | |
145 inlineStyle: contentInlineStyle, | |
146 children: children | |
147 ); | |
148 | |
149 return new Container( | |
150 style: _style, | |
151 inlineStyle: inlineStyle, | |
152 children: [ mask, content ] | |
153 )..events.listen('pointerdown', animation.handlePointerDown) | |
154 ..events.listen('pointermove', animation.handlePointerMove) | |
155 ..events.listen('pointerup', animation.handlePointerUp) | |
156 ..events.listen('pointercancel', animation.handlePointerCancel); | |
157 | |
158 } | |
159 } | |
OLD | NEW |