| // Copyright 2014 The Flutter 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 'dart:math'; |
| import 'package:flutter/material.dart'; |
| import 'package:vector_math/vector_math.dart' show Vector2; |
| |
| // Provides calculations for an object moving with inertia and friction using |
| // the equation of motion from physics. |
| // https://en.wikipedia.org/wiki/Equations_of_motion#Constant_translational_acceleration_in_a_straight_line |
| // TODO(justinmc): Can this be replaced with friction_simulation.dart? |
| @immutable |
| class InertialMotion { |
| const InertialMotion(this._initialVelocity, this._initialPosition); |
| |
| static const double _kFrictionalAcceleration = 0.01; // How quickly to stop |
| final Velocity _initialVelocity; |
| final Offset _initialPosition; |
| |
| // The position when the motion stops. |
| Offset get finalPosition { |
| return _getPositionAt(Duration(milliseconds: duration.toInt())); |
| } |
| |
| // The total time that the animation takes start to stop in milliseconds. |
| double get duration { |
| return (_initialVelocity.pixelsPerSecond.dx / 1000 / _acceleration.x).abs(); |
| } |
| |
| // The acceleration opposing the initial velocity in x and y components. |
| Vector2 get _acceleration { |
| // TODO(justinmc): Find actual velocity instead of summing? |
| final double velocityTotal = _initialVelocity.pixelsPerSecond.dx.abs() |
| + _initialVelocity.pixelsPerSecond.dy.abs(); |
| final double vRatioX = _initialVelocity.pixelsPerSecond.dx / velocityTotal; |
| final double vRatioY = _initialVelocity.pixelsPerSecond.dy / velocityTotal; |
| return Vector2( |
| _kFrictionalAcceleration * vRatioX, |
| _kFrictionalAcceleration * vRatioY, |
| ); |
| } |
| |
| // The position at a given time. |
| Offset _getPositionAt(Duration time) { |
| final double xf = _getPosition( |
| r0: _initialPosition.dx, |
| v0: _initialVelocity.pixelsPerSecond.dx / 1000, |
| t: time.inMilliseconds, |
| a: _acceleration.x, |
| ); |
| final double yf = _getPosition( |
| r0: _initialPosition.dy, |
| v0: _initialVelocity.pixelsPerSecond.dy / 1000, |
| t: time.inMilliseconds, |
| a: _acceleration.y, |
| ); |
| return Offset(xf, yf); |
| } |
| |
| // Solve the equation of motion to find the position at a given point in time |
| // in one dimension. |
| double _getPosition({required double r0, required double v0, required int t, required double a}) { |
| // Stop movement when it would otherwise reverse direction. |
| final double stopTime = (v0 / a).abs(); |
| if (t > stopTime) { |
| t = stopTime.toInt(); |
| } |
| |
| return r0 + v0 * t + 0.5 * a * pow(t, 2); |
| } |
| } |