using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; #if UNITY_EDITOR using UnityEditor; #endif public class AdvancedImageViewer : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IScrollHandler, IPointerClickHandler { [Header("Zoom Settings")] [Range(0.1f, 0.5f)] public float minZoom = 0.3f; [Range(2f, 10f)] public float maxZoom = 4f; [Range(0.01f, 0.2f)] public float zoomSpeed = 0.1f; [Range(0.001f, 0.05f)] public float touchZoomSpeed = 0.01f; [Header("Move Settings")] [Range(0.5f, 2f)] public float moveSpeed = 1f; [Range(0.01f, 0.3f)] public float elasticFactor = 0.1f; [Header("References")] public RectTransform imageRect; public RectTransform contentRect; private Vector2 lastDragPosition; private float currentZoom = 1f; private Vector2 initialAnchoredPosition; private Vector2 initialSizeDelta; private float lastTapTime; private const float DoubleTapTime = 0.3f; private bool isDragging; private Vector2 velocity; private const float friction = 0.95f; private void OnEnable() { Screen.orientation = ScreenOrientation.LandscapeLeft; Screen.autorotateToLandscapeLeft = false; Screen.autorotateToLandscapeRight = false; Screen.autorotateToPortrait = false; Screen.autorotateToPortraitUpsideDown = false; Initialize(); } private void OnDisable() { // 强制横屏并禁止自动旋转 Screen.orientation = ScreenOrientation.Portrait; Screen.autorotateToLandscapeLeft = false; Screen.autorotateToLandscapeRight = false; Screen.autorotateToPortrait = false; Screen.autorotateToPortraitUpsideDown = false; } private void Initialize() { if (imageRect == null) imageRect = GetComponent<RectTransform>(); if (contentRect == null) contentRect = transform.parent.GetComponent<RectTransform>(); initialAnchoredPosition = imageRect.anchoredPosition; initialSizeDelta = imageRect.sizeDelta; currentZoom = 1f; imageRect.localScale = Vector3.one; } private void Update() { #if UNITY_EDITOR if (!Application.isPlaying) { // 编辑器模式下保持初始化状态 if (currentZoom != 1f) { currentZoom = 1f; imageRect.localScale = Vector3.one; imageRect.anchoredPosition = initialAnchoredPosition; } return; } #endif HandleTouchZoom(); ApplyInertia(); } private void HandleTouchZoom() { if (Input.touchCount == 2) { Touch touchZero = Input.GetTouch(0); Touch touchOne = Input.GetTouch(1); if (touchZero.phase == TouchPhase.Moved || touchOne.phase == TouchPhase.Moved) { Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition; float prevDistance = Vector2.Distance(touchZeroPrevPos, touchOnePrevPos); float currentDistance = Vector2.Distance(touchZero.position, touchOne.position); float difference = currentDistance - prevDistance; ZoomImage(1 + difference * touchZoomSpeed, (touchZero.position + touchOne.position) / 2); } isTwo=true; } else if(Input.touchCount == 0) { isTwo=false; } } private void ApplyInertia() { if (!isDragging && velocity.magnitude > 0.1f) { imageRect.anchoredPosition += velocity * Time.deltaTime; velocity *= friction; ClampImagePosition(); } else { velocity = Vector2.zero; } } public void OnBeginDrag(PointerEventData eventData) { if (Input.touchCount > 1) return; isDragging = true; lastDragPosition = eventData.position; } public void OnDrag(PointerEventData eventData) { if (Input.touchCount > 1 || currentZoom <= 1f) return; Vector2 delta = eventData.position - lastDragPosition; imageRect.anchoredPosition += delta * moveSpeed; velocity = delta * moveSpeed; lastDragPosition = eventData.position; ClampImagePosition(); } public void OnEndDrag(PointerEventData eventData) { isDragging = false; } public void OnScroll(PointerEventData eventData) { float scrollDelta = eventData.scrollDelta.y; float zoomFactor = 1f + scrollDelta * zoomSpeed; ZoomImage(zoomFactor, eventData.position); } bool isTwo=false; public void OnPointerClick(PointerEventData eventData) { if (eventData.clickCount == 2) { HandleDoubleTap(eventData.position); } else if (eventData.clickCount == 1&&!isTwo) { float currentTime = Time.time; if (currentTime - lastTapTime < DoubleTapTime) { HandleDoubleTap(eventData.position); } lastTapTime = currentTime; } } private void HandleDoubleTap(Vector2 position) { if (currentZoom > 1f) { ResetView(); } else { ZoomImage(2f, position); } } private void ZoomImage(float zoomFactor, Vector2 zoomCenter) { float newZoom = currentZoom * zoomFactor; newZoom = Mathf.Clamp(newZoom, minZoom, maxZoom); RectTransformUtility.ScreenPointToLocalPointInRectangle( imageRect, zoomCenter, null, out Vector2 localPoint); Vector2 pivotOffset = (Vector2)imageRect.pivot - new Vector2(0.5f, 0.5f); Vector2 offset = localPoint * (newZoom - currentZoom); imageRect.localScale = Vector3.one * newZoom; // imageRect.anchoredPosition -= zoomCenter-imageRect.sizeDelta*imageRect.localScale.x; currentZoom = newZoom; ClampImagePosition(); } private void ClampImagePosition() { if (currentZoom <= 1f) { imageRect.anchoredPosition = initialAnchoredPosition; return; } Vector3[] corners = new Vector3[4]; imageRect.GetWorldCorners(corners); if (contentRect == null) return; Vector3[] contentCorners = new Vector3[4]; contentRect.GetWorldCorners(contentCorners); float imageWidth = (corners[2].x - corners[0].x)*imageRect.localScale.x; float imageHeight = (corners[2].y - corners[0].y)*imageRect.localScale.x; float contentWidth = contentCorners[2].x - contentCorners[0].x; float contentHeight = contentCorners[2].y - contentCorners[0].y; float minX = contentWidth - imageWidth; float maxX = 0+ imageWidth; float minY = contentHeight - imageHeight; float maxY = 0+ imageHeight; Vector3 pos = imageRect.localPosition; pos.x = Mathf.Clamp(pos.x, minX, maxX); pos.y = Mathf.Clamp(pos.y, minY, maxY); // 弹性效果 if (pos.x != imageRect.localPosition.x || pos.y != imageRect.localPosition.y) { velocity = Vector2.zero; imageRect.localPosition = Vector3.Lerp(imageRect.localPosition, pos, elasticFactor); } else { imageRect.localPosition = pos; } } public void ResetView() { currentZoom = 1f; imageRect.localScale = Vector3.one; imageRect.anchoredPosition = initialAnchoredPosition; velocity = Vector2.zero; } #if UNITY_EDITOR private void OnValidate() { if (EditorApplication.isPlayingOrWillChangePlaymode) return; if (imageRect == null) imageRect = GetComponent<RectTransform>(); if (contentRect == null && transform.parent != null) contentRect = transform.parent.GetComponent<RectTransform>(); initialAnchoredPosition = imageRect.anchoredPosition; initialSizeDelta = imageRect.sizeDelta; } #endif }