首页 > 解决方案 > 适用于 Android/IOS 的 Canvas 上的 Unity 平滑移动和缩放 UI

问题描述

我为我的任务编写了这个脚本,在论坛上冲浪后我找不到好的决定,所以我为你们修改了代码并发布了这个。也许有人会有用。如果你有一些修改,我会很高兴听到?

标签: androidiosunity3dmovezoom-sdk

解决方案


using UnityEngine;

public class MovableScrollRectFree : MonoBehaviour
{
    [Space(2)][Header("Settings")]
    // Content need set Anchor Presets [Middle & Center]
    [SerializeField] private GameObject Content;
    
    [Space(10)]
    [SerializeField] private float MinZoom = 0.5f;
    [SerializeField] private float MaxZoom = 3f;
    [SerializeField] private float ZoomSpeed = -0.002f;
    [SerializeField] private float MoveSpeed = 1f;

    [Space(10)] 
    [SerializeField] [Range(1, 10)] private int SmoothMove = 6;
    [SerializeField] [Range(1, 10)] private int SmoothScale = 6;
    
    [Space(10)] 
    // Can be zero values
    [SerializeField] private float MinPermissibleHeightOffset;
    [SerializeField] private float MaxPermissibleHeightOffset;
    [SerializeField] private float MinPermissibleWidthOffset;
    [SerializeField] private float MaxPermissibleWidthOffset;

    private RectTransform ContentRect;
    private Vector2 ContentSizeDelta;
    
    private Vector2 TargetScale;
    private Vector2 TargetPosition;

    private float moveMagnitude;
    private float scaleMagnitude;
    
    private float oldMoveMagnitude;
    private float oldScaleMagnitude;
    private float startScaleMagnitude;
    private float newScaleMagnitude;
    
    private Vector2 ContentScaleOnStart;
    private Vector2 ContentPositionOnStart;
    
    private Vector2 startPostiton;
    private Vector2 newPosition;

    private Vector2 stepMove;
    private Vector3 stepScale;
    
    private int avalibleMoveSteps;
    private int avalibleScaleSteps;
    
    Vector2 fingerPosition1;
    Vector2 fingerPosition2;
    
    private bool _isPinching;
    private bool active;

    private void Awake()
    {
        Input.multiTouchEnabled = true;
    }

    void Start()
    {
        ContentRect = Content.GetComponent<RectTransform>();
        ContentSizeDelta = ContentRect.sizeDelta;
    }

    private void Update()
    {
        if (Input.touchCount == 2)
            active = true;
        else if (active)
        {
            active = false;
            TryOutDangerZone();
            _isPinching = false;
        }
    }

    private void FixedUpdate()
    {
        TransformContent();
        
        if (!active) return;
        if (Input.touchCount != 2) return;
        
        fingerPosition1 = Input.touches[0].position;
        fingerPosition2 = Input.touches[1].position;
        
        if (!_isPinching)
        {
            _isPinching = true;
            OnPinchStart();
        }
        OnPinch();
    }
    
    private void OnPinchStart()
    {
        startPostiton = GetMidlePosition(fingerPosition1, fingerPosition2);
        startScaleMagnitude = (fingerPosition1 - fingerPosition2).magnitude;
        ContentScaleOnStart = ContentRect.localScale;
        ContentPositionOnStart = ContentRect.anchoredPosition;
    }
    
    private void OnPinch()
    {
        newPosition = GetMidlePosition(fingerPosition1, fingerPosition2);

        oldMoveMagnitude = moveMagnitude;
        oldScaleMagnitude = scaleMagnitude;
        
        moveMagnitude = (newPosition - startPostiton).magnitude;
        
        newScaleMagnitude = (fingerPosition1 - fingerPosition2).magnitude;
        scaleMagnitude = startScaleMagnitude - newScaleMagnitude;


        if (Mathf.Abs(scaleMagnitude - oldScaleMagnitude) > 1 ||
            Mathf.Abs(moveMagnitude - oldMoveMagnitude) > 1)
        {
            CalculateScaleContent();
            CalculateMoveContent();
        }
    }

    private void TransformContent()
    {
        if (avalibleScaleSteps > 0)
        {
            ContentRect.localScale += stepScale * (avalibleScaleSteps / 2f);
            avalibleScaleSteps--;
        }
        
        if (avalibleMoveSteps > 0)
        {
            ContentRect.anchoredPosition += stepMove * (avalibleMoveSteps / 2f);
            avalibleMoveSteps--;
        }
    }

    private void CalculateScaleContent()
    {
        float tempScale = Mathf.Clamp(ContentScaleOnStart.x + (scaleMagnitude * ZoomSpeed), MinZoom, MaxZoom);
        
        TargetScale = new Vector2(tempScale, tempScale);
        
        stepScale = (TargetScale - (Vector2)ContentRect.localScale) / SmoothScale;
        avalibleScaleSteps = SmoothScale;
    }

    private void CalculateMoveContent()
    {
        Vector2 localScale = ContentRect.localScale;
        float maxMoveOffsetX = Mathf.Abs((ContentSizeDelta.x * localScale.x) - ContentSizeDelta.x) / 2 + MaxPermissibleWidthOffset;
        float maxMoveOffsetY = Mathf.Abs((ContentSizeDelta.y * localScale.y) - ContentSizeDelta.y) / 2 + MaxPermissibleHeightOffset;
        float offcetMoveX = newPosition.x - startPostiton.x;
        float offcetMoveY = newPosition.y - startPostiton.y;

        TargetPosition = new Vector2(
            Mathf.Clamp(ContentPositionOnStart.x + (offcetMoveX * MoveSpeed), -maxMoveOffsetX, maxMoveOffsetX),
            Mathf.Clamp(ContentPositionOnStart.y + (offcetMoveY * MoveSpeed), -maxMoveOffsetY, maxMoveOffsetY));
        
        stepMove = (TargetPosition - ContentRect.anchoredPosition) / SmoothMove;
        avalibleMoveSteps = SmoothMove;
    }

    private void TryOutDangerZone()
    {
        Vector2 localScale = ContentRect.localScale;
        float maxMoveOffsetX = Mathf.Abs((ContentSizeDelta.x * localScale.x) - ContentSizeDelta.x) / 2 + MinPermissibleWidthOffset;
        float maxMoveOffsetY = Mathf.Abs((ContentSizeDelta.y * localScale.y) - ContentSizeDelta.y) / 2 + MinPermissibleHeightOffset;

        TargetPosition = new Vector2(
            Mathf.Clamp(TargetPosition.x, -maxMoveOffsetX, maxMoveOffsetX),
            Mathf.Clamp(TargetPosition.y, -maxMoveOffsetY, maxMoveOffsetY));
        
        stepMove = (TargetPosition - ContentRect.anchoredPosition) / SmoothMove;
        avalibleMoveSteps = SmoothMove;
    }
    
    private Vector2 GetMidlePosition(Vector2 pos1, Vector2 pos2)
    {
        return Vector2.Lerp(pos1, pos2, 0.5f);
    }
}

推荐阅读