这里的多点触摸实现方式具有通用性,在提供触摸事件调用和触摸点数据的情况下都可实现。首先,我们需要继承复写ScrollRect的OnDrag方法,让其在一个触摸点的情况下,使用原有拖拽功能,在两个点的情况下,滑动缩放。
private int touchNum = 0;
public override void OnBeginDrag (PointerEventData eventData)
{
if(Input.touchCount > 1)
{
return;
}
base.OnBeginDrag(eventData);
}
public override void OnDrag (PointerEventData eventData)
{
if (Input.touchCount > 1)
{
touchNum = Input.touchCount;
return;
}
else if(Input.touchCount == 1 && touchNum > 1)
{
touchNum = Input.touchCount;
base.OnBeginDrag(eventData);
return;
}
base.OnDrag(eventData);
}
多点滑动的逻辑是写在Update方法里面。经过测试,发现如果写在OnDrag方法里面。会出现放到最大继续快速滑动,或是最小在快速缩小,出现突然缩放的情况。而实际上应该不能继续放大或缩小。
private float preX;
private float preY;
private void Update()
{
if (Input.touchCount == 2)
{
Touch t1 = Input.GetTouch(0);
Touch t2 = Input.GetTouch(1);
Vector3 p1 = t1.position;
Vector3 p2 = t2.position;
float newX = Mathf.Abs(p1.x - p2.x);
float newY = Mathf.Abs(p1.y - p2.y);
if (t1.phase == TouchPhase.Began || t2.phase == TouchPhase.Began)
{
preX = newX;
preY = newY;
}
else if (t1.phase == TouchPhase.Moved && t2.phase == TouchPhase.Moved)
{
RectTransform rt = base.content;
float scale = (newX + newY - preX - preY) / (rt.rect.width * 0.25f) + rt.localScale.x;
if (scale > 1.0f && scale < 2.5f)
{
float ratio = scale / rt.localScale.x;
rt.localScale = new Vector3(scale, scale, 0);
float maxX = base.content.rect.width * scale / 2 - this.viewRect.rect.width / 2;
float minX = -maxX;
float maxY = base.content.rect.height * scale / 2 - this.viewRect.rect.height / 2;
float minY = -maxY;
Vector3 pos = rt.position * ratio;
if (pos.x > maxX)
{
pos.x = maxX;
}
else if (pos.x < minX)
{
pos.x = minX;
}
if (pos.y > maxY)
{
pos.y = maxY;
}
else if (pos.y < minY)
{
pos.y = minY;
}
rt.position = pos;
}
}
preX = newX;
preY = newY;
}
}
float newX = Mathf.Abs(p1.x - p2.x);
float newY = Mathf.Abs(p1.y - p2.y);
这里我们计算的是两个触摸点坐标差值的绝对值。用来粗略的判断两个点的位置关系。
t1.phase == TouchPhase.Began || t2.phase == TouchPhase.Began
这个条件判断是为了,在移动条件达成时候,初始化前一个坐标点的数据。
float scale = (newX + newY - preX - preY) / (rt.rect.width * 0.25f) + rt.localScale.x;
这是一个粗略的判断算法。用新的xy差值减去上次的xy差值,这个结果反映了两个手指的距离变化。然后除以rt.rect.width,得到这个距离变化与缩放目标宽度的比例。0.25f是一个调整系数,用来控制scale的变化率。这里的数值就是根据手势得到的缩放率,然后加上原有的缩放数值,得到的scale就是最终需要缩放的数值。
scale > 1.0f && scale < 2.5f
这个条件约束了,最终的缩放的范围。
后面的算法,做了两个事情。
设置最终的缩放,然后计算边界的约束,让缩放后位置仍然在边界内。不然,我放大拖到边界,然后缩小就会越界的情况。
根据缩放的倍率,来设置新的位置。比如,原有位置不在00点,缩放了,那么位置也缩放同一个倍率,就能让缩放中心同步出现在手势操作位置上,而不是物体中心。
float ratio = scale / rt.localScale.x;
这里新的缩放值除以原有缩放值,得到的就是缩放的倍率。
Vector3 pos = rt.position * ratio;
用原有的位置坐标乘以缩放倍率,得到新的位置。
float maxX = base.content.rect.width * scale / 2 - this.viewRect.rect.width / 2;
float minX = -maxX;
float maxY = base.content.rect.height * scale / 2 - this.viewRect.rect.height / 2;
float minY = -maxY;
这里是计算位置的边界。
if (pos.x > maxX) {
pos.x = maxX;
} else if (pos.x < minX) {
pos.x = minX;
}
if (pos.y > maxY) {
pos.y = maxY;
} else if (pos.y < minY) {
pos.y = minY;
}
rt.position = pos;
根据边界计算新的位置坐标。