目标:制作一个通用组件,支持捏合缩放设备和滚动鼠标缩放编辑器。捏合时,将枢轴设置为两个手指的中点,这样它就会围绕您捏合的位置缩放。
我在下面提供了我的脚本。然而,当它“跳跃”或抖动很多时,它在两根手指捏合的设备上的滚动视图中效果不佳。不过,它可以在编辑器中使用鼠标滚动。我认为原因可能与滚动视图内部更新布局的方式有关。在单帧中,我的脚本更改了内容的轴心和位置,但是滚动视图无法正确处理这些更改。这就是为什么我还使用 1 LateUpdate、2ield WaitForEndOfFrame、3 在滚动视图的 OnValueChanged 事件中注册回调来测试我的脚本,但都失败了。
有谁知道如何解决我的脚本中的问题或任何其他新的解决方案如何制作滚动视图以支持捏合缩放?谢谢!
我的代码是这样的:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PinchZoom : MonoBehaviour {
public float zoomSpeedPinch = 0.001f;
public float zoomSpeedMouseScrollWheel = 0.05f;
public float zoomMin = 0.1f;
public float zoomMax = 1f;
RectTransform rectTransform;
public int type = 1; // for device testing type 1 use LateUpdate; type 2 use Update
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
}
//public void OnValueChanged(Vector2 v) // test failed: called by scroll view event
//{
// //Zoom();
//}
void Update()
{
//Zoom();
if (type == 2)
{
if (Input.touchCount == 2)
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, Input.GetTouch(0), Input.GetTouch(1), Input.mousePosition));
else
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, default(Touch), default(Touch), Input.mousePosition));
}
}
private void LateUpdate()
{
if (type == 1) Zoom();
}
void Zoom()
{
var mouseScrollWheel = Input.mouseScrollDelta.y;
float scaleChange = 0f;
Vector2 midPoint = Vector2.zero;
if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = touchDeltaMag - prevTouchDeltaMag;
scaleChange = deltaMagnitudeDiff * zoomSpeedPinch;
midPoint = (touchOne.position + touchZero.position) / 2;
}
if (mouseScrollWheel != 0)
{
scaleChange = mouseScrollWheel * zoomSpeedMouseScrollWheel;
midPoint = Input.mousePosition;
}
if (scaleChange != 0)
{
var scaleX = transform.localScale.x;
scaleX += scaleChange;
scaleX = Mathf.Clamp(scaleX, zoomMin, zoomMax);
var size = rectTransform.rect.size;
size.Scale(rectTransform.localScale);
var parentRect = ((RectTransform)rectTransform.parent);
var parentSize = parentRect.rect.size;
parentSize.Scale(parentRect.localScale);
if (size.x > parentSize.x && size.y > parentSize.y)
{
var p1 = Camera.main.ScreenToWorldPoint(midPoint);
var p2 = transform.InverseTransformPoint(p1);
var pivotP = rectTransform.pivot * rectTransform.rect.size;
var p3 = (Vector2)p2 + pivotP;
var newPivot = p3 / rectTransform.rect.size;
newPivot = new Vector2(Mathf.Clamp01(newPivot.x), Mathf.Clamp01(newPivot.y));
rectTransform.SetPivot(newPivot);
}
else
{
rectTransform.SetPivot(new Vector2(0.5f, 0.5f));
}
transform.localScale = new Vector3(scaleX, scaleX, transform.localScale.z);
}
}
//private IEnumerator ZoomInTheEndOfFrame(float mouseScrollWheel, int touchCount, Touch touchZero, Touch touchOne, Vector3 mousePosition) // testing failed
//{
// yield return new WaitForEndOfFrame();
// ZoomWithData(mouseScrollWheel, touchCount, touchZero, touchOne, mousePosition);
//}
}
为了在不让图像“跳跃”的情况下更改枢轴,我使用了扩展脚本:
using UnityEngine;
public static class RectTranformExtension
{
/// <summary>
/// Set pivot without changing the position of the element
/// </summary>
public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
Vector3 deltaPosition = rectTransform.pivot - pivot; // get change in pivot
deltaPosition.Scale(rectTransform.rect.size); // apply sizing
deltaPosition.Scale(rectTransform.localScale); // apply scaling
deltaPosition = rectTransform.rotation * deltaPosition; // apply rotation
rectTransform.pivot = pivot; // change the pivot
rectTransform.localPosition -= deltaPosition; // reverse the position change
}
}