GitHub
6 年前
当前提交
90af3128
共有 6 个文件被更改,包括 558 次插入 和 22 次删除
-
2Runtime/gestures/constants.cs
-
80Runtime/widgets/gesture_detector.cs
-
412Runtime/gestures/scale.cs
-
11Runtime/gestures/scale.cs.meta
-
64Samples/UIWidgetSample/ScaleGestureSample.cs
-
11Samples/UIWidgetSample/ScaleGestureSample.cs.meta
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.gestures { |
|||
enum _ScaleState { |
|||
ready, |
|||
possible, |
|||
accepted, |
|||
started |
|||
} |
|||
|
|||
public class ScaleStartDetails { |
|||
public ScaleStartDetails(Offset focalPoint = null) { |
|||
this.focalPoint = focalPoint ?? Offset.zero; |
|||
} |
|||
|
|||
public readonly Offset focalPoint; |
|||
|
|||
public override string ToString() { |
|||
return $"ScaleStartDetails(focalPoint: {this.focalPoint}"; |
|||
} |
|||
} |
|||
|
|||
|
|||
public class ScaleUpdateDetails { |
|||
public ScaleUpdateDetails( |
|||
Offset focalPoint = null, |
|||
float scale = 1.0f, |
|||
float horizontalScale = 1.0f, |
|||
float verticalScale = 1.0f, |
|||
float rotation = 0.0f |
|||
) { |
|||
focalPoint = focalPoint ?? Offset.zero; |
|||
|
|||
D.assert(scale >= 0.0f); |
|||
D.assert(horizontalScale >= 0.0f); |
|||
D.assert(verticalScale >= 0.0f); |
|||
|
|||
this.focalPoint = focalPoint; |
|||
this.scale = scale; |
|||
this.horizontalScale = horizontalScale; |
|||
this.verticalScale = verticalScale; |
|||
this.rotation = rotation; |
|||
} |
|||
|
|||
public readonly Offset focalPoint; |
|||
|
|||
public readonly float scale; |
|||
|
|||
public readonly float horizontalScale; |
|||
|
|||
public readonly float verticalScale; |
|||
|
|||
public readonly float rotation; |
|||
|
|||
public override string ToString() { |
|||
return |
|||
$"ScaleUpdateDetails(focalPoint: {this.focalPoint}, scale: {this.scale}, horizontalScale: {this.horizontalScale}, verticalScale: {this.verticalScale}, rotation: {this.rotation}"; |
|||
} |
|||
} |
|||
|
|||
public class ScaleEndDetails { |
|||
public ScaleEndDetails(Velocity velocity = null) { |
|||
this.velocity = velocity ?? Velocity.zero; |
|||
} |
|||
|
|||
public readonly Velocity velocity; |
|||
|
|||
public override string ToString() { |
|||
return $"ScaleEndDetails(velocity: {this.velocity}"; |
|||
} |
|||
} |
|||
|
|||
public delegate void GestureScaleStartCallback(ScaleStartDetails details); |
|||
|
|||
public delegate void GestureScaleUpdateCallback(ScaleUpdateDetails details); |
|||
|
|||
public delegate void GestureScaleEndCallback(ScaleEndDetails details); |
|||
|
|||
static class _ScaleGestureUtils { |
|||
public static bool _isFlingGesture(Velocity velocity) { |
|||
D.assert(velocity != null); |
|||
float speedSquared = velocity.pixelsPerSecond.distanceSquared; |
|||
return speedSquared > Constants.kMinFlingVelocity * Constants.kMinFlingVelocity; |
|||
} |
|||
} |
|||
|
|||
class _LineBetweenPointers { |
|||
public _LineBetweenPointers( |
|||
Offset pointerStartLocation = null, |
|||
int pointerStartId = 0, |
|||
Offset pointerEndLocation = null, |
|||
int pointerEndId = 1) { |
|||
pointerStartLocation = pointerEndLocation ?? Offset.zero; |
|||
pointerEndLocation = pointerEndLocation ?? Offset.zero; |
|||
|
|||
D.assert(pointerStartId != pointerEndId); |
|||
|
|||
this.pointerStartLocation = pointerStartLocation; |
|||
this.pointerStartId = pointerStartId; |
|||
this.pointerEndLocation = pointerEndLocation; |
|||
this.pointerEndId = pointerEndId; |
|||
} |
|||
|
|||
public readonly Offset pointerStartLocation; |
|||
|
|||
public readonly int pointerStartId; |
|||
|
|||
public readonly Offset pointerEndLocation; |
|||
|
|||
public readonly int pointerEndId; |
|||
} |
|||
|
|||
|
|||
public class ScaleGestureRecognizer : OneSequenceGestureRecognizer { |
|||
public ScaleGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public GestureScaleStartCallback onStart; |
|||
|
|||
public GestureScaleUpdateCallback onUpdate; |
|||
|
|||
public GestureScaleEndCallback onEnd; |
|||
|
|||
_ScaleState _state = _ScaleState.ready; |
|||
|
|||
Offset _initialFocalPoint; |
|||
Offset _currentFocalPoint; |
|||
float _initialSpan; |
|||
float _currentSpan; |
|||
float _initialHorizontalSpan; |
|||
float _currentHorizontalSpan; |
|||
float _initialVerticalSpan; |
|||
float _currentVerticalSpan; |
|||
_LineBetweenPointers _initialLine; |
|||
_LineBetweenPointers _currentLine; |
|||
Dictionary<int, Offset> _pointerLocations; |
|||
List<int> _pointerQueue; |
|||
readonly Dictionary<int, VelocityTracker> _velocityTrackers = new Dictionary<int, VelocityTracker>(); |
|||
|
|||
float _scaleFactor { |
|||
get { return this._initialSpan > 0.0f ? this._currentSpan / this._initialSpan : 1.0f; } |
|||
} |
|||
|
|||
float _horizontalScaleFactor { |
|||
get { |
|||
return this._initialHorizontalSpan > 0.0f |
|||
? this._currentHorizontalSpan / this._initialHorizontalSpan |
|||
: 1.0f; |
|||
} |
|||
} |
|||
|
|||
float _verticalScaleFactor { |
|||
get { |
|||
return this._initialVerticalSpan > 0.0f ? this._currentVerticalSpan / this._initialVerticalSpan : 1.0f; |
|||
} |
|||
} |
|||
|
|||
float _computeRotationFactor() { |
|||
if (this._initialLine == null || this._currentLine == null) { |
|||
return 0.0f; |
|||
} |
|||
|
|||
float fx = this._initialLine.pointerStartLocation.dx; |
|||
float fy = this._initialLine.pointerStartLocation.dy; |
|||
float sx = this._initialLine.pointerEndLocation.dx; |
|||
float sy = this._initialLine.pointerEndLocation.dy; |
|||
|
|||
float nfx = this._currentLine.pointerStartLocation.dx; |
|||
float nfy = this._currentLine.pointerStartLocation.dy; |
|||
float nsx = this._currentLine.pointerEndLocation.dx; |
|||
float nsy = this._currentLine.pointerEndLocation.dy; |
|||
|
|||
float angle1 = Mathf.Atan2(fy - sy, fx - sx); |
|||
float angle2 = Mathf.Atan2(nfy - nsy, nfx - nsx); |
|||
|
|||
return angle2 - angle1; |
|||
} |
|||
|
|||
public override void addAllowedPointer(PointerDownEvent evt) { |
|||
this.startTrackingPointer(evt.pointer); |
|||
this._velocityTrackers[evt.pointer] = new VelocityTracker(); |
|||
if (this._state == _ScaleState.ready) { |
|||
this._state = _ScaleState.possible; |
|||
this._initialSpan = 0.0f; |
|||
this._currentSpan = 0.0f; |
|||
this._initialHorizontalSpan = 0.0f; |
|||
this._currentHorizontalSpan = 0.0f; |
|||
this._initialVerticalSpan = 0.0f; |
|||
this._currentVerticalSpan = 0.0f; |
|||
this._pointerLocations = new Dictionary<int, Offset>(); |
|||
this._pointerQueue = new List<int>(); |
|||
} |
|||
} |
|||
|
|||
protected override void handleEvent(PointerEvent evt) { |
|||
D.assert(this._state != _ScaleState.ready); |
|||
bool didChangeConfiguration = false; |
|||
bool shouldStartIfAccepted = false; |
|||
|
|||
if (evt is PointerMoveEvent) { |
|||
VelocityTracker tracker = this._velocityTrackers[evt.pointer]; |
|||
D.assert(tracker != null); |
|||
if (!evt.synthesized) { |
|||
tracker.addPosition(evt.timeStamp, evt.position); |
|||
} |
|||
|
|||
this._pointerLocations[evt.pointer] = evt.position; |
|||
shouldStartIfAccepted = true; |
|||
} |
|||
else if (evt is PointerDownEvent) { |
|||
this._pointerLocations[evt.pointer] = evt.position; |
|||
this._pointerQueue.Add(evt.pointer); |
|||
didChangeConfiguration = true; |
|||
shouldStartIfAccepted = true; |
|||
} |
|||
else if (evt is PointerUpEvent || evt is PointerCancelEvent) { |
|||
this._pointerLocations.Remove(evt.pointer); |
|||
this._pointerQueue.Remove(evt.pointer); |
|||
didChangeConfiguration = true; |
|||
} |
|||
|
|||
this._updateLines(); |
|||
this._update(); |
|||
|
|||
if (!didChangeConfiguration || this._reconfigure(evt.pointer)) { |
|||
this._advanceStateMachine(shouldStartIfAccepted); |
|||
} |
|||
|
|||
this.stopTrackingIfPointerNoLongerDown(evt); |
|||
} |
|||
|
|||
void _update() { |
|||
int count = this._pointerLocations.Keys.Count; |
|||
|
|||
Offset focalPoint = Offset.zero; |
|||
foreach (int pointer in this._pointerLocations.Keys) { |
|||
focalPoint += this._pointerLocations[pointer]; |
|||
} |
|||
|
|||
this._currentFocalPoint = count > 0 ? focalPoint / count : Offset.zero; |
|||
|
|||
float totalDeviation = 0.0f; |
|||
float totalHorizontalDeviation = 0.0f; |
|||
float totalVerticalDeviation = 0.0f; |
|||
|
|||
foreach (int pointer in this._pointerLocations.Keys) { |
|||
totalDeviation += (this._currentFocalPoint - this._pointerLocations[pointer]).distance; |
|||
totalHorizontalDeviation += (this._currentFocalPoint.dx - this._pointerLocations[pointer].dx).abs(); |
|||
totalVerticalDeviation += (this._currentFocalPoint.dy - this._pointerLocations[pointer].dy).abs(); |
|||
} |
|||
|
|||
this._currentSpan = count > 0 ? totalDeviation / count : 0.0f; |
|||
this._currentHorizontalSpan = count > 0 ? totalHorizontalDeviation / count : 0.0f; |
|||
this._currentVerticalSpan = count > 0 ? totalVerticalDeviation / count : 0.0f; |
|||
} |
|||
|
|||
void _updateLines() { |
|||
int count = this._pointerLocations.Keys.Count; |
|||
D.assert(this._pointerQueue.Count >= count); |
|||
|
|||
if (count < 2) { |
|||
this._initialLine = this._currentLine; |
|||
} |
|||
else if (this._initialLine != null && |
|||
this._initialLine.pointerStartId == this._pointerQueue[0] && |
|||
this._initialLine.pointerEndId == this._pointerQueue[1]) { |
|||
this._currentLine = new _LineBetweenPointers( |
|||
pointerStartId: this._pointerQueue[0], |
|||
pointerStartLocation: this._pointerLocations[this._pointerQueue[0]], |
|||
pointerEndId: this._pointerQueue[1], |
|||
pointerEndLocation: this._pointerLocations[this._pointerQueue[1]] |
|||
); |
|||
} |
|||
else { |
|||
this._initialLine = new _LineBetweenPointers( |
|||
pointerStartId: this._pointerQueue[0], |
|||
pointerStartLocation: this._pointerLocations[this._pointerQueue[0]], |
|||
pointerEndId: this._pointerQueue[1], |
|||
pointerEndLocation: this._pointerLocations[this._pointerQueue[1]] |
|||
); |
|||
this._currentLine = null; |
|||
} |
|||
} |
|||
|
|||
bool _reconfigure(int pointer) { |
|||
this._initialFocalPoint = this._currentFocalPoint; |
|||
this._initialSpan = this._currentSpan; |
|||
this._initialLine = this._currentLine; |
|||
this._initialHorizontalSpan = this._currentHorizontalSpan; |
|||
this._initialVerticalSpan = this._currentVerticalSpan; |
|||
if (this._state == _ScaleState.started) { |
|||
if (this.onEnd != null) { |
|||
VelocityTracker tracker = this._velocityTrackers[pointer]; |
|||
D.assert(tracker != null); |
|||
|
|||
Velocity velocity = tracker.getVelocity(); |
|||
if (_ScaleGestureUtils._isFlingGesture(velocity)) { |
|||
Offset pixelsPerSecond = velocity.pixelsPerSecond; |
|||
if (pixelsPerSecond.distanceSquared > |
|||
Constants.kMaxFlingVelocity * Constants.kMaxFlingVelocity) { |
|||
velocity = new Velocity( |
|||
pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * |
|||
Constants.kMaxFlingVelocity); |
|||
} |
|||
|
|||
this.invokeCallback<object>("onEnd", () => { |
|||
this.onEnd(new ScaleEndDetails(velocity: velocity)); |
|||
return null; |
|||
}); |
|||
} |
|||
else { |
|||
this.invokeCallback<object>("onEnd", () => { |
|||
this.onEnd(new ScaleEndDetails(velocity: Velocity.zero)); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
this._state = _ScaleState.accepted; |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void _advanceStateMachine(bool shouldStartIfAccepted) { |
|||
if (this._state == _ScaleState.ready) { |
|||
this._state = _ScaleState.possible; |
|||
} |
|||
|
|||
if (this._state == _ScaleState.possible) { |
|||
float spanDelta = (this._currentSpan - this._initialSpan).abs(); |
|||
float focalPointDelta = (this._currentFocalPoint - this._initialFocalPoint).distance; |
|||
if (spanDelta > Constants.kScaleSlop || focalPointDelta > Constants.kPanSlop) { |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
} |
|||
else if (this._state >= _ScaleState.accepted) { |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
|
|||
if (this._state == _ScaleState.accepted && shouldStartIfAccepted) { |
|||
this._state = _ScaleState.started; |
|||
this._dispatchOnStartCallbackIfNeeded(); |
|||
} |
|||
|
|||
if (this._state == _ScaleState.started && this.onUpdate != null) { |
|||
this.invokeCallback<object>("onUpdate", () => { |
|||
this.onUpdate(new ScaleUpdateDetails( |
|||
scale: this._scaleFactor, |
|||
horizontalScale: this._horizontalScaleFactor, |
|||
verticalScale: this._verticalScaleFactor, |
|||
focalPoint: this._currentFocalPoint, |
|||
rotation: this._computeRotationFactor() |
|||
)); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void _dispatchOnStartCallbackIfNeeded() { |
|||
D.assert(this._state == _ScaleState.started); |
|||
if (this.onStart != null) { |
|||
this.invokeCallback<object>("onStart", () => { |
|||
this.onStart(new ScaleStartDetails(focalPoint: this._currentFocalPoint)); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
if (this._state == _ScaleState.possible) { |
|||
this._state = _ScaleState.started; |
|||
this._dispatchOnStartCallbackIfNeeded(); |
|||
} |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
this.stopTrackingPointer(pointer); |
|||
} |
|||
|
|||
protected override void didStopTrackingLastPointer(int pointer) { |
|||
switch (this._state) { |
|||
case _ScaleState.possible: |
|||
this.resolve(GestureDisposition.rejected); |
|||
break; |
|||
case _ScaleState.ready: |
|||
D.assert(false); |
|||
break; |
|||
case _ScaleState.accepted: |
|||
break; |
|||
case _ScaleState.started: |
|||
D.assert(false); |
|||
break; |
|||
} |
|||
|
|||
this._state = _ScaleState.ready; |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._velocityTrackers.Clear(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "scale"; } |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6408adf8aeb2a4ac38ee9f49dc748c2a |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgetsSample { |
|||
public class ScaleGestureSample : UIWidgetsSamplePanel { |
|||
protected override Widget createWidget() { |
|||
return new MaterialApp( |
|||
showPerformanceOverlay: false, |
|||
home: new ScaleGesturePanel() |
|||
); |
|||
} |
|||
|
|||
protected override void OnEnable() { |
|||
FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"), "Material Icons"); |
|||
base.OnEnable(); |
|||
} |
|||
} |
|||
|
|||
class ScaleGesturePanel : StatefulWidget { |
|||
public override State createState() { |
|||
return new ScaleGesturePanelState(); |
|||
} |
|||
} |
|||
|
|||
class ScaleGesturePanelState : State<ScaleGesturePanel> { |
|||
float scaleValue = 1.0f; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Scaffold( |
|||
appBar: new AppBar( |
|||
title: new Center( |
|||
child: new Text("Test Scale Gesture Widget") |
|||
) |
|||
), |
|||
body: new GestureDetector( |
|||
onScaleStart: scaleDetails => { Debug.Log("Scale Start !"); }, |
|||
onScaleUpdate: scaleDetails => { |
|||
Debug.Log("Scale value = " + scaleDetails.scale); |
|||
this.setState(() => { this.scaleValue = scaleDetails.scale; }); |
|||
}, |
|||
onScaleEnd: scaleDetails => { Debug.Log("Scale End"); }, |
|||
child: new Card( |
|||
color: Colors.white, |
|||
child: new Center( |
|||
child: new Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
crossAxisAlignment: CrossAxisAlignment.center, |
|||
children: new List<Widget> { |
|||
new Icon(Unity.UIWidgets.material.Icons.ac_unit, size: 128.0f, color: Colors.black), |
|||
new RaisedButton( |
|||
child: new Text("Scale: " + this.scaleValue), |
|||
onPressed: () => { Debug.Log("Button Pressed"); }) |
|||
} |
|||
) |
|||
)) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 38800d11bb7a9447582e892145b3bd82 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue