提交 edd9b315 编写于 作者: M mpcomplete

Merge pull request #3 from domokit/drawer.spring

Make Drawer animate using a spring force instead of a linear curve.
......@@ -34,7 +34,7 @@ vars = {
'cassowary_dart_revision': '7e5afc5b3956a18636d5b37b1dcba1705865564b',
'collection_dart_revision': '79ebc6fc2dae581cb23ad50a5c600c1b7dd132f8',
'crypto_dart_revision': 'd4558dea1639e5ad2a41d045265b8ece270c2d90',
'newton_dart_revision': '26da04f0c441d005a6ecbf62ae047cd02ec9abc5',
'newton_dart_revision': '9fbe5fbac809246f7ace4176feca13bdf731e393',
'path_dart_revision': '2f3dcdec32011f1bc41194ae3640d6d9292a7096',
'quiver_dart_revision': '6bab7dec34189eee579178eb16d3063c8ae69031',
'source_span_dart_revision': '5c6c13f62fc111adaace3aeb4a38853d64481d06',
......
......@@ -11,6 +11,7 @@ dart_pkg("sky") {
"lib/animation/animated_simulation.dart",
"lib/animation/animation_performance.dart",
"lib/animation/curves.dart",
"lib/animation/forces.dart",
"lib/animation/scroll_behavior.dart",
"lib/animation/timeline.dart",
"lib/assets/.gitignore",
......
......@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:newton/newton.dart';
import 'dart:async';
import 'package:sky/animation/timeline.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/animation/forces.dart';
abstract class AnimatedVariable {
void setFraction(double t);
......@@ -99,23 +101,20 @@ class AnimationPerformance {
bool get isCompleted => progress == 1.0;
bool get isAnimating => timeline.isAnimating;
void play() {
_animateTo(1.0);
}
void reverse() {
_animateTo(0.0);
}
Future play() => _animateTo(1.0);
Future reverse() => _animateTo(0.0);
void stop() {
timeline.stop();
}
// Resume animating in a direction, with the given velocity.
// TODO(mpcomplete): Allow user to specify the Simulation.
void fling({double velocity: 1.0}) {
Simulation simulation =
timeline.defaultSpringSimulation(velocity: velocity);
timeline.fling(simulation);
// Flings the timeline with an optional force (defaults to a critically damped
// spring) and initial velocity. Negative velocity causes the timeline to go
// in reverse.
Future fling({double velocity: 1.0, Force force}) {
if (force == null)
force = kDefaultSpringForce;
return timeline.fling(force.release(progress, velocity));
}
final List<Function> _listeners = new List<Function>();
......@@ -134,11 +133,12 @@ class AnimationPerformance {
listener();
}
void _animateTo(double target) {
Future _animateTo(double target) {
double remainingDistance = (target - timeline.value).abs();
timeline.stop();
if (remainingDistance != 0.0)
timeline.animateTo(target, duration: duration * remainingDistance);
if (remainingDistance == 0.0)
return new Future.value();
return timeline.animateTo(target, duration: duration * remainingDistance);
}
void _tick(double t) {
......
......@@ -5,10 +5,11 @@
import 'dart:math' as math;
import 'package:newton/newton.dart';
import 'package:sky/animation/forces.dart';
const double _kSecondsPerMillisecond = 1000.0;
abstract class ScrollBehavior {
abstract class ScrollBehavior extends Force {
Simulation release(double position, double velocity) => null;
// Returns the new scroll offset.
......
......@@ -7,8 +7,6 @@ import 'dart:async';
import 'package:newton/newton.dart';
import 'package:sky/animation/animated_simulation.dart';
const double _kEpsilon = 0.001;
// Simple simulation that linearly varies from |begin| to |end| over |duration|.
class TweenSimulation extends Simulation {
final double _durationInSeconds;
......@@ -68,17 +66,6 @@ class Timeline {
_animation.stop();
}
static final SpringDescription _kDefaultSpringDesc =
new SpringDescription.withDampingRatio(
mass: 1.0, springConstant: 500.0, ratio: 1.0);
Simulation defaultSpringSimulation({double velocity: 0.0}) {
// Target just past the 0 or 1 endpoint, because the animation will stop
// once the Spring gets within the epsilon, and we want to stop at 0 or 1.
double target = velocity < 0.0 ? -_kEpsilon : 1.0 + _kEpsilon;
return new SpringSimulation(_kDefaultSpringDesc, value, target, velocity);
}
// Give |simulation| control over the timeline.
Future fling(Simulation simulation) {
stop();
......
......@@ -13,6 +13,7 @@ import 'package:vector_math/vector_math.dart';
const Duration _kCardDismissFadeout = const Duration(milliseconds: 300);
const double _kMinFlingVelocity = 700.0;
const double _kMinFlingVelocityDelta = 400.0;
const double _kFlingVelocityScale = 1.0/300.0;
const double _kDismissCardThreshold = 0.6;
typedef void DismissedCallback();
......@@ -129,7 +130,7 @@ class Dismissable extends AnimatedComponent {
_dragUnderway = false;
_dragX = event.velocityX.sign;
_position.end = _activeCardDragEndPoint;
_performance.fling(velocity: event.velocityX.abs() / _width);
_performance.fling(velocity: event.velocityX.abs() * _kFlingVelocityScale);
}
}
......
......@@ -5,7 +5,6 @@
import 'dart:sky' as sky;
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/theme/shadows.dart';
import 'package:sky/theme/colors.dart' as colors;
import 'package:sky/widgets/animated_component.dart';
......@@ -30,14 +29,11 @@ import 'package:vector_math/vector_math.dart';
// The right nav can vary depending on content.
const double _kWidth = 304.0;
const double _kMinFlingVelocity = 1.2;
const double _kMinFlingVelocity = 365.0;
const double _kFlingVelocityScale = 1.0/300.0;
const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
// TODO(mpcomplete): The curve must be linear if we want the drawer to track
// the user's finger. Odeon remedies this by attaching spring forces to the
// initial timeline when animating (so it doesn't look linear).
const Point _kOpenPosition = Point.origin;
const Point _kClosedPosition = const Point(-_kWidth, 0.0);
const Curve _kAnimationCurve = linear;
typedef void DrawerStatusChangeHandler (bool showing);
......@@ -69,7 +65,7 @@ class Drawer extends AnimatedComponent {
AnimationPerformance _performance;
void initState() {
_position = new AnimatedType<Point>(_kClosedPosition, end: _kOpenPosition, curve: _kAnimationCurve);
_position = new AnimatedType<Point>(_kClosedPosition, end: _kOpenPosition);
_maskColor = new AnimatedColor(colors.transparent, end: const Color(0x7F000000));
_performance = new AnimationPerformance()
..duration = _kBaseSettleDuration
......@@ -95,11 +91,18 @@ class Drawer extends AnimatedComponent {
void _show() {
if (navigator != null)
navigator.pushState(this, (_) => _performance.reverse());
_performance.play();
_fling(1.0);
}
void _hide() {
_performance.reverse();
_fling(-1.0);
}
// We fling the performance timeline instead of animating it to give it a
// nice spring effect. We can't use curves for this because we need a linear
// curve in order to track the user's finger while dragging.
void _fling(double direction) {
_performance.fling(velocity: direction.sign);
}
Widget build() {
......@@ -151,9 +154,9 @@ class Drawer extends AnimatedComponent {
DrawerStatus get _status => _performance.isDismissed ? DrawerStatus.inactive : DrawerStatus.active;
bool get _isMostlyClosed => xPosition <= -_kWidth/2;
void _settle() => _isMostlyClosed ? _performance.reverse() : _performance.play();
void _settle() => _fling(_isMostlyClosed ? -1.0 : 1.0);
void handleMaskTap(_) => _performance.reverse();
void handleMaskTap(_) => _fling(-1.0);
// TODO(mpcomplete): Figure out how to generalize these handlers on a
// "PannableThingy" interface.
......@@ -176,8 +179,7 @@ class Drawer extends AnimatedComponent {
}
void handleFlingStart(event) {
double velocityX = event.velocityX / _kWidth;
if (velocityX.abs() >= _kMinFlingVelocity)
_performance.fling(velocity: velocityX);
if (event.velocityX.abs() >= _kMinFlingVelocity)
_performance.fling(velocity: event.velocityX * _kFlingVelocityScale);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册