您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
346 行
12 KiB
346 行
12 KiB
using System;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.physics;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.UIWidgets.widgets {
|
|
public class ScrollPhysics {
|
|
public ScrollPhysics(ScrollPhysics parent) {
|
|
this.parent = parent;
|
|
}
|
|
|
|
public readonly ScrollPhysics parent;
|
|
|
|
protected ScrollPhysics buildParent(ScrollPhysics ancestor) {
|
|
if (this.parent == null) {
|
|
return ancestor;
|
|
}
|
|
|
|
return this.parent.applyTo(ancestor) ?? ancestor;
|
|
}
|
|
|
|
public virtual ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new ScrollPhysics(parent: this.buildParent(ancestor));
|
|
}
|
|
|
|
public virtual float applyPhysicsToUserOffset(ScrollMetrics position, float offset) {
|
|
if (this.parent == null) {
|
|
return offset;
|
|
}
|
|
|
|
return this.parent.applyPhysicsToUserOffset(position, offset);
|
|
}
|
|
|
|
public virtual bool shouldAcceptUserOffset(ScrollMetrics position) {
|
|
if (this.parent == null) {
|
|
return position.pixels != 0.0 || position.minScrollExtent != position.maxScrollExtent;
|
|
}
|
|
|
|
return this.parent.shouldAcceptUserOffset(position);
|
|
}
|
|
|
|
public virtual float applyBoundaryConditions(ScrollMetrics position, float value) {
|
|
if (this.parent == null) {
|
|
return 0.0f;
|
|
}
|
|
|
|
return this.parent.applyBoundaryConditions(position, value);
|
|
}
|
|
|
|
public virtual Simulation createBallisticSimulation(ScrollMetrics position, float velocity) {
|
|
if (this.parent == null) {
|
|
return null;
|
|
}
|
|
|
|
return this.parent.createBallisticSimulation(position, velocity);
|
|
}
|
|
|
|
static readonly SpringDescription _kDefaultSpring = SpringDescription.withDampingRatio(
|
|
mass: 0.5f,
|
|
stiffness: 100.0f,
|
|
ratio: 1.1f
|
|
);
|
|
|
|
public virtual SpringDescription spring {
|
|
get {
|
|
if (this.parent == null) {
|
|
return _kDefaultSpring;
|
|
}
|
|
|
|
return this.parent.spring ?? _kDefaultSpring;
|
|
}
|
|
}
|
|
|
|
// todo: Handle the case of the device pixel ratio changing. use 1 as devicePixelRatio for now.
|
|
static readonly Tolerance _kDefaultTolerance = new Tolerance(
|
|
velocity: 1.0f / (0.050f * 1),
|
|
distance: 1.0f / 1
|
|
);
|
|
|
|
public virtual Tolerance tolerance {
|
|
get {
|
|
if (this.parent == null) {
|
|
return _kDefaultTolerance;
|
|
}
|
|
|
|
return this.parent.tolerance ?? _kDefaultTolerance;
|
|
}
|
|
}
|
|
|
|
public virtual float minFlingDistance {
|
|
get {
|
|
if (this.parent == null) {
|
|
return Constants.kTouchSlop;
|
|
}
|
|
|
|
return this.parent.minFlingDistance;
|
|
}
|
|
}
|
|
|
|
public virtual float carriedMomentum(float existingVelocity) {
|
|
if (this.parent == null) {
|
|
return 0.0f;
|
|
}
|
|
|
|
return this.parent.carriedMomentum(existingVelocity);
|
|
}
|
|
|
|
public virtual float minFlingVelocity {
|
|
get {
|
|
if (this.parent == null) {
|
|
return Constants.kMinFlingVelocity;
|
|
}
|
|
|
|
return this.parent.minFlingVelocity;
|
|
}
|
|
}
|
|
|
|
public virtual float maxFlingVelocity {
|
|
get {
|
|
if (this.parent == null) {
|
|
return Constants.kMaxFlingVelocity;
|
|
}
|
|
|
|
return this.parent.maxFlingVelocity;
|
|
}
|
|
}
|
|
|
|
public virtual float? dragStartDistanceMotionThreshold {
|
|
get {
|
|
return this.parent?.dragStartDistanceMotionThreshold;
|
|
}
|
|
}
|
|
|
|
public virtual bool allowImplicitScrolling {
|
|
get { return true; }
|
|
}
|
|
|
|
public override string ToString() {
|
|
if (this.parent == null) {
|
|
return $"{this.GetType()}";
|
|
}
|
|
|
|
return $"{this.GetType()} -> {this.parent}";
|
|
}
|
|
}
|
|
|
|
|
|
public class BouncingScrollPhysics : ScrollPhysics {
|
|
public BouncingScrollPhysics(ScrollPhysics parent = null) : base(parent: parent) {
|
|
}
|
|
|
|
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new BouncingScrollPhysics(parent: this.buildParent(ancestor));
|
|
}
|
|
|
|
public float frictionFactor(float overscrollFraction) {
|
|
return 0.52f * Mathf.Pow(1 - overscrollFraction, 2);
|
|
}
|
|
|
|
public override float applyPhysicsToUserOffset(ScrollMetrics position, float offset) {
|
|
D.assert(position.minScrollExtent <= position.maxScrollExtent);
|
|
|
|
if (!position.outOfRange()) {
|
|
return offset;
|
|
}
|
|
|
|
float overscrollPastStart = Mathf.Max(position.minScrollExtent - position.pixels, 0.0f);
|
|
float overscrollPastEnd = Mathf.Max(position.pixels - position.maxScrollExtent, 0.0f);
|
|
float overscrollPast = Mathf.Max(overscrollPastStart, overscrollPastEnd);
|
|
bool easing = (overscrollPastStart > 0.0f && offset < 0.0f) || (overscrollPastEnd > 0.0f && offset > 0.0f);
|
|
|
|
float friction = easing
|
|
? this.frictionFactor((overscrollPast - offset.abs()) / position.viewportDimension)
|
|
: this.frictionFactor(overscrollPast / position.viewportDimension);
|
|
float direction = offset.sign();
|
|
|
|
return direction * _applyFriction(overscrollPast, offset.abs(), friction);
|
|
}
|
|
|
|
static float _applyFriction(float extentOutside, float absDelta, float gamma) {
|
|
D.assert(absDelta > 0);
|
|
float total = 0.0f;
|
|
if (extentOutside > 0) {
|
|
float deltaToLimit = extentOutside / gamma;
|
|
if (absDelta < deltaToLimit) {
|
|
return absDelta * gamma;
|
|
}
|
|
|
|
total += extentOutside;
|
|
absDelta -= deltaToLimit;
|
|
}
|
|
|
|
return total + absDelta;
|
|
}
|
|
|
|
public override float applyBoundaryConditions(ScrollMetrics position, float value) {
|
|
return 0.0f;
|
|
}
|
|
|
|
public override Simulation createBallisticSimulation(ScrollMetrics position, float velocity) {
|
|
Tolerance tolerance = this.tolerance;
|
|
if (velocity.abs() >= tolerance.velocity || position.outOfRange()) {
|
|
return new BouncingScrollSimulation(
|
|
spring: this.spring,
|
|
position: position.pixels,
|
|
velocity: velocity * 0.91f,
|
|
leadingExtent: position.minScrollExtent,
|
|
trailingExtent: position.maxScrollExtent,
|
|
tolerance: tolerance
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override float minFlingVelocity {
|
|
get { return Constants.kMinFlingVelocity * 2.0f; }
|
|
}
|
|
|
|
public override float carriedMomentum(float existingVelocity) {
|
|
return existingVelocity.sign() *
|
|
Mathf.Min(0.000816f * Mathf.Pow(existingVelocity.abs(), 1.967f), 40000.0f);
|
|
}
|
|
|
|
public override float? dragStartDistanceMotionThreshold {
|
|
get { return 3.5f; }
|
|
}
|
|
}
|
|
|
|
|
|
public class ClampingScrollPhysics : ScrollPhysics {
|
|
public ClampingScrollPhysics(ScrollPhysics parent = null) : base(parent: parent) {
|
|
}
|
|
|
|
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new ClampingScrollPhysics(parent: this.buildParent(ancestor));
|
|
}
|
|
|
|
public override float applyBoundaryConditions(ScrollMetrics position, float value) {
|
|
D.assert(() => {
|
|
if (value == position.pixels) {
|
|
throw new UIWidgetsError(
|
|
$"{this.GetType()}.applyBoundaryConditions() was called redundantly.\n" +
|
|
$"The proposed new position, {value}, is exactly equal to the current position of the " +
|
|
$"given {position.GetType()}, {position.pixels}.\n" +
|
|
"The applyBoundaryConditions method should only be called when the value is " +
|
|
"going to actually change the pixels, otherwise it is redundant.\n" +
|
|
"The physics object in question was:\n" + $" {this}\n" +
|
|
"The position object in question was:\n" + $" {position}\n");
|
|
}
|
|
|
|
return true;
|
|
});
|
|
if (value < position.pixels && position.pixels <= position.minScrollExtent) {
|
|
return value - position.pixels;
|
|
}
|
|
|
|
if (position.maxScrollExtent <= position.pixels && position.pixels < value) {
|
|
return value - position.pixels;
|
|
}
|
|
|
|
if (value < position.minScrollExtent && position.minScrollExtent < position.pixels) {
|
|
return value - position.minScrollExtent;
|
|
}
|
|
|
|
if (position.pixels < position.maxScrollExtent && position.maxScrollExtent < value) {
|
|
return value - position.maxScrollExtent;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
public override Simulation createBallisticSimulation(ScrollMetrics position, float velocity) {
|
|
Tolerance tolerance = this.tolerance;
|
|
if (position.outOfRange()) {
|
|
float? end = null;
|
|
if (position.pixels > position.maxScrollExtent) {
|
|
end = position.maxScrollExtent;
|
|
}
|
|
|
|
if (position.pixels < position.minScrollExtent) {
|
|
end = position.minScrollExtent;
|
|
}
|
|
|
|
D.assert(end != null);
|
|
return new ScrollSpringSimulation(
|
|
this.spring,
|
|
position.pixels,
|
|
position.maxScrollExtent,
|
|
Mathf.Min(0.0f, velocity),
|
|
tolerance: tolerance
|
|
);
|
|
}
|
|
|
|
if (velocity.abs() < tolerance.velocity) {
|
|
return null;
|
|
}
|
|
|
|
if (velocity > 0.0 && position.pixels >= position.maxScrollExtent) {
|
|
return null;
|
|
}
|
|
|
|
if (velocity < 0.0 && position.pixels <= position.minScrollExtent) {
|
|
return null;
|
|
}
|
|
|
|
return new ClampingScrollSimulation(
|
|
position: position.pixels,
|
|
velocity: velocity,
|
|
tolerance: tolerance
|
|
);
|
|
}
|
|
}
|
|
|
|
public class AlwaysScrollableScrollPhysics : ScrollPhysics {
|
|
public AlwaysScrollableScrollPhysics(ScrollPhysics parent = null) : base(parent: parent) {
|
|
}
|
|
|
|
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new AlwaysScrollableScrollPhysics(parent: this.buildParent(ancestor));
|
|
}
|
|
|
|
public override bool shouldAcceptUserOffset(ScrollMetrics position) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class NeverScrollableScrollPhysics : ScrollPhysics {
|
|
public NeverScrollableScrollPhysics(ScrollPhysics parent = null) : base(parent: parent) {
|
|
}
|
|
|
|
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new NeverScrollableScrollPhysics(parent: this.buildParent(ancestor));
|
|
}
|
|
|
|
public override bool shouldAcceptUserOffset(ScrollMetrics position) {
|
|
return false;
|
|
}
|
|
|
|
public override bool allowImplicitScrolling {
|
|
get { return false; }
|
|
}
|
|
}
|
|
}
|