simulation_stepper.dart 3.13 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 9 10
import 'animated_value.dart';
import 'curves.dart';
import 'ticker.dart';
11

Adam Barth's avatar
Adam Barth committed
12 13 14 15
/// A simulation that varies from [begin] to [end] over [duration] using [curve]
///
/// This class is an adaptor between the Simulation interface and the
/// AnimatedValue interface.
16 17
class _TweenSimulation extends Simulation {
  _TweenSimulation(double begin, double end, Duration duration, Curve curve)
Adam Barth's avatar
Adam Barth committed
18
    : _durationInSeconds = duration.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND,
19
      _tween = new AnimatedValue<double>(begin, end: end, curve: curve) {
20
    assert(_durationInSeconds > 0.0);
21 22
    assert(begin != null);
    assert(end != null);
23 24
  }

Adam Barth's avatar
Adam Barth committed
25 26 27
  final double _durationInSeconds;
  final AnimatedValue<double> _tween;

28 29
  double x(double timeInSeconds) {
    assert(timeInSeconds >= 0.0);
30
    final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
31
    _tween.setProgress(t, AnimationDirection.forward);
32
    return _tween.value;
33 34 35 36 37 38 39
  }

  double dx(double timeInSeconds) => 1.0;

  bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}

Adam Barth's avatar
Adam Barth committed
40 41 42 43 44 45
typedef TimelineCallback(double value);

/// Steps a simulation one per frame
class SimulationStepper {
  SimulationStepper(TimelineCallback onTick) : _onTick = onTick {
    _ticker = new Ticker(_tick);
46 47
  }

Adam Barth's avatar
Adam Barth committed
48 49 50
  final TimelineCallback _onTick;
  Ticker _ticker;
  Simulation _simulation;
51

52
  /// The current value of the timeline
Adam Barth's avatar
Adam Barth committed
53 54
  double get value => _value;
  double _value = 0.0;
55
  void set value(double newValue) {
56
    assert(newValue != null);
57
    assert(!isAnimating);
Adam Barth's avatar
Adam Barth committed
58 59
    _value = newValue;
    _onTick(_value);
60 61
  }

62
  /// Whether the timeline is currently animating
Adam Barth's avatar
Adam Barth committed
63
  bool get isAnimating => _ticker.isTicking;
64

65 66 67 68
  /// Animate value of the timeline to the given target over the given duration
  ///
  /// Returns a future that resolves when the timeline stops animating,
  /// typically when the timeline arives at the target value.
69
  Future animateTo(double target, { Duration duration, Curve curve: Curves.linear }) {
70
    assert(duration > Duration.ZERO);
Adam Barth's avatar
Adam Barth committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    assert(!isAnimating);
    return _start(new _TweenSimulation(value, target, duration, curve));
  }

  /// Gives the given simulation control over the timeline
  Future animateWith(Simulation simulation) {
    stop();
    return _start(simulation);
  }

  /// Start ticking the given simulation once per frame
  ///
  /// Returns a future that resolves when the simulation stops ticking.
  Future _start(Simulation simulation) {
    assert(simulation != null);
    assert(!isAnimating);
    _simulation = simulation;
    _value = simulation.x(0.0);
    return _ticker.start();
90 91
  }

92
  /// Stop animating the timeline
93
  void stop() {
Adam Barth's avatar
Adam Barth committed
94 95
    _simulation = null;
    _ticker.stop();
96 97
  }

Adam Barth's avatar
Adam Barth committed
98 99 100 101 102 103
  void _tick(Duration elapsed) {
    double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
    _value = _simulation.x(elapsedInSeconds);
    if (_simulation.isDone(elapsedInSeconds))
      stop();
    _onTick(_value);
104 105
  }
}