| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:newton/newton.dart'; |
| import 'package:sky/base/scheduler.dart' as scheduler; |
| |
| const double _kSecondsPerMillisecond = 1000.0; |
| |
| class Ticker { |
| Ticker(Function onTick) : _onTick = onTick; |
| |
| final Function _onTick; |
| |
| Completer _completer; |
| int _animationId; |
| |
| Future start() { |
| assert(!isTicking); |
| _completer = new Completer(); |
| _scheduleTick(); |
| return _completer.future; |
| } |
| |
| void stop() { |
| if (!isTicking) |
| return; |
| |
| if (_animationId != null) { |
| scheduler.cancelAnimationFrame(_animationId); |
| _animationId = null; |
| } |
| |
| // We take the _completer into a local variable so that !isTicking |
| // when we actually complete the future (isTicking uses _completer |
| // to determine its state). |
| Completer localCompleter = _completer; |
| _completer = null; |
| assert(!isTicking); |
| localCompleter.complete(); |
| } |
| |
| bool get isTicking => _completer != null; |
| |
| void _tick(double timeStamp) { |
| assert(isTicking); |
| assert(_animationId != null); |
| _animationId = null; |
| |
| _onTick(timeStamp); |
| |
| // The onTick callback may have scheduled another tick already. |
| if (isTicking && _animationId == null) |
| _scheduleTick(); |
| } |
| |
| void _scheduleTick() { |
| assert(isTicking); |
| assert(_animationId == null); |
| _animationId = scheduler.requestAnimationFrame(_tick); |
| } |
| } |
| |
| class AnimatedSimulation { |
| |
| AnimatedSimulation(Function onTick) : _onTick = onTick { |
| _ticker = new Ticker(_tick); |
| } |
| |
| final Function _onTick; |
| Ticker _ticker; |
| |
| Simulation _simulation; |
| double _startTime; |
| |
| double _value = 0.0; |
| double get value => _value; |
| void set value(double newValue) { |
| assert(!_ticker.isTicking); |
| _value = newValue; |
| _onTick(_value); |
| } |
| |
| Future start(Simulation simulation) { |
| assert(simulation != null); |
| assert(!_ticker.isTicking); |
| _simulation = simulation; |
| _startTime = null; |
| _value = simulation.x(0.0); |
| return _ticker.start(); |
| } |
| |
| void stop() { |
| _simulation = null; |
| _startTime = null; |
| _ticker.stop(); |
| } |
| |
| bool get isAnimating => _ticker.isTicking; |
| |
| void _tick(double timeStamp) { |
| if (_startTime == null) |
| _startTime = timeStamp; |
| |
| double timeInSeconds = (timeStamp - _startTime) / _kSecondsPerMillisecond; |
| _value = _simulation.x(timeInSeconds); |
| final bool isLastTick = _simulation.isDone(timeInSeconds); |
| |
| if (isLastTick) |
| stop(); |
| |
| _onTick(_value); |
| } |
| |
| } |