AdvancedImageViewer.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using UnityEngine.EventSystems;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. #endif
  7. public class AdvancedImageViewer : MonoBehaviour,
  8. IBeginDragHandler, IDragHandler, IEndDragHandler,
  9. IScrollHandler, IPointerClickHandler
  10. {
  11. [Header("Zoom Settings")]
  12. [Range(0.1f, 0.5f)] public float minZoom = 0.3f;
  13. [Range(2f, 10f)] public float maxZoom = 4f;
  14. [Range(0.01f, 0.2f)] public float zoomSpeed = 0.1f;
  15. [Range(0.001f, 0.05f)] public float touchZoomSpeed = 0.01f;
  16. [Header("Move Settings")]
  17. [Range(0.5f, 2f)] public float moveSpeed = 1f;
  18. [Range(0.01f, 0.3f)] public float elasticFactor = 0.1f;
  19. [Header("References")]
  20. public RectTransform imageRect;
  21. public RectTransform contentRect;
  22. private Vector2 lastDragPosition;
  23. private float currentZoom = 1f;
  24. private Vector2 initialAnchoredPosition;
  25. private Vector2 initialSizeDelta;
  26. private float lastTapTime;
  27. private const float DoubleTapTime = 0.3f;
  28. private bool isDragging;
  29. private Vector2 velocity;
  30. private const float friction = 0.95f;
  31. private void OnEnable()
  32. {
  33. Screen.orientation = ScreenOrientation.LandscapeLeft;
  34. Screen.autorotateToLandscapeLeft = false;
  35. Screen.autorotateToLandscapeRight = false;
  36. Screen.autorotateToPortrait = false;
  37. Screen.autorotateToPortraitUpsideDown = false;
  38. Initialize();
  39. }
  40. private void OnDisable()
  41. {
  42. // 强制横屏并禁止自动旋转
  43. Screen.orientation = ScreenOrientation.Portrait;
  44. Screen.autorotateToLandscapeLeft = false;
  45. Screen.autorotateToLandscapeRight = false;
  46. Screen.autorotateToPortrait = false;
  47. Screen.autorotateToPortraitUpsideDown = false;
  48. }
  49. private void Initialize()
  50. {
  51. if (imageRect == null) imageRect = GetComponent<RectTransform>();
  52. if (contentRect == null) contentRect = transform.parent.GetComponent<RectTransform>();
  53. initialAnchoredPosition = imageRect.anchoredPosition;
  54. initialSizeDelta = imageRect.sizeDelta;
  55. currentZoom = 1f;
  56. imageRect.localScale = Vector3.one;
  57. }
  58. private void Update()
  59. {
  60. #if UNITY_EDITOR
  61. if (!Application.isPlaying)
  62. {
  63. // 编辑器模式下保持初始化状态
  64. if (currentZoom != 1f)
  65. {
  66. currentZoom = 1f;
  67. imageRect.localScale = Vector3.one;
  68. imageRect.anchoredPosition = initialAnchoredPosition;
  69. }
  70. return;
  71. }
  72. #endif
  73. HandleTouchZoom();
  74. ApplyInertia();
  75. }
  76. private void HandleTouchZoom()
  77. {
  78. if (Input.touchCount == 2)
  79. {
  80. Touch touchZero = Input.GetTouch(0);
  81. Touch touchOne = Input.GetTouch(1);
  82. if (touchZero.phase == TouchPhase.Moved || touchOne.phase == TouchPhase.Moved)
  83. {
  84. Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
  85. Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
  86. float prevDistance = Vector2.Distance(touchZeroPrevPos, touchOnePrevPos);
  87. float currentDistance = Vector2.Distance(touchZero.position, touchOne.position);
  88. float difference = currentDistance - prevDistance;
  89. ZoomImage(1 + difference * touchZoomSpeed, (touchZero.position + touchOne.position) / 2);
  90. }
  91. isTwo=true;
  92. }
  93. else if(Input.touchCount == 0)
  94. {
  95. isTwo=false;
  96. }
  97. }
  98. private void ApplyInertia()
  99. {
  100. if (!isDragging && velocity.magnitude > 0.1f)
  101. {
  102. imageRect.anchoredPosition += velocity * Time.deltaTime;
  103. velocity *= friction;
  104. ClampImagePosition();
  105. }
  106. else
  107. {
  108. velocity = Vector2.zero;
  109. }
  110. }
  111. public void OnBeginDrag(PointerEventData eventData)
  112. {
  113. if (Input.touchCount > 1) return;
  114. isDragging = true;
  115. lastDragPosition = eventData.position;
  116. }
  117. public void OnDrag(PointerEventData eventData)
  118. {
  119. if (Input.touchCount > 1 || currentZoom <= 1f) return;
  120. Vector2 delta = eventData.position - lastDragPosition;
  121. imageRect.anchoredPosition += delta * moveSpeed;
  122. velocity = delta * moveSpeed;
  123. lastDragPosition = eventData.position;
  124. ClampImagePosition();
  125. }
  126. public void OnEndDrag(PointerEventData eventData)
  127. {
  128. isDragging = false;
  129. }
  130. public void OnScroll(PointerEventData eventData)
  131. {
  132. float scrollDelta = eventData.scrollDelta.y;
  133. float zoomFactor = 1f + scrollDelta * zoomSpeed;
  134. ZoomImage(zoomFactor, eventData.position);
  135. }
  136. bool isTwo=false;
  137. public void OnPointerClick(PointerEventData eventData)
  138. {
  139. if (eventData.clickCount == 2)
  140. {
  141. HandleDoubleTap(eventData.position);
  142. }
  143. else if (eventData.clickCount == 1&&!isTwo)
  144. {
  145. float currentTime = Time.time;
  146. if (currentTime - lastTapTime < DoubleTapTime)
  147. {
  148. HandleDoubleTap(eventData.position);
  149. }
  150. lastTapTime = currentTime;
  151. }
  152. }
  153. private void HandleDoubleTap(Vector2 position)
  154. {
  155. if (currentZoom > 1f)
  156. {
  157. ResetView();
  158. }
  159. else
  160. {
  161. ZoomImage(2f, position);
  162. }
  163. }
  164. private void ZoomImage(float zoomFactor, Vector2 zoomCenter)
  165. {
  166. float newZoom = currentZoom * zoomFactor;
  167. newZoom = Mathf.Clamp(newZoom, minZoom, maxZoom);
  168. RectTransformUtility.ScreenPointToLocalPointInRectangle(
  169. imageRect,
  170. zoomCenter,
  171. null,
  172. out Vector2 localPoint);
  173. Vector2 pivotOffset = (Vector2)imageRect.pivot - new Vector2(0.5f, 0.5f);
  174. Vector2 offset = localPoint * (newZoom - currentZoom);
  175. imageRect.localScale = Vector3.one * newZoom;
  176. // imageRect.anchoredPosition -= zoomCenter-imageRect.sizeDelta*imageRect.localScale.x;
  177. currentZoom = newZoom;
  178. ClampImagePosition();
  179. }
  180. private void ClampImagePosition()
  181. {
  182. if (currentZoom <= 1f)
  183. {
  184. imageRect.anchoredPosition = initialAnchoredPosition;
  185. return;
  186. }
  187. Vector3[] corners = new Vector3[4];
  188. imageRect.GetWorldCorners(corners);
  189. if (contentRect == null) return;
  190. Vector3[] contentCorners = new Vector3[4];
  191. contentRect.GetWorldCorners(contentCorners);
  192. float imageWidth = (corners[2].x - corners[0].x)*imageRect.localScale.x;
  193. float imageHeight = (corners[2].y - corners[0].y)*imageRect.localScale.x;
  194. float contentWidth = contentCorners[2].x - contentCorners[0].x;
  195. float contentHeight = contentCorners[2].y - contentCorners[0].y;
  196. float minX = contentWidth - imageWidth;
  197. float maxX = 0+ imageWidth;
  198. float minY = contentHeight - imageHeight;
  199. float maxY = 0+ imageHeight;
  200. Vector3 pos = imageRect.localPosition;
  201. pos.x = Mathf.Clamp(pos.x, minX, maxX);
  202. pos.y = Mathf.Clamp(pos.y, minY, maxY);
  203. // 弹性效果
  204. if (pos.x != imageRect.localPosition.x || pos.y != imageRect.localPosition.y)
  205. {
  206. velocity = Vector2.zero;
  207. imageRect.localPosition = Vector3.Lerp(imageRect.localPosition, pos, elasticFactor);
  208. }
  209. else
  210. {
  211. imageRect.localPosition = pos;
  212. }
  213. }
  214. public void ResetView()
  215. {
  216. currentZoom = 1f;
  217. imageRect.localScale = Vector3.one;
  218. imageRect.anchoredPosition = initialAnchoredPosition;
  219. velocity = Vector2.zero;
  220. }
  221. #if UNITY_EDITOR
  222. private void OnValidate()
  223. {
  224. if (EditorApplication.isPlayingOrWillChangePlaymode) return;
  225. if (imageRect == null) imageRect = GetComponent<RectTransform>();
  226. if (contentRect == null && transform.parent != null)
  227. contentRect = transform.parent.GetComponent<RectTransform>();
  228. initialAnchoredPosition = imageRect.anchoredPosition;
  229. initialSizeDelta = imageRect.sizeDelta;
  230. }
  231. #endif
  232. }