You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity UI:移动端UI Image捏合缩放功能实现求助

我之前也碰到过类似的需求——Sprite Render的缩放方案确实顺手,但UI Image因为属于UGUI体系,坐标和交互逻辑都不一样,用ScrollRect+Mask来实现缩放是个靠谱的思路,我来给你拆解下具体怎么做:

实现UI Image的移动端双指缩放(基于ScrollRect+Mask)

1. 搭建基础UI结构

首先在Canvas下创建如下层级:

  • ScrollRect(添加Mask组件,勾选"Show Mask Graphic"可选)
    • Viewport(ScrollRect默认自带,确保它的RectTransform和你想要显示的区域一致)
      • Content(ScrollRect默认自带,把你的UI Image作为它的子物体)
        • TargetImage(你要缩放的UI Image)

注意点:

  • 把ScrollRect的Horizontal和Vertical滚动都勾选上,后续可保留滚动+缩放的组合交互
  • TargetImage的锚点设为(0.5, 0.5),让它中心点对齐Content,方便后续缩放定位
  • Content的RectTransform大小要设为TargetImage的原始尺寸(或更大),确保缩放时不会出现空白区域

2. 编写缩放交互脚本

给ScrollRect挂载一个C#脚本,处理移动端双指触摸的缩放逻辑,核心是计算缩放比例、调整Content的localScale,同时保证缩放中心是双指中点,避免画面偏移:

using UnityEngine;
using UnityEngine.UI;

public class UIImageZoom : MonoBehaviour
{
    private ScrollRect _scrollRect;
    private RectTransform _contentRect;
    private Vector2 _lastTouchPos1;
    private Vector2 _lastTouchPos2;
    private float _lastDistance;
    public float minScale = 1f;
    public float maxScale = 3f;

    void Awake()
    {
        _scrollRect = GetComponent<ScrollRect>();
        _contentRect = _scrollRect.content;
        // 初始化缩放为原始大小
        _contentRect.localScale = Vector3.one;
    }

    void Update()
    {
        if (Input.touchCount == 2)
        {
            Touch touch1 = Input.GetTouch(0);
            Touch touch2 = Input.GetTouch(1);

            // 计算当前双指距离
            Vector2 currentPos1 = touch1.position;
            Vector2 currentPos2 = touch2.position;
            float currentDistance = Vector2.Distance(currentPos1, currentPos2);

            if (touch1.phase == TouchPhase.Began || touch2.phase == TouchPhase.Began)
            {
                // 记录初始距离和触摸位置
                _lastDistance = currentDistance;
                _lastTouchPos1 = currentPos1;
                _lastTouchPos2 = currentPos2;
            }
            else if (touch1.phase == TouchPhase.Moved && touch2.phase == TouchPhase.Moved)
            {
                // 计算缩放因子
                float scaleFactor = currentDistance / _lastDistance;
                Vector3 newScale = _contentRect.localScale * scaleFactor;
                // 限制缩放范围,避免过度缩放
                newScale = Vector3.ClampMagnitude(newScale, maxScale);
                newScale = Vector3.Max(newScale, Vector3.one * minScale);

                // 计算双指中点在Canvas上的位置
                Vector2 midPoint = (currentPos1 + currentPos2) / 2;
                // 转换为Content的本地坐标
                RectTransformUtility.ScreenPointToLocalPointInRectangle(
                    _contentRect, midPoint, _scrollRect.GetComponentInParent<Canvas>().worldCamera, out Vector2 localMidPoint);

                // 计算缩放前后中点的偏移,调整Content位置保证缩放中心不变
                Vector3 offset = _contentRect.TransformPoint(localMidPoint) - _contentRect.position;
                _contentRect.position += offset - (_contentRect.localScale / scaleFactor).Multiply(offset);

                // 应用最终缩放比例
                _contentRect.localScale = newScale;

                // 更新上一帧数据,用于下一帧计算
                _lastDistance = currentDistance;
                _lastTouchPos1 = currentPos1;
                _lastTouchPos2 = currentPos2;
            }
        }
    }
}

3. 细节优化

  • 如果你不需要滚动功能,可以把ScrollRect的Horizontal和Vertical取消勾选,只保留缩放交互
  • 调整minScale和maxScale参数,适配你的业务需求,比如最小1倍(原始大小)、最大3倍放大
  • 给TargetImage开启"Preserve Aspect"选项,避免缩放时出现拉伸变形
  • 若需要单指拖动查看缩放后的大图,ScrollRect默认已经支持,只要Content大小超过Viewport即可

这个方案的好处是利用了UGUI原生组件,不需要额外的裁剪逻辑,移动端触摸交互也符合用户习惯,比直接修改Image的scale要更稳定,尤其适合需要配合滚动查看大图的场景。

内容的提问来源于stack exchange,提问作者Nanopl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:11:46