hejianhao
2026-04-02 0940d56748a73525790c350c393a5be960aab62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/**
 * 鼠标位置和RGB颜色追踪工具
 * 用于图像标注页面实时显示鼠标坐标和当前像素的RGB值
 */
 
import { ref, onUnmounted } from 'vue'
 
export function useMouseTracker() {
  // 鼠标位置和RGB数据
  const mousePosition = ref({ 
    x: 0, 
    y: 0, 
    rgb: '0,0,0',
    hex: '#000000'
  })
  
  // Canvas元素用于获取像素RGB值
  let canvas = null
  let ctx = null
  let imageElement = null
  
  /**
   * 初始化Canvas用于RGB采样
   */
  const initCanvas = () => {
    if (!canvas) {
      canvas = document.createElement('canvas')
      ctx = canvas.getContext('2d', { willReadFrequently: true })
    }
  }
  
  /**
   * 从图像坐标获取RGB值
   * @param {number} x - 图像上的x坐标
   * @param {number} y - 图像上的y坐标
   * @returns {object} RGB值对象
   */
  const getRGBFromImage = (x, y) => {
    if (!imageElement || !ctx || x < 0 || y < 0) {
      return { r: 0, g: 0, b: 0 }
    }
    
    try {
      // 确保canvas尺寸与图像一致
      if (canvas.width !== imageElement.naturalWidth || canvas.height !== imageElement.naturalHeight) {
        canvas.width = imageElement.naturalWidth
        canvas.height = imageElement.naturalHeight
        ctx.drawImage(imageElement, 0, 0)
      }
      
      // 获取指定位置的像素数据
      const imageData = ctx.getImageData(x, y, 1, 1)
      const data = imageData.data
      
      return {
        r: data[0],
        g: data[1], 
        b: data[2]
      }
    } catch (error) {
      console.warn('[MouseTracker] 获取RGB失败:', error)
      return { r: 0, g: 0, b: 0 }
    }
  }
  
  /**
   * RGB转十六进制
   * @param {number} r - 红色值
   * @param {number} g - 绿色值  
   * @param {number} b - 蓝色值
   * @returns {string} 十六进制颜色值
   */
  const rgbToHex = (r, g, b) => {
    return '#' + [r, g, b].map(x => {
      const hex = x.toString(16)
      return hex.length === 1 ? '0' + hex : hex
    }).join('')
  }
  
  /**
   * 将屏幕坐标转换为图像坐标
   * @param {MouseEvent} event - 鼠标事件
   * @returns {object} 图像坐标
   */
  const getImageCoordinates = (event) => {
    if (!imageElement) return { x: 0, y: 0 }
    
    const rect = imageElement.getBoundingClientRect()
    
    // 检查尺寸是否有效
    if (rect.width === 0 || rect.height === 0) {
      return { x: 0, y: 0 }
    }
    
    // 计算鼠标在图片上的相对位置(0-1范围)
    const relativeX = (event.clientX - rect.left) / rect.width
    const relativeY = (event.clientY - rect.top) / rect.height
    
    // 转换为图片内部坐标
    const x = Math.floor(relativeX * imageElement.naturalWidth)
    const y = Math.floor(relativeY * imageElement.naturalHeight)
    
    return { 
      x: Math.max(0, Math.min(x, imageElement.naturalWidth - 1)),
      y: Math.max(0, Math.min(y, imageElement.naturalHeight - 1))
    }
  }
  
  /**
   * 鼠标移动事件处理
   * @param {MouseEvent} event - 鼠标事件
   */
  const handleMouseMove = (event) => {
    if (!imageElement) return
    
    // 获取图像坐标
    const { x, y } = getImageCoordinates(event)
    
    // 获取RGB值
    const { r, g, b } = getRGBFromImage(x, y)
    const hex = rgbToHex(r, g, b)
    
    // 更新鼠标位置数据
    mousePosition.value = {
      x,
      y,
      rgb: `${r},${g},${b}`,
      hex,
      r,
      g, 
      b
    }
  }
  
  /**
   * 图像加载完成事件处理
   */
  const handleImageLoad = () => {
    if (imageElement && imageElement.complete) {
      initCanvas()
      // 重新绘制图像到canvas
      canvas.width = imageElement.naturalWidth
      canvas.height = imageElement.naturalHeight
      ctx.drawImage(imageElement, 0, 0)
    }
  }
  
  /**
   * 设置要追踪的图像元素和容器
   * @param {HTMLImageElement} imgElement - 图像元素
   * @param {HTMLElement} containerElement - 容器元素(可选)
   */
  const setImageElement = (imgElement, containerElement = null) => {
    // 移除旧的事件监听器
    if (imageElement) {
      imageElement.removeEventListener('mousemove', handleMouseMove)
      imageElement.removeEventListener('load', handleImageLoad)
      // 尝试从父容器移除事件监听
      const parent = imageElement.parentElement
      if (parent) {
        parent.removeEventListener('mousemove', handleMouseMove)
      }
    }
    
    imageElement = imgElement
    
    if (imageElement) {
      initCanvas()
      
      // 使用容器元素或图像的父元素来监听鼠标移动
      // 这样可以捕获Canvas上的鼠标移动
      const targetElement = containerElement || imageElement.parentElement || imageElement
      
      // 添加事件监听器到容器
      targetElement.addEventListener('mousemove', handleMouseMove)
      imageElement.addEventListener('load', handleImageLoad)
      
      // 如果图像已加载,立即处理
      if (imageElement.complete) {
        handleImageLoad()
      }
    }
  }
  
  /**
   * 重置鼠标位置数据
   */
  const resetPosition = () => {
    mousePosition.value = { 
      x: 0, 
      y: 0, 
      rgb: '0,0,0',
      hex: '#000000',
      r: 0,
      g: 0,
      b: 0
    }
  }
  
  /**
   * 清理资源
   */
  const cleanup = () => {
    if (imageElement) {
      imageElement.removeEventListener('mousemove', handleMouseMove)
      imageElement.removeEventListener('load', handleImageLoad)
      // 尝试从父容器移除事件监听
      const parent = imageElement.parentElement
      if (parent) {
        parent.removeEventListener('mousemove', handleMouseMove)
      }
    }
    imageElement = null
    canvas = null
    ctx = null
  }
  
  // 组件卸载时清理
  onUnmounted(() => {
    cleanup()
  })
  
  return {
    mousePosition,
    setImageElement,
    resetPosition,
    cleanup,
    // 暴露工具方法供外部使用
    getRGBFromImage,
    getImageCoordinates,
    rgbToHex
  }
}