/**
|
* 鼠标位置和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
|
}
|
}
|