// 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/src/animation/animated_value.dart'; import 'package:sky/src/animation/curves.dart'; import 'package:sky/src/animation/ticker.dart'; /// 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. class _TweenSimulation extends Simulation { _TweenSimulation(double begin, double end, Duration duration, Curve curve) : _durationInSeconds = duration.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND, _tween = new AnimatedValue<double>(begin, end: end, curve: curve) { assert(_durationInSeconds > 0.0); assert(begin != null); assert(end != null); } final double _durationInSeconds; final AnimatedValue<double> _tween; double x(double timeInSeconds) { assert(timeInSeconds >= 0.0); final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0); _tween.setProgress(t, AnimationDirection.forward); return _tween.value; } double dx(double timeInSeconds) => 1.0; bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds; } typedef TimelineCallback(double value); /// Steps a simulation one per frame class SimulationStepper { SimulationStepper(TimelineCallback onTick) : _onTick = onTick { _ticker = new Ticker(_tick); } final TimelineCallback _onTick; Ticker _ticker; Simulation _simulation; /// The current value of the timeline double get value => _value; double _value = 0.0; void set value(double newValue) { assert(newValue != null); assert(!isAnimating); _value = newValue; _onTick(_value); } /// Whether the timeline is currently animating bool get isAnimating => _ticker.isTicking; /// 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. Future animateTo(double target, { Duration duration, Curve curve: linear }) { assert(duration > Duration.ZERO); 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(); } /// Stop animating the timeline void stop() { _simulation = null; _ticker.stop(); } void _tick(Duration elapsed) { double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND; _value = _simulation.x(elapsedInSeconds); if (_simulation.isDone(elapsedInSeconds)) stop(); _onTick(_value); } }