flowChartImg.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <template>
  2. <div
  3. ref="imageWrapperRef"
  4. class="image-wrapper"
  5. @wheel="handleMouseWheel"
  6. @mousedown="handleMouseDown"
  7. @mousemove="handleMouseMove"
  8. @mouseup="handleMouseUp"
  9. @mouseleave="handleMouseLeave"
  10. @dblclick="resetTransform"
  11. :style="transformStyle"
  12. >
  13. <el-card class="box-card">
  14. <el-image :src="props.imgUrl" class="scalable-image" />
  15. </el-card>
  16. </div>
  17. </template>
  18. <script setup lang="ts">
  19. // Props 定义方式变化
  20. const props = defineProps({
  21. imgUrl: {
  22. type: String,
  23. default: () => ''
  24. }
  25. });
  26. const imageWrapperRef = ref<HTMLElement | null>(null);
  27. const scale = ref(1); // 初始缩放比例
  28. const maxScale = 3; // 最大缩放比例
  29. const minScale = 0.5; // 最小缩放比例
  30. let isDragging = false;
  31. let startX = 0;
  32. let startY = 0;
  33. let currentTranslateX = 0;
  34. let currentTranslateY = 0;
  35. const handleMouseWheel = (event: WheelEvent) => {
  36. event.preventDefault();
  37. let newScale = scale.value - event.deltaY / 1000;
  38. newScale = Math.max(minScale, Math.min(newScale, maxScale));
  39. if (newScale !== scale.value) {
  40. scale.value = newScale;
  41. resetDragPosition(); // 重置拖拽位置,使图片居中
  42. }
  43. };
  44. const handleMouseDown = (event: MouseEvent) => {
  45. if (scale.value > 1) {
  46. event.preventDefault(); // 阻止默认行为,防止拖拽
  47. isDragging = true;
  48. startX = event.clientX;
  49. startY = event.clientY;
  50. }
  51. };
  52. const handleMouseMove = (event: MouseEvent) => {
  53. if (!isDragging || !imageWrapperRef.value) return;
  54. const deltaX = event.clientX - startX;
  55. const deltaY = event.clientY - startY;
  56. startX = event.clientX;
  57. startY = event.clientY;
  58. currentTranslateX += deltaX;
  59. currentTranslateY += deltaY;
  60. // 边界检测,防止图片被拖出容器
  61. const bounds = getBounds();
  62. if (currentTranslateX > bounds.maxTranslateX) {
  63. currentTranslateX = bounds.maxTranslateX;
  64. } else if (currentTranslateX < bounds.minTranslateX) {
  65. currentTranslateX = bounds.minTranslateX;
  66. }
  67. if (currentTranslateY > bounds.maxTranslateY) {
  68. currentTranslateY = bounds.maxTranslateY;
  69. } else if (currentTranslateY < bounds.minTranslateY) {
  70. currentTranslateY = bounds.minTranslateY;
  71. }
  72. applyTransform();
  73. };
  74. const handleMouseUp = () => {
  75. isDragging = false;
  76. };
  77. const handleMouseLeave = () => {
  78. isDragging = false;
  79. };
  80. const resetTransform = () => {
  81. scale.value = 1;
  82. currentTranslateX = 0;
  83. currentTranslateY = 0;
  84. applyTransform();
  85. };
  86. const resetDragPosition = () => {
  87. currentTranslateX = 0;
  88. currentTranslateY = 0;
  89. applyTransform();
  90. };
  91. const applyTransform = () => {
  92. if (imageWrapperRef.value) {
  93. imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
  94. }
  95. };
  96. const getBounds = () => {
  97. if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
  98. const imgRect = imageWrapperRef.value.getBoundingClientRect();
  99. const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
  100. const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
  101. const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
  102. const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
  103. const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
  104. return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
  105. };
  106. const transformStyle = computed(() => ({
  107. transition: isDragging ? 'none' : 'transform 0.2s ease'
  108. }));
  109. </script>
  110. <style scoped>
  111. .image-wrapper {
  112. width: 100%;
  113. overflow: hidden;
  114. position: relative;
  115. margin: 0 auto;
  116. display: flex;
  117. justify-content: center;
  118. align-items: center;
  119. user-select: none; /* 禁用文本选择 */
  120. cursor: grab; /* 设置初始鼠标指针为可拖动 */
  121. }
  122. .image-wrapper:active {
  123. cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
  124. }
  125. .scalable-image {
  126. object-fit: contain;
  127. width: 100%;
  128. padding: 15px;
  129. }
  130. </style>