Pinch and Zoom functionality on Canvas UI Images

I have a scroll view with the set of images.When I click on one of the images They enlarge to fit the full screen.Now i want to provide pinch,zoom and Pan functionality on every image.Just like google play store or android Gallery.

here a solution

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PinchableScrollRect : ScrollRect
{
    [SerializeField] float _minZoom = .1f;
    [SerializeField] float _maxZoom = 10;
    [SerializeField] float _zoomLerpSpeed = 10f;
    float _currentZoom = 1;
    bool _isPinching = false;
    float _startPinchDist;
    float _startPinchZoom;
    Vector2 _startPinchCenterPosition;
    Vector2 _startPinchScreenPosition;
    float _mouseWheelSensitivity = 1;
    bool blockPan = false;

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

    private void Update()
    {
        if (Input.touchCount == 2)
        {
            if (!_isPinching)
            {
                _isPinching = true;
                OnPinchStart();
            }
            OnPinch();
        }
        else
        {
            _isPinching = false;
            if (Input.touchCount == 0)
            {
                blockPan = false;
            }
        }
        //pc input
        float scrollWheelInput = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scrollWheelInput) > float.Epsilon)
        {
            _currentZoom *= 1 + scrollWheelInput * _mouseWheelSensitivity;
            _currentZoom = Mathf.Clamp(_currentZoom, _minZoom, _maxZoom);
            _startPinchScreenPosition = (Vector2)Input.mousePosition;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(content, _startPinchScreenPosition, null, out _startPinchCenterPosition);
            Vector2 pivotPosition = new Vector3(content.pivot.x * content.rect.size.x, content.pivot.y * content.rect.size.y);
            Vector2 posFromBottomLeft = pivotPosition + _startPinchCenterPosition;
            SetPivot(content, new Vector2(posFromBottomLeft.x / content.rect.width, posFromBottomLeft.y / content.rect.height));
        }
        //pc input end

        if (Mathf.Abs(content.localScale.x - _currentZoom) > 0.001f)
            content.localScale = Vector3.Lerp(content.localScale, Vector3.one * _currentZoom, _zoomLerpSpeed * Time.deltaTime);
    }

    protected override void SetContentAnchoredPosition(Vector2 position)
    {
        if (_isPinching || blockPan) return;
        base.SetContentAnchoredPosition(position);
    }

    void OnPinchStart()
    {
        Vector2 pos1 = Input.touches[0].position;
        Vector2 pos2 = Input.touches[1].position;

        _startPinchDist = Distance(pos1, pos2) * content.localScale.x;
        _startPinchZoom = _currentZoom;
        _startPinchScreenPosition = (pos1 + pos2) / 2;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(content, _startPinchScreenPosition, null, out _startPinchCenterPosition);

        Vector2 pivotPosition = new Vector3(content.pivot.x * content.rect.size.x, content.pivot.y * content.rect.size.y);
        Vector2 posFromBottomLeft = pivotPosition + _startPinchCenterPosition;

        SetPivot(content, new Vector2(posFromBottomLeft.x / content.rect.width, posFromBottomLeft.y / content.rect.height));
        blockPan = true;
    }

    void OnPinch()
    {
        float currentPinchDist = Distance(Input.touches[0].position, Input.touches[1].position) * content.localScale.x;
        _currentZoom = (currentPinchDist / _startPinchDist) * _startPinchZoom;
        _currentZoom = Mathf.Clamp(_currentZoom, _minZoom, _maxZoom);
    }

    float Distance(Vector2 pos1, Vector2 pos2)
    {
        RectTransformUtility.ScreenPointToLocalPointInRectangle(content, pos1, null, out pos1);
        RectTransformUtility.ScreenPointToLocalPointInRectangle(content, pos2, null, out pos2);
        return Vector2.Distance(pos1, pos2);
    }

    static void SetPivot(RectTransform rectTransform, Vector2 pivot)
    {
        if (rectTransform == null) return;

        Vector2 size = rectTransform.rect.size;
        Vector2 deltaPivot = rectTransform.pivot - pivot;
        Vector3 deltaPosition = new Vector3(deltaPivot.x * size.x, deltaPivot.y * size.y) * rectTransform.localScale.x;
        rectTransform.pivot = pivot;
        rectTransform.localPosition -= deltaPosition;
    }
}

you could use event trigger to collect all the touches. when there are 2 points, you use their distance this frame and last frame to calculate how much you should scale in this frame

first apply event listener on gameobject like image in this case,
then attach c# script with this code

public GameObject image;
public static bool flag = false;

public void OnImageTapBeginClick(){
	if (flag) {
		flag = false;
		image.transform.localScale = new Vector2 (1f, 1f);
	} else {
		flag = true;
		image.transform.localScale = new Vector2 (2f,2f);
	}
}

Very useful script NoGame0life :slight_smile:

If you’re using a “clamped movement” your scroll view could goes out of limits when you’re zooming out. I think it could be a good idea to add a callback function when the pinching is finished to keeps the scroll view into limits.

Here’s a simple way to do this with NoGame0life’s script:

private void Update() {
    if (Input.touchCount == 2) {
        if (!_isPinching) {
            _isPinching = true;
            OnPinchStart();
        }
        OnPinch();
    } else {

        if (_isPinching) {
            // - Callback (pinch end)
            StartCoroutine(KeepsInLimits());
        }

        _isPinching = false;
        if (Input.touchCount == 0) {
            blockPan = false;
        }
    }/*

IEnumerator KeepsInLimits() {
    this.movementType = MovementType.Elastic;
    yield return new WaitForSeconds(0.3f); // - for example
    this.movementType = MovementType.Clamped;
}

@imad_bzitou hey imad, it’s enough if you set _minZoom to 0.5f in the Awake function. Greets

Very use full code but i am facing a problem,when i zoom out the image size get smaller than the screen size.So how can i fix the min zoom to screen size?,Very use full code for me,but i am facing a problem when i zoomout the image size get smaller than the screen size, so how can i fix min zoom to screen size?,I also use this code.This code is very useful for me but i am facing one problem how can i set the min zoom size fit to screen size?

for whatever reason i had to remove the component from the game object and put it back for things to actually update properly, after making changes to the min max values.