Skip to content

Commit

Permalink
Merge pull request #3 from domokit/drawer.spring
Browse files Browse the repository at this point in the history
Make Drawer animate using a spring force instead of a linear curve.
  • Loading branch information
mpcomplete committed Jul 16, 2015
2 parents cf074be + 871eeb2 commit edd9b31
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 46 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions sky/sdk/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
32 changes: 16 additions & 16 deletions sky/sdk/lib/animation/animation_performance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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>();
Expand All @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion sky/sdk/lib/animation/scroll_behavior.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 0 additions & 13 deletions sky/sdk/lib/animation/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion sky/sdk/lib/widgets/dismissable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
}

Expand Down
30 changes: 16 additions & 14 deletions sky/sdk/lib/widgets/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);

Expand Down Expand Up @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -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.
Expand All @@ -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);
}
}

0 comments on commit edd9b31

Please sign in to comment.