就像用画图软件在图片上画框框:
打开一张图片
选择红色/黄色/蓝色框框
在图片上拖拽画框
画错了可以撤销(后退)
撤销了又想恢复(前进)


功能概述
这个工具允许用户在图片上进行矩形标注,支持三种不同类型的标注,并提供前进/后退功能来撤销或重做操作。
核心实现思路
1. 数据结构设计
typescript
export interface MarkerType {
x: number, // 标记左上角x坐标
y: number, // 标记左上角y坐标
height: number, // 标记高度
width: number, // 标记宽度
type: "1" | "2" | "3" // 标记类型
}
export interface ImgMarkerType {
img: string, // 图片地址
markList: MarkerType[] // 标记列表
}
2. 关键状态管理
typescript
// 图片标注数据
const imgMarkerData = reactive<ImgMarkerType>({
img: url,
markList: []
});
// 当前标注类型
const nowType = ref<"1" | "2" | "3">("1");
// 当前操作索引(用于前进后退)
const nowIndex = ref(0);
// 是否可以绘制(鼠标按下时)
const canDraw = ref(false);
3. 核心功能实现
3.1 渲染画布
typescript
function renderCanvas() {
if (!ctx.value) return;
// 清空画布
ctx.value.clearRect(0, 0, 800, 800);
// 绘制图片
if (img.value) {
ctx.value.drawImage(img.value, 0, 0, 800, 800);
}
// 绘制标记
imgMarkerData.markList.forEach((marker, index) => {
if (index > nowIndex.value) return; // 前进后退控制
// 根据类型设置颜色
const colorMap = {
"1": "red",
"2": "yellow",
"3": "blue"
};
ctx.value!.strokeStyle = colorMap[marker.type];
ctx.value!.strokeRect(marker.x, marker.y, marker.width, marker.height);
});
}
3.2 鼠标事件处理
typescript
function mousedown(e: MouseEvent) {
canDraw.value = true;
// 创建新标记
const rect = (e.target as HTMLCanvasElement).getBoundingClientRect();
const newMarker: MarkerType = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
width: 0,
height: 0,
type: nowType.value
};
imgMarkerData.markList.push(newMarker);
nowIndex.value = imgMarkerData.markList.length - 1;
}
function mousemove(e: MouseEvent) {
if (!canDraw.value) return;
const rect = (e.target as HTMLCanvasElement).getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
// 更新当前标记的尺寸
const currentMarker = imgMarkerData.markList[nowIndex.value];
currentMarker.width = currentX - currentMarker.x;
currentMarker.height = currentY - currentMarker.y;
renderCanvas(); // 重新渲染
}
function mouseup() {
canDraw.value = false;
renderCanvas(); // 最终渲染
}
3.3 前进后退功能
typescript
function goBack() {
if (nowIndex.value > 0) {
nowIndex.value--;
renderCanvas();
}
}
function goForward() {
if (nowIndex.value < imgMarkerData.markList.length - 1) {
nowIndex.value++;
renderCanvas();
}
}