blob: 65c373f548a7029301c7b55d5f3ece344a7b6236 [file] [log] [blame] [edit]
// 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 'curves.dart';
import 'dart:async';
import 'dart:math' as math;
import 'dart:sky' as sky;
class FrameGenerator {
Function onDone;
StreamController _controller;
Stream<double> get onTick => _controller.stream;
int _animationId = 0;
bool _cancelled = false;
FrameGenerator({this.onDone}) {
_controller = new StreamController(
sync: true,
onListen: _scheduleTick,
onCancel: cancel);
}
void cancel() {
if (_cancelled) {
return;
}
if (_animationId != 0) {
sky.window.cancelAnimationFrame(_animationId);
}
_animationId = 0;
_cancelled = true;
if (onDone != null) {
onDone();
}
}
void _scheduleTick() {
assert(_animationId == 0);
_animationId = sky.window.requestAnimationFrame(_tick);
}
void _tick(double timeStamp) {
_animationId = 0;
_controller.add(timeStamp);
if (!_cancelled) {
_scheduleTick();
}
}
}
class AnimationGenerator extends FrameGenerator {
Stream<double> get onTick => _stream;
final double initialDelay;
final double duration;
final double begin;
final double end;
final Curve curve;
Stream<double> _stream;
bool _done = false;
AnimationGenerator(this.duration, {
this.initialDelay: 0.0,
this.begin: 0.0,
this.end: 1.0,
this.curve: linear,
Function onDone
}):super(onDone: onDone) {
assert(duration > 0);
double startTime = 0.0;
_stream = super.onTick.map((timeStamp) {
if (startTime == 0.0)
startTime = timeStamp;
double t = (timeStamp - (startTime + initialDelay)) / duration;
return math.max(0.0, math.min(t, 1.0));
})
.takeWhile(_checkForCompletion) //
.where((t) => t >= 0.0)
.map(_transform);
}
double _transform(double t) {
if (_done)
return end;
return begin + (end - begin) * curve.transform(t);
}
// This is required because Dart Streams don't have takeUntil (inclusive).
bool _checkForCompletion(double t) {
if (_done)
return false;
_done = t >= 1;
return true;
}
}
class Animation {
Stream<double> get onValueChanged => _controller.stream;
double get value => _value;
void set value(double value) {
stop();
_setValue(value);
}
bool get isAnimating => _animation != null;
StreamController _controller = new StreamController(sync: true);
AnimationGenerator _animation;
double _value;
void _setValue(double value) {
_value = value;
_controller.add(_value);
}
void stop() {
if (_animation != null) {
_animation.cancel();
_animation = null;
}
}
void animateTo(double newValue, double duration,
{ Curve curve: linear, double initialDelay: 0.0 }) {
stop();
_animation = new AnimationGenerator(duration, begin: _value, end: newValue,
curve: curve, initialDelay: initialDelay);
_animation.onTick.listen(_setValue, onDone: () {
_animation = null;
});
}
}