animated_simulation.dart 3.36 KB
Newer Older
1 2 3 4 5 6 7
// 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';
8
import 'package:sky/src/animation/scheduler.dart';
9 10 11

const double _kSecondsPerMillisecond = 1000.0;

12 13 14 15
// TODO(abarth): Change from double to Duration.
typedef _TickerCallback(double timeStamp);

/// Calls its callback once per animation frame
16
class Ticker {
17 18
  /// Constructs a ticker that will call onTick once per frame while running
  Ticker(_TickerCallback onTick) : _onTick = onTick;
19

20
  final _TickerCallback _onTick;
21 22 23 24

  Completer _completer;
  int _animationId;

25 26 27
  /// Start calling onTick once per animation frame
  ///
  /// The returned future resolves once the ticker stops ticking.
28 29 30 31 32 33 34
  Future start() {
    assert(!isTicking);
    _completer = new Completer();
    _scheduleTick();
    return _completer.future;
  }

35 36 37
  /// Stop calling onTick
  ///
  /// Causes the future returned by [start] to resolve.
38 39 40 41 42 43 44 45 46
  void stop() {
    if (!isTicking)
      return;

    if (_animationId != null) {
      scheduler.cancelAnimationFrame(_animationId);
      _animationId = null;
    }

47 48 49
    // We take the _completer into a local variable so that !isTicking
    // when we actually complete the future (isTicking uses _completer
    // to determine its state).
50 51 52 53 54 55
    Completer localCompleter = _completer;
    _completer = null;
    assert(!isTicking);
    localCompleter.complete();
  }

56
  /// Whether this ticker has scheduled a call to onTick
57 58 59 60 61 62 63 64 65
  bool get isTicking => _completer != null;

  void _tick(double timeStamp) {
    assert(isTicking);
    assert(_animationId != null);
    _animationId = null;

    _onTick(timeStamp);

66 67
    // The onTick callback may have scheduled another tick already.
    if (isTicking && _animationId == null)
68 69 70 71 72 73 74 75 76 77
      _scheduleTick();
  }

  void _scheduleTick() {
    assert(isTicking);
    assert(_animationId == null);
    _animationId = scheduler.requestAnimationFrame(_tick);
  }
}

78
/// Ticks a simulation once per frame
79 80 81 82 83 84 85 86 87 88 89 90 91
class AnimatedSimulation {

  AnimatedSimulation(Function onTick) : _onTick = onTick {
    _ticker = new Ticker(_tick);
  }

  final Function _onTick;
  Ticker _ticker;

  Simulation _simulation;
  double _startTime;

  double _value = 0.0;
92
  /// The current value of the simulation
93 94 95 96 97 98 99
  double get value => _value;
  void set value(double newValue) {
    assert(!_ticker.isTicking);
    _value = newValue;
    _onTick(_value);
  }

100 101 102
  /// Start ticking the given simulation once per frame
  ///
  /// Returns a future that resolves when the simulation stops ticking.
103 104 105 106 107 108 109 110 111
  Future start(Simulation simulation) {
    assert(simulation != null);
    assert(!_ticker.isTicking);
    _simulation = simulation;
    _startTime = null;
    _value = simulation.x(0.0);
    return _ticker.start();
  }

112
  /// Stop ticking the current simulation
113 114 115 116 117 118
  void stop() {
    _simulation = null;
    _startTime = null;
    _ticker.stop();
  }

119
  /// Whether this object is currently ticking a simulation
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  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);
  }

}