// 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 'package:sky/animation/animated_value.dart'; import 'package:sky/animation/animation_performance.dart'; import 'package:sky/src/widgets/animated_component.dart'; import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/framework.dart'; import 'package:vector_math/vector_math.dart'; export 'package:sky/animation/direction.dart' show Direction; // A helper class to anchor widgets to one another. Pass an instance of this to // a Transition, then use the build() method to create a child with the same // transition applied. class Anchor { Anchor(); TransitionBase transition; Widget build(Widget child) { return new _AnchorTransition(anchoredTo: this, child: child); } } // Used with the Anchor class to apply a transition to multiple children. class _AnchorTransition extends AnimatedComponent { _AnchorTransition({ Key key, this.anchoredTo, this.child }) : super(key: key); Anchor anchoredTo; Widget child; TransitionBase get transition => anchoredTo.transition; void initState() { if (transition != null) watch(transition.performance); } void syncConstructorArguments(_AnchorTransition source) { if (transition != null && isWatching(transition.performance)) unwatch(transition.performance); anchoredTo = source.anchoredTo; if (transition != null) watch(transition.performance); child = source.child; super.syncConstructorArguments(source); } Widget build() { if (transition == null) return child; return transition.buildWithChild(child); } } abstract class TransitionBase extends AnimatedComponent { TransitionBase({ Key key, this.anchor, this.child, this.direction, this.duration, this.performance, this.onDismissed, this.onCompleted }) : super(key: key); Widget child; Anchor anchor; Direction direction; Duration duration; AnimationPerformance performance; Function onDismissed; Function onCompleted; void initState() { if (anchor != null) anchor.transition = this; if (performance == null) { assert(duration != null); performance = new AnimationPerformance(duration: duration); } if (direction == Direction.reverse) performance.progress = 1.0; performance.addStatusListener(_checkStatusChanged); watch(performance); _start(); } void syncConstructorArguments(TransitionBase source) { child = source.child; onCompleted = source.onCompleted; onDismissed = source.onDismissed; duration = source.duration; if (direction != source.direction) { direction = source.direction; _start(); } super.syncConstructorArguments(source); } void _start() { performance.play(direction); } void _checkStatusChanged(AnimationStatus status) { if (performance.isDismissed) { if (onDismissed != null) onDismissed(); } else if (performance.isCompleted) { if (onCompleted != null) onCompleted(); } } Widget build() { return buildWithChild(child); } Widget buildWithChild(Widget child); } class SlideTransition extends TransitionBase { // TODO(mpcomplete): this constructor is mostly boilerplate, passing values // to super. Is there a simpler way? SlideTransition({ Key key, Anchor anchor, this.position, Duration duration, AnimationPerformance performance, Direction direction, Function onDismissed, Function onCompleted, Widget child }) : super(key: key, anchor: anchor, duration: duration, performance: performance, direction: direction, onDismissed: onDismissed, onCompleted: onCompleted, child: child); AnimatedValue<Point> position; void syncConstructorArguments(SlideTransition source) { position = source.position; super.syncConstructorArguments(source); } Widget buildWithChild(Widget child) { performance.updateVariable(position); Matrix4 transform = new Matrix4.identity() ..translate(position.value.x, position.value.y); return new Transform(transform: transform, child: child); } } class FadeTransition extends TransitionBase { FadeTransition({ Key key, Anchor anchor, this.opacity, Duration duration, AnimationPerformance performance, Direction direction, Function onDismissed, Function onCompleted, Widget child }) : super(key: key, anchor: anchor, duration: duration, performance: performance, direction: direction, onDismissed: onDismissed, onCompleted: onCompleted, child: child); AnimatedValue<double> opacity; void syncConstructorArguments(FadeTransition source) { opacity = source.opacity; super.syncConstructorArguments(source); } Widget buildWithChild(Widget child) { performance.updateVariable(opacity); return new Opacity(opacity: opacity.value, child: child); } } class ColorTransition extends TransitionBase { ColorTransition({ Key key, Anchor anchor, this.color, Duration duration, AnimationPerformance performance, Direction direction, Function onDismissed, Function onCompleted, Widget child }) : super(key: key, anchor: anchor, duration: duration, performance: performance, direction: direction, onDismissed: onDismissed, onCompleted: onCompleted, child: child); AnimatedColorValue color; void syncConstructorArguments(ColorTransition source) { color = source.color; super.syncConstructorArguments(source); } Widget buildWithChild(Widget child) { performance.updateVariable(color); return new DecoratedBox( decoration: new BoxDecoration(backgroundColor: color.value), child: child ); } } class SquashTransition extends TransitionBase { SquashTransition({ Key key, Anchor anchor, this.width, this.height, Duration duration, AnimationPerformance performance, Direction direction, Function onDismissed, Function onCompleted, Widget child }) : super(key: key, anchor: anchor, duration: duration, performance: performance, direction: direction, onDismissed: onDismissed, onCompleted: onCompleted, child: child); AnimatedValue<double> width; AnimatedValue<double> height; void syncConstructorArguments(SquashTransition source) { width = source.width; height = source.height; super.syncConstructorArguments(source); } Widget buildWithChild(Widget child) { if (width != null) performance.updateVariable(width); if (height != null) performance.updateVariable(height); return new SizedBox(width: width?.value, height: height?.value, child: child); } } typedef Widget BuilderFunction(); class BuilderTransition extends TransitionBase { BuilderTransition({ Key key, Anchor anchor, this.variables, this.builder, Duration duration, AnimationPerformance performance, Direction direction, Function onDismissed, Function onCompleted, Widget child }) : super(key: key, anchor: anchor, duration: duration, performance: performance, direction: direction, onDismissed: onDismissed, onCompleted: onCompleted, child: child); List<AnimatedValue> variables; BuilderFunction builder; void syncConstructorArguments(BuilderTransition source) { variables = source.variables; builder = source.builder; super.syncConstructorArguments(source); } Widget buildWithChild(Widget child) { for (int i = 0; i < variables.length; ++i) performance.updateVariable(variables[i]); return builder(); } }