Merge pull request #1872 from abarth/velocity
Add a Velocity class to be explicit about units
This commit is contained in:
commit
93b46c0469
@ -16,19 +16,19 @@ enum DragState {
|
|||||||
|
|
||||||
typedef void GestureDragStartCallback(Point globalPosition);
|
typedef void GestureDragStartCallback(Point globalPosition);
|
||||||
typedef void GestureDragUpdateCallback(double delta);
|
typedef void GestureDragUpdateCallback(double delta);
|
||||||
typedef void GestureDragEndCallback(Offset velocity);
|
typedef void GestureDragEndCallback(Velocity velocity);
|
||||||
|
|
||||||
typedef void GesturePanStartCallback(Point globalPosition);
|
typedef void GesturePanStartCallback(Point globalPosition);
|
||||||
typedef void GesturePanUpdateCallback(Offset delta);
|
typedef void GesturePanUpdateCallback(Offset delta);
|
||||||
typedef void GesturePanEndCallback(Offset velocity);
|
typedef void GesturePanEndCallback(Velocity velocity);
|
||||||
|
|
||||||
typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
|
typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
|
||||||
|
|
||||||
bool _isFlingGesture(Offset velocity) {
|
bool _isFlingGesture(Velocity velocity) {
|
||||||
assert(velocity != null);
|
assert(velocity != null);
|
||||||
double velocitySquared = velocity.dx * velocity.dx + velocity.dy * velocity.dy;
|
final double speedSquared = velocity.pixelsPerSecond.distanceSquared;
|
||||||
return velocitySquared > kMinFlingVelocity * kMinFlingVelocity
|
return speedSquared > kMinFlingVelocity * kMinFlingVelocity
|
||||||
&& velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity;
|
&& speedSquared < kMaxFlingVelocity * kMaxFlingVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGestureRecognizer {
|
abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGestureRecognizer {
|
||||||
@ -110,11 +110,11 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest
|
|||||||
VelocityTracker tracker = _velocityTrackers[pointer];
|
VelocityTracker tracker = _velocityTrackers[pointer];
|
||||||
assert(tracker != null);
|
assert(tracker != null);
|
||||||
|
|
||||||
Offset velocity = tracker.getVelocity();
|
Velocity velocity = tracker.getVelocity();
|
||||||
if (velocity != null && _isFlingGesture(velocity))
|
if (velocity != null && _isFlingGesture(velocity))
|
||||||
onEnd(velocity);
|
onEnd(velocity);
|
||||||
else
|
else
|
||||||
onEnd(Offset.zero);
|
onEnd(Velocity.zero);
|
||||||
}
|
}
|
||||||
_velocityTrackers.clear();
|
_velocityTrackers.clear();
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ typedef Drag GestureMultiDragStartCallback(Point position);
|
|||||||
|
|
||||||
class Drag {
|
class Drag {
|
||||||
void move(Offset offset) { }
|
void move(Offset offset) { }
|
||||||
void end(Offset velocity) { }
|
void end(Velocity velocity) { }
|
||||||
void cancel() { }
|
void cancel() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +208,28 @@ class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A velocity in two dimensions.
|
||||||
|
class Velocity {
|
||||||
|
const Velocity({ this.pixelsPerSecond });
|
||||||
|
|
||||||
|
/// A velocity that isn't moving at all.
|
||||||
|
static const Velocity zero = const Velocity(pixelsPerSecond: Offset.zero);
|
||||||
|
|
||||||
|
/// The number of pixels per second of velocity in the x and y directions.
|
||||||
|
final Offset pixelsPerSecond;
|
||||||
|
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
if (other is! Velocity)
|
||||||
|
return false;
|
||||||
|
final Velocity typedOther = other;
|
||||||
|
return pixelsPerSecond == typedOther.pixelsPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get hashCode => pixelsPerSecond.hashCode;
|
||||||
|
|
||||||
|
String toString() => 'Velocity(${pixelsPerSecond.dx.toStringAsFixed(1)}, ${pixelsPerSecond.dy.toStringAsFixed(1)})';
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes a pointer velocity based on data from PointerMove events.
|
/// Computes a pointer velocity based on data from PointerMove events.
|
||||||
///
|
///
|
||||||
/// The input data is provided by calling addPosition(). Adding data
|
/// The input data is provided by calling addPosition(). Adding data
|
||||||
@ -248,12 +270,14 @@ class VelocityTracker {
|
|||||||
///
|
///
|
||||||
/// getVelocity() will return null if no estimate is available or if
|
/// getVelocity() will return null if no estimate is available or if
|
||||||
/// the velocity is zero.
|
/// the velocity is zero.
|
||||||
Offset getVelocity() {
|
Velocity getVelocity() {
|
||||||
_Estimate estimate = _strategy.getEstimate();
|
_Estimate estimate = _strategy.getEstimate();
|
||||||
if (estimate != null && estimate.degree >= 1) {
|
if (estimate != null && estimate.degree >= 1) {
|
||||||
return new Offset( // convert from pixels/ms to pixels/s
|
return new Velocity(
|
||||||
estimate.xCoefficients[1] * 1000,
|
pixelsPerSecond: new Offset( // convert from pixels/ms to pixels/s
|
||||||
estimate.yCoefficients[1] * 1000
|
estimate.xCoefficients[1] * 1000,
|
||||||
|
estimate.yCoefficients[1] * 1000
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -59,11 +59,11 @@ class _BottomSheetState extends State<BottomSheet> {
|
|||||||
config.animationController.value -= delta / (_childHeight ?? delta);
|
config.animationController.value -= delta / (_childHeight ?? delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragEnd(Offset velocity) {
|
void _handleDragEnd(Velocity velocity) {
|
||||||
if (_dismissUnderway)
|
if (_dismissUnderway)
|
||||||
return;
|
return;
|
||||||
if (velocity.dy > _kMinFlingVelocity) {
|
if (velocity.pixelsPerSecond.dy > _kMinFlingVelocity) {
|
||||||
double flingVelocity = -velocity.dy / _childHeight;
|
double flingVelocity = -velocity.pixelsPerSecond.dy / _childHeight;
|
||||||
config.animationController.fling(velocity: flingVelocity);
|
config.animationController.fling(velocity: flingVelocity);
|
||||||
if (flingVelocity < 0.0)
|
if (flingVelocity < 0.0)
|
||||||
config.onClosing();
|
config.onClosing();
|
||||||
|
@ -135,11 +135,11 @@ class DrawerControllerState extends State<DrawerController> {
|
|||||||
_controller.value += delta / _width;
|
_controller.value += delta / _width;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _settle(Offset velocity) {
|
void _settle(Velocity velocity) {
|
||||||
if (_controller.isDismissed)
|
if (_controller.isDismissed)
|
||||||
return;
|
return;
|
||||||
if (velocity.dx.abs() >= _kMinFlingVelocity) {
|
if (velocity.pixelsPerSecond.dx.abs() >= _kMinFlingVelocity) {
|
||||||
_controller.fling(velocity: velocity.dx / _width);
|
_controller.fling(velocity: velocity.pixelsPerSecond.dx / _width);
|
||||||
} else if (_controller.value < 0.5) {
|
} else if (_controller.value < 0.5) {
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
|
@ -145,7 +145,7 @@ class _RenderSlider extends RenderConstrainedBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragEnd(Offset velocity) {
|
void _handleDragEnd(Velocity velocity) {
|
||||||
if (_active) {
|
if (_active) {
|
||||||
_active = false;
|
_active = false;
|
||||||
_currentDragValue = 0.0;
|
_currentDragValue = 0.0;
|
||||||
|
@ -156,7 +156,7 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragEnd(Offset velocity) {
|
void _handleDragEnd(Velocity velocity) {
|
||||||
if (position.value >= 0.5)
|
if (position.value >= 0.5)
|
||||||
positionController.forward();
|
positionController.forward();
|
||||||
else
|
else
|
||||||
|
@ -425,7 +425,7 @@ class _DialState extends State<_Dial> {
|
|||||||
_notifyOnChangedIfNeeded();
|
_notifyOnChangedIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePanEnd(Offset velocity) {
|
void _handlePanEnd(Velocity velocity) {
|
||||||
assert(_dragging);
|
assert(_dragging);
|
||||||
_dragging = false;
|
_dragging = false;
|
||||||
_position = null;
|
_position = null;
|
||||||
|
@ -182,9 +182,9 @@ class _DismissableState extends State<Dismissable> {
|
|||||||
_dismissController.value = _dragExtent.abs() / _size.width;
|
_dismissController.value = _dragExtent.abs() / _size.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isFlingGesture(Offset velocity) {
|
bool _isFlingGesture(Velocity velocity) {
|
||||||
double vx = velocity.dx;
|
double vx = velocity.pixelsPerSecond.dx;
|
||||||
double vy = velocity.dy;
|
double vy = velocity.pixelsPerSecond.dy;
|
||||||
if (_directionIsYAxis) {
|
if (_directionIsYAxis) {
|
||||||
if (vy.abs() - vx.abs() < _kMinFlingVelocityDelta)
|
if (vy.abs() - vx.abs() < _kMinFlingVelocityDelta)
|
||||||
return false;
|
return false;
|
||||||
@ -211,7 +211,7 @@ class _DismissableState extends State<Dismissable> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragEnd(Offset velocity) {
|
void _handleDragEnd(Velocity velocity) {
|
||||||
if (!_isActive || _dismissController.isAnimating)
|
if (!_isActive || _dismissController.isAnimating)
|
||||||
return;
|
return;
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -219,7 +219,7 @@ class _DismissableState extends State<Dismissable> {
|
|||||||
if (_dismissController.isCompleted) {
|
if (_dismissController.isCompleted) {
|
||||||
_startResizeAnimation();
|
_startResizeAnimation();
|
||||||
} else if (_isFlingGesture(velocity)) {
|
} else if (_isFlingGesture(velocity)) {
|
||||||
double flingVelocity = _directionIsYAxis ? velocity.dy : velocity.dx;
|
double flingVelocity = _directionIsYAxis ? velocity.pixelsPerSecond.dy : velocity.pixelsPerSecond.dx;
|
||||||
_dragExtent = flingVelocity.sign;
|
_dragExtent = flingVelocity.sign;
|
||||||
_dismissController.fling(velocity: flingVelocity.abs() * _kFlingVelocityScale);
|
_dismissController.fling(velocity: flingVelocity.abs() * _kFlingVelocityScale);
|
||||||
} else if (_dismissController.value > _kDismissCardThreshold) {
|
} else if (_dismissController.value > _kDismissCardThreshold) {
|
||||||
|
@ -396,7 +396,7 @@ class _DragAvatar<T> extends Drag {
|
|||||||
_position += offset;
|
_position += offset;
|
||||||
update(_position);
|
update(_position);
|
||||||
}
|
}
|
||||||
void end(Offset velocity) {
|
void end(Velocity velocity) {
|
||||||
finish(_DragEndKind.dropped);
|
finish(_DragEndKind.dropped);
|
||||||
}
|
}
|
||||||
void cancel() {
|
void cancel() {
|
||||||
|
@ -25,7 +25,8 @@ export 'package:flutter/gestures.dart' show
|
|||||||
GesturePanEndCallback,
|
GesturePanEndCallback,
|
||||||
GestureScaleStartCallback,
|
GestureScaleStartCallback,
|
||||||
GestureScaleUpdateCallback,
|
GestureScaleUpdateCallback,
|
||||||
GestureScaleEndCallback;
|
GestureScaleEndCallback,
|
||||||
|
Velocity;
|
||||||
|
|
||||||
/// A widget that detects gestures.
|
/// A widget that detects gestures.
|
||||||
///
|
///
|
||||||
@ -414,7 +415,7 @@ class _GestureSemantics extends OneChildRenderObjectWidget {
|
|||||||
if (onHorizontalDragUpdate != null)
|
if (onHorizontalDragUpdate != null)
|
||||||
onHorizontalDragUpdate(delta);
|
onHorizontalDragUpdate(delta);
|
||||||
if (onHorizontalDragEnd != null)
|
if (onHorizontalDragEnd != null)
|
||||||
onHorizontalDragEnd(Offset.zero);
|
onHorizontalDragEnd(Velocity.zero);
|
||||||
} else {
|
} else {
|
||||||
assert(_watchingPans);
|
assert(_watchingPans);
|
||||||
if (onPanStart != null)
|
if (onPanStart != null)
|
||||||
@ -422,7 +423,7 @@ class _GestureSemantics extends OneChildRenderObjectWidget {
|
|||||||
if (onPanUpdate != null)
|
if (onPanUpdate != null)
|
||||||
onPanUpdate(new Offset(delta, 0.0));
|
onPanUpdate(new Offset(delta, 0.0));
|
||||||
if (onPanEnd != null)
|
if (onPanEnd != null)
|
||||||
onPanEnd(Offset.zero);
|
onPanEnd(Velocity.zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,7 +434,7 @@ class _GestureSemantics extends OneChildRenderObjectWidget {
|
|||||||
if (onVerticalDragUpdate != null)
|
if (onVerticalDragUpdate != null)
|
||||||
onVerticalDragUpdate(delta);
|
onVerticalDragUpdate(delta);
|
||||||
if (onVerticalDragEnd != null)
|
if (onVerticalDragEnd != null)
|
||||||
onVerticalDragEnd(Offset.zero);
|
onVerticalDragEnd(Velocity.zero);
|
||||||
} else {
|
} else {
|
||||||
assert(_watchingPans);
|
assert(_watchingPans);
|
||||||
if (onPanStart != null)
|
if (onPanStart != null)
|
||||||
@ -441,7 +442,7 @@ class _GestureSemantics extends OneChildRenderObjectWidget {
|
|||||||
if (onPanUpdate != null)
|
if (onPanUpdate != null)
|
||||||
onPanUpdate(new Offset(0.0, delta));
|
onPanUpdate(new Offset(0.0, delta));
|
||||||
if (onPanEnd != null)
|
if (onPanEnd != null)
|
||||||
onPanEnd(Offset.zero);
|
onPanEnd(Velocity.zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,8 +450,8 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
scrollBy(pixelOffsetToScrollOffset(delta));
|
scrollBy(pixelOffsetToScrollOffset(delta));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _handleDragEnd(Offset velocity) {
|
Future _handleDragEnd(Velocity velocity) {
|
||||||
double scrollVelocity = pixelDeltaToScrollOffset(velocity) / Duration.MILLISECONDS_PER_SECOND;
|
double scrollVelocity = pixelDeltaToScrollOffset(velocity.pixelsPerSecond) / Duration.MILLISECONDS_PER_SECOND;
|
||||||
// The gesture velocity properties are pixels/second, config min,max limits are pixels/ms
|
// The gesture velocity properties are pixels/second, config min,max limits are pixels/ms
|
||||||
return fling(scrollVelocity.clamp(-kMaxFlingVelocity, kMaxFlingVelocity)).then((_) {
|
return fling(scrollVelocity.clamp(-kMaxFlingVelocity, kMaxFlingVelocity)).then((_) {
|
||||||
dispatchOnScrollEnd();
|
dispatchOnScrollEnd();
|
||||||
|
@ -58,7 +58,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> {
|
|||||||
_lastPointerDownLocation = null;
|
_lastPointerDownLocation = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void _handlePanEnd(Offset velocity) {
|
void _handlePanEnd(Velocity velocity) {
|
||||||
assert(_lastPointerDownLocation != null);
|
assert(_lastPointerDownLocation != null);
|
||||||
_SemanticsDebuggerListener.instance.handlePanEnd(_lastPointerDownLocation, velocity);
|
_SemanticsDebuggerListener.instance.handlePanEnd(_lastPointerDownLocation, velocity);
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -329,16 +329,18 @@ class _SemanticsDebuggerListener implements mojom.SemanticsListener {
|
|||||||
void handleLongPress(Point position) {
|
void handleLongPress(Point position) {
|
||||||
_server.longPress(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeLongPressed)?.id ?? 0);
|
_server.longPress(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeLongPressed)?.id ?? 0);
|
||||||
}
|
}
|
||||||
void handlePanEnd(Point position, Offset velocity) {
|
void handlePanEnd(Point position, Velocity velocity) {
|
||||||
if (velocity.dx.abs() == velocity.dy.abs())
|
double vx = velocity.pixelsPerSecond.dx;
|
||||||
|
double vy = velocity.pixelsPerSecond.dy;
|
||||||
|
if (vx.abs() == vy.abs())
|
||||||
return;
|
return;
|
||||||
if (velocity.dx.abs() > velocity.dy.abs()) {
|
if (vx.abs() > vy.abs()) {
|
||||||
if (velocity.dx.sign < 0)
|
if (vx.sign < 0)
|
||||||
_server.scrollLeft(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledHorizontally)?.id ?? 0);
|
_server.scrollLeft(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledHorizontally)?.id ?? 0);
|
||||||
else
|
else
|
||||||
_server.scrollRight(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledHorizontally)?.id ?? 0);
|
_server.scrollRight(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledHorizontally)?.id ?? 0);
|
||||||
} else {
|
} else {
|
||||||
if (velocity.dy.sign < 0)
|
if (vy.sign < 0)
|
||||||
_server.scrollUp(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledVertically)?.id ?? 0);
|
_server.scrollUp(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledVertically)?.id ?? 0);
|
||||||
else
|
else
|
||||||
_server.scrollDown(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledVertically)?.id ?? 0);
|
_server.scrollDown(_hitTest(position, (_SemanticsDebuggerEntry entry) => entry.canBeScrolledVertically)?.id ?? 0);
|
||||||
|
@ -30,7 +30,7 @@ void main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool didEndPan = false;
|
bool didEndPan = false;
|
||||||
pan.onEnd = (Offset velocity) {
|
pan.onEnd = (Velocity velocity) {
|
||||||
didEndPan = true;
|
didEndPan = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ bool _withinTolerance(double actual, double expected) {
|
|||||||
return diff.abs() < kTolerance;
|
return diff.abs() < kTolerance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _checkVelocity(Offset actual, Offset expected) {
|
bool _checkVelocity(Velocity actual, Offset expected) {
|
||||||
return (actual != null)
|
return (actual != null)
|
||||||
&& _withinTolerance(actual.dx, expected.dx)
|
&& _withinTolerance(actual.pixelsPerSecond.dx, expected.dx)
|
||||||
&& _withinTolerance(actual.dy, expected.dy);
|
&& _withinTolerance(actual.pixelsPerSecond.dy, expected.dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -22,7 +22,7 @@ void main() {
|
|||||||
onVerticalDragUpdate: (double scrollDelta) {
|
onVerticalDragUpdate: (double scrollDelta) {
|
||||||
updatedDragDelta = scrollDelta;
|
updatedDragDelta = scrollDelta;
|
||||||
},
|
},
|
||||||
onVerticalDragEnd: (Offset velocity) {
|
onVerticalDragEnd: (Velocity velocity) {
|
||||||
didEndDrag = true;
|
didEndDrag = true;
|
||||||
},
|
},
|
||||||
child: new Container(
|
child: new Container(
|
||||||
@ -73,9 +73,9 @@ void main() {
|
|||||||
|
|
||||||
Widget widget = new GestureDetector(
|
Widget widget = new GestureDetector(
|
||||||
onVerticalDragUpdate: (double delta) { dragDistance += delta; },
|
onVerticalDragUpdate: (double delta) { dragDistance += delta; },
|
||||||
onVerticalDragEnd: (Offset velocity) { gestureCount += 1; },
|
onVerticalDragEnd: (Velocity velocity) { gestureCount += 1; },
|
||||||
onHorizontalDragUpdate: (_) { fail("gesture should not match"); },
|
onHorizontalDragUpdate: (_) { fail("gesture should not match"); },
|
||||||
onHorizontalDragEnd: (Offset velocity) { fail("gesture should not match"); },
|
onHorizontalDragEnd: (Velocity velocity) { fail("gesture should not match"); },
|
||||||
child: new Container(
|
child: new Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
backgroundColor: const Color(0xFF00FF00)
|
backgroundColor: const Color(0xFF00FF00)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user