OLD | NEW |
(Empty) | |
| 1 part of widgets; |
| 2 |
| 3 class FrameGenerator { |
| 4 |
| 5 Function onDone; |
| 6 StreamController _controller; |
| 7 |
| 8 Stream<double> get onTick => _controller.stream; |
| 9 |
| 10 int _animationId = 0; |
| 11 bool _cancelled = false; |
| 12 |
| 13 FrameGenerator({this.onDone}) { |
| 14 _controller = new StreamController( |
| 15 sync: true, |
| 16 onListen: _scheduleTick, |
| 17 onCancel: cancel); |
| 18 } |
| 19 |
| 20 void cancel() { |
| 21 if (_cancelled) { |
| 22 return; |
| 23 } |
| 24 if (_animationId != 0) { |
| 25 sky.window.cancelAnimationFrame(_animationId); |
| 26 } |
| 27 _animationId = 0; |
| 28 _cancelled = true; |
| 29 if (onDone != null) { |
| 30 onDone(); |
| 31 } |
| 32 } |
| 33 |
| 34 void _scheduleTick() { |
| 35 assert(_animationId == 0); |
| 36 _animationId = sky.window.requestAnimationFrame(_tick); |
| 37 } |
| 38 |
| 39 void _tick(double timeStamp) { |
| 40 _animationId = 0; |
| 41 _controller.add(timeStamp); |
| 42 if (!_cancelled) { |
| 43 _scheduleTick(); |
| 44 } |
| 45 } |
| 46 } |
| 47 |
| 48 const double _kFrameTime = 1000 / 60; |
| 49 |
| 50 class AnimationGenerator extends FrameGenerator { |
| 51 |
| 52 Stream<double> get onTick => _stream; |
| 53 final double duration; |
| 54 final double begin; |
| 55 final double end; |
| 56 final Curve curve; |
| 57 Stream<double> _stream; |
| 58 |
| 59 AnimationGenerator(this.duration, { |
| 60 this.begin: 0.0, |
| 61 this.end: 1.0, |
| 62 this.curve: linear, |
| 63 Function onDone |
| 64 }):super(onDone: onDone) { |
| 65 double startTime = 0.0; |
| 66 double targetTime = 0.0; |
| 67 bool done = false; |
| 68 _stream = super.onTick.map((timeStamp) { |
| 69 if (startTime == 0.0) { |
| 70 startTime = timeStamp; |
| 71 targetTime = startTime + duration; |
| 72 } |
| 73 |
| 74 // Clamp the final frame to target time so we terminate the series with |
| 75 // 1.0 exactly. |
| 76 if ((timeStamp - targetTime).abs() <= _kFrameTime) { |
| 77 return 1.0; |
| 78 } |
| 79 |
| 80 return (timeStamp - startTime) / duration; |
| 81 }) |
| 82 .takeWhile((t) => t <= 1.0) |
| 83 .map((t) => begin + (end - begin) * curve.transform(t)); |
| 84 } |
| 85 } |
| 86 |
| 87 double _evaluateCubic(double a, double b, double m) { |
| 88 // TODO(abarth): Would Math.pow be faster? |
| 89 return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m; |
| 90 } |
| 91 |
| 92 const double _kCubicErrorBound = 0.001; |
| 93 |
| 94 abstract class Curve { |
| 95 double transform(double t); |
| 96 } |
| 97 |
| 98 class Linear implements Curve { |
| 99 const Linear(); |
| 100 |
| 101 double transform(double t) { |
| 102 return t; |
| 103 } |
| 104 } |
| 105 |
| 106 class Cubic implements Curve { |
| 107 final double a; |
| 108 final double b; |
| 109 final double c; |
| 110 final double d; |
| 111 |
| 112 const Cubic(this.a, this.b, this.c, this.d); |
| 113 |
| 114 double transform(double t) { |
| 115 if (t == 0.0 || t == 1.0) |
| 116 return t; |
| 117 |
| 118 double start = 0.0; |
| 119 double end = 1.0; |
| 120 while (true) { |
| 121 double midpoint = (start + end) / 2; |
| 122 double estimate = _evaluateCubic(a, c, midpoint); |
| 123 |
| 124 if ((t - estimate).abs() < _kCubicErrorBound) |
| 125 return _evaluateCubic(b, d, midpoint); |
| 126 |
| 127 if (estimate < t) |
| 128 start = midpoint; |
| 129 else |
| 130 end = midpoint; |
| 131 } |
| 132 } |
| 133 } |
| 134 |
| 135 const Linear linear = const Linear(); |
| 136 const Cubic ease = const Cubic(0.25, 0.1, 0.25, 1.0); |
| 137 const Cubic easeIn = const Cubic(0.42, 0.0, 1.0, 1.0); |
| 138 const Cubic easeOut = const Cubic(0.0, 0.0, 0.58, 1.0); |
| 139 const Cubic easeInOut = const Cubic(0.42, 0.0, 0.58, 1.0); |
OLD | NEW |