面试官:给我实现一个图片标注工具,截图标注,讲一下思路

这个工具是干啥的?

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

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

核心代码(去掉复杂术语)

1. 先定义"框框"长什么样

typescript 复制代码
// 一个框框需要哪些信息?
interface MarkerType {
  x: number,        // 框框左上角的横坐标
  y: number,        // 框框左上角的纵坐标  
  width: number,    // 框框有多宽
  height: number,   // 框框有多高
  type: "1" | "2" | "3"  // 框框类型:1=红,2=黄,3=蓝
}

// 整张图片的信息
interface ImgMarkerType {
  img: string,      // 图片网址
  markList: MarkerType[]  // 所有的框框
}

2. 准备工具和材料

typescript 复制代码
// 主要的数据
const imgMarkerData = reactive<ImgMarkerType>({
  img: "https://图片网址.com",  // 要标注的图片
  markList: []  // 开始时空的,画了框框就往里加
});

// 当前选中的颜色
const nowType = ref<"1" | "2" | "3">("1");  // 默认红色

// 记录当前显示到第几个框框(用于前进后退)
const nowIndex = ref(0);

// 是否正在画框(鼠标按下才开始画)
const canDraw = ref(false);

3. 最重要的功能:在画布上画画

typescript 复制代码
function renderCanvas() {
  if (!ctx.value) return;
  
  // 1. 先擦干净画布
  ctx.value.clearRect(0, 0, 800, 800);
  
  // 2. 画上背景图片
  if (img.value) {
    ctx.value.drawImage(img.value, 0, 0, 800, 800);
  }
  
  // 3. 画所有的框框
  imgMarkerData.markList.forEach((marker, index) => {
    // 前进后退功能:只画到当前步骤的框框
    if (index > nowIndex.value) return;
    
    // 根据类型设置颜色
    let color = "red";
    if (marker.type === "2") color = "yellow";
    if (marker.type === "3") color = "blue";
    
    // 画框框
    ctx.value!.strokeStyle = color;
    ctx.value!.strokeRect(marker.x, marker.y, marker.width, marker.height);
  });
}

4. 鼠标操作:怎么画框框?

typescript 复制代码
// 鼠标按下:开始画框
function mousedown(e: MouseEvent) {
  canDraw.value = true;  // 告诉程序:开始画了!
  
  // 计算鼠标点击的位置
  const rect = (e.target as HTMLCanvasElement).getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  // 创建一个新框框(开始很小,宽高都是0)
  const newMarker: MarkerType = {
    x: x,
    y: y,
    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();  // 最后画一次
}

5. 前进后退功能

typescript 复制代码
// 后退:撤销上一步
function goBack() {
  if (nowIndex.value > 0) {
    nowIndex.value--;  // 少显示一个框框
    renderCanvas();
  }
}

// 前进:恢复撤销的操作
function goForward() {
  if (nowIndex.value < imgMarkerData.markList.length - 1) {
    nowIndex.value++;  // 多显示一个框框
    renderCanvas();
  }
}

6. 切换框框颜色

typescript 复制代码
// 很简单,就是改一下当前选中的类型
function changeType(type: "1" | "2" | "3") {
  nowType.value = type;
}

下面👇🏻说一下主要思想吧:跟着来是实现,实战一下前端的一些功能,以不变应万变。


  1. 页面加载时:显示图片,准备画布
  2. 选择颜色:点击"标注1"(红)、"标注2"(黄)、"标注3"(蓝)
  3. 开始画框
    • 在图片上按下鼠标(记录起点)
    • 拖拽鼠标(框框跟着变大)
    • 松开鼠标(框框完成)
  4. 修改错误
    • 点"后退":撤销最后一个框框
    • 点"前进":恢复刚才撤销的框框

为什么这样设计?

核心思想:数据驱动

  • 不直接操作画面,而是操作数据
  • 数据变了,画面自动更新
  • 就像玩橡皮泥:你只管捏形状,眼睛会自动看到变化

好处

  • 代码清晰好维护
  • 功能容易扩展(比如加个绿色框框很简单)
  • 前进后退实现简单(只是改个数字)

这样设计后,无论功能多复杂,只要想清楚"数据怎么变",代码就很好写了!

相关推荐
发现一只大呆瓜4 小时前
前端性能优化:图片懒加载的三种手写方案
前端·javascript·面试
不爱吃糖的程序媛4 小时前
Flutter 与 OpenHarmony 通信:Flutter Channel 使用指南
前端·javascript·flutter
利刃大大5 小时前
【Vue】Element-Plus快速入门 && Form && Card && Table && Tree && Dialog && Menu
前端·javascript·vue.js·element-plus
NEXT065 小时前
AI 应用工程化实战:使用 LangChain.js 编排 DeepSeek 复杂工作流
前端·javascript·langchain
念风零壹5 小时前
AI 时代的前端技术:从系统编程到 JavaScript/TypeScript
前端·ai
光影少年6 小时前
react的hooks防抖和节流是怎样做的
前端·javascript·react.js
小毛驴8506 小时前
Vue 路由示例
前端·javascript·vue.js
发现一只大呆瓜6 小时前
AI流式交互:SSE与WebSocket技术选型
前端·javascript·面试
m0_719084117 小时前
React笔记张天禹
前端·笔记·react.js
Ziky学习记录7 小时前
从零到实战:React Router 学习与总结
前端·学习·react.js