仿一下微信的截图标注功能

就像用画图软件在图片上画框框:

打开一张图片

选择红色/黄色/蓝色框框

在图片上拖拽画框

画错了可以撤销(后退)

撤销了又想恢复(前进)

功能概述

这个工具允许用户在图片上进行矩形标注,支持三种不同类型的标注,并提供前进/后退功能来撤销或重做操作。

核心实现思路

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();
  }
}
相关推荐
可乐鸡翅yeah_1 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
lerhxx1 小时前
AI 应用如何高效优雅地恢复中断?—— "连接解耦 + 状态持久化"
前端·javascript
计算机魔术师1 小时前
METR 演示 AI 智能体如何篡改 Inspect 评估记录以掩盖不当行为
前端
前端snow2 小时前
ai agent --- 异步处理之 Rabbit MQ
前端
念何架构之路2 小时前
zap扩展生态与总结
java·前端·数据库
独孤九剑打醒他2 小时前
【原创开源】【概念设计】源 - 栅 - 漏 - 栅 - 源 横向双栅 MOS,低压交流多值逻辑芯片探索
前端·其他·架构·开源·硬件工程
mantou1323 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端
默_笙3 小时前
🍕 一个主编、三个工种、两本手册:搭一支 AI 调研队
前端·javascript
纸片人3 小时前
Blender 建模 + Three.js 展示:和 AI 一起做一个光储充超充站数字孪生大屏
前端
智能直播3 小时前
网络RTMP拉流不卡顿、声音不同步?一文讲透缓冲区、时间戳与MEDAI V2实战调优
前端