Commit c06995a3 authored by Hans Muller's avatar Hans Muller

Clamp the snapped scroll simulation; Scrollable settle() and fling() return a Future

parent 946da101
......@@ -10,6 +10,7 @@ library animation;
export 'src/animation/animated_simulation.dart';
export 'src/animation/animated_value.dart';
export 'src/animation/animation_performance.dart';
export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart';
export 'src/animation/forces.dart';
export 'src/animation/scheduler.dart';
......
......@@ -132,34 +132,45 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return offset >= behavior.minScrollOffset && offset < behavior.maxScrollOffset;
}
double _alignedScrollSnapOffset(double offset) {
return config.snapOffsetCallback(offset + config.snapAlignmentOffset) - config.snapAlignmentOffset;
Simulation _createFlingSimulation(double velocity) {
return scrollBehavior.createFlingScrollSimulation(scrollOffset, velocity);
}
void _startToEndAnimation({ double velocity }) {
_stopAnimations();
Simulation _createSnapSimulation(double velocity) {
if (velocity == null || config.snapOffsetCallback == null || !_scrollOffsetIsInBounds(scrollOffset))
return null;
if (velocity != null && config.snapOffsetCallback != null && _scrollOffsetIsInBounds(scrollOffset)) {
Simulation simulation = scrollBehavior.createFlingScrollSimulation(scrollOffset, velocity);
if (simulation == null)
return;
double endScrollOffset = simulation.x(double.INFINITY);
if (!endScrollOffset.isNaN) {
double alignedScrollOffset = _alignedScrollSnapOffset(endScrollOffset);
if (_scrollOffsetIsInBounds(alignedScrollOffset)) {
double snapVelocity = velocity.abs() * (alignedScrollOffset - scrollOffset).sign;
Simulation toSnapSimulation = scrollBehavior.createSnapScrollSimulation(
scrollOffset, alignedScrollOffset, snapVelocity);
_toEndAnimation.start(toSnapSimulation);
return;
}
}
}
Simulation simulation = _createFlingSimulation(velocity);
if (simulation == null)
return null;
double endScrollOffset = simulation.x(double.INFINITY);
if (endScrollOffset.isNaN)
return null;
double snappedScrollOffset = config.snapOffsetCallback(endScrollOffset + config.snapAlignmentOffset);
double alignedScrollOffset = snappedScrollOffset - config.snapAlignmentOffset;
if (!_scrollOffsetIsInBounds(alignedScrollOffset))
return null;
Simulation simulation = scrollBehavior.createFlingScrollSimulation(scrollOffset, velocity ?? 0.0);
double snapVelocity = velocity.abs() * (alignedScrollOffset - scrollOffset).sign;
Simulation toSnapSimulation =
scrollBehavior.createSnapScrollSimulation(scrollOffset, alignedScrollOffset, snapVelocity);
if (toSnapSimulation == null)
return null;
double offsetMin = math.min(scrollOffset, alignedScrollOffset);
double offsetMax = math.max(scrollOffset, alignedScrollOffset);
return new ClampedSimulation(toSnapSimulation, xMin: offsetMin, xMax: offsetMax);
}
Future _startToEndAnimation({ double velocity }) {
_stopAnimations();
Simulation simulation =
_createSnapSimulation(velocity) ?? _createFlingSimulation(velocity ?? 0.0);
if (simulation == null)
return;
_toEndAnimation.start(simulation);
return new Future.value();
return _toEndAnimation.start(simulation);
}
void dispose() {
......@@ -195,16 +206,16 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return scrollTo(newScrollOffset, duration: duration, curve: curve);
}
void fling(Offset velocity) {
if (velocity != Offset.zero) {
_startToEndAnimation(velocity: _scrollVelocity(velocity));
} else if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating)) {
settleScrollOffset();
}
Future fling(Offset velocity) {
if (velocity != Offset.zero)
return _startToEndAnimation(velocity: _scrollVelocity(velocity));
if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
return settleScrollOffset();
return new Future.value();
}
void settleScrollOffset() {
_startToEndAnimation();
Future settleScrollOffset() {
return _startToEndAnimation();
}
double _scrollVelocity(sky.Offset velocity) {
......@@ -623,12 +634,12 @@ class PageableListState<T> extends ScrollableListState<T, PageableList<T>> {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
}
void fling(sky.Offset velocity) {
Future fling(sky.Offset velocity) {
double scrollVelocity = _scrollVelocity(velocity);
double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * config.itemExtent)
.clamp(_snapScrollOffset(scrollOffset - config.itemExtent / 2.0),
_snapScrollOffset(scrollOffset + config.itemExtent / 2.0));
scrollTo(newScrollOffset, duration: config.duration, curve: config.curve).then(_notifyPageChanged);
return scrollTo(newScrollOffset, duration: config.duration, curve: config.curve).then(_notifyPageChanged);
}
int get currentPage => (scrollOffset / config.itemExtent).floor() % itemCount;
......@@ -638,8 +649,8 @@ class PageableListState<T> extends ScrollableListState<T, PageableList<T>> {
config.onPageChanged(currentPage);
}
void settleScrollOffset() {
scrollTo(_snapScrollOffset(scrollOffset), duration: config.duration, curve: config.curve).then(_notifyPageChanged);
Future settleScrollOffset() {
return scrollTo(_snapScrollOffset(scrollOffset), duration: config.duration, curve: config.curve).then(_notifyPageChanged);
}
}
......
......@@ -2,6 +2,8 @@
// 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:quiver/testing/async.dart';
import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';
......@@ -49,11 +51,11 @@ void set scrollOffset(double value) {
scrollableState.scrollTo(value);
}
void fling(double velocity) {
Future fling(double velocity) {
Offset velocityOffset = scrollDirection == ScrollDirection.vertical
? new Offset(0.0, velocity)
: new Offset(velocity, 0.0);
scrollableState.fling(velocityOffset);
return scrollableState.fling(velocityOffset);
}
void main() {
......@@ -66,7 +68,7 @@ void main() {
expect(scrollOffset, 0.0);
double t0 = 0.0;
int dt = 1000;
int dt = 2000;
new FakeAsync().run((async) {
fling(-800.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
......@@ -82,10 +84,10 @@ void main() {
expect(scrollOffset, 0.0);
double t0 = 0.0;
int dt = 1000;
int dt = 2000;
new FakeAsync().run((async) {
fling(-2000.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0);
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(400.0, 1.0));
......@@ -94,14 +96,14 @@ void main() {
test('ScrollableList snap scrolling, fling(800)', () {
scrollOffset = 400.0;
tester.pumpFrameWithoutChange(1000.0);
tester.pumpFrameWithoutChange();
expect(scrollOffset, 400.0);
double t0 = 0.0;
int dt = 2000;
new FakeAsync().run((async) {
fling(800.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0);
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(0.0, 1.0));
......@@ -110,18 +112,38 @@ void main() {
test('ScrollableList snap scrolling, fling(2000)', () {
scrollOffset = 800.0;
tester.pumpFrameWithoutChange(1000.0);
tester.pumpFrameWithoutChange();
expect(scrollOffset, 800.0);
double t0 = 0.0;
int dt = 1500;
int dt = 2000;
new FakeAsync().run((async) {
fling(2000.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0);
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(200.0, 1.0));
});
});
test('ScrollableList snap scrolling, fling(2000).then()', () {
scrollOffset = 800.0;
tester.pumpFrameWithoutChange();
expect(scrollOffset, 800.0);
double t0 = 0.0;
int dt = 2000;
bool completed = false;
new FakeAsync().run((async) {
fling(2000.0).then((_) {
completed = true;
expect(scrollOffset, closeTo(200.0, 1.0));
});
tester.pumpFrameWithoutChange(t0);
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(completed, true);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment