两种方式实现图片标记

效果图
第一种:通过动态添加dom元素实现标记
代码如下
cpp 复制代码
// index.tsx
import React from "react";
import "./index.less";

export default function index() {
  const parentRef = React.useRef<any>(null);
  const ulRef = React.useRef<any>(null);
  let count = 0;

  const generateMark = (x: number, y: number) => {
    const li = document.createElement("li");
    li.innerHTML = count.toString();
    li.style.left = x + "px";
    li.style.top = y + "px";
    ulRef.current.appendChild(li);
  };

  const onClick = (e) => {
    const rect = ulRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left - 10; // 减去li宽度一半、居中
    const y = e.clientY - rect.top - 10;
    count += 1;
    generateMark(x, y);
  };
  return (
    <div ref={parentRef} className="wrap" onClick={onClick}>
      <img
        src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
        alt=""
      />
      <ul ref={ulRef}></ul>
    </div>
  );
}
cpp 复制代码
// index.less
.wrap {
  width: 600px;
  height: 600px;
  margin: auto;
  position: relative;
  img {
    width: 100%;
    height: 100%;
  }
  ul {
    position: absolute;
    top: 0;
    border: 1px solid green;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
    li {
      position: absolute;
      width: 20px;
      height: 20px;
      line-height: 20px;
      border-radius: 50%;
      background-color: orange;
      text-align: center;
      color: white;
    }
  }
}
第二种:通过canvas实现标记
cpp 复制代码
import React, { useEffect } from "react";
import "./index.less";

export default function index() {
  const parentRef = React.useRef<any>(null);
  const imgRef = React.useRef<any>(null);
  const canvasRef = React.useRef<any>(null);
  let count = 0;

  const drawMark = (ctx, x: number, y: number) => {
    const textWidth = ctx.measureText(count).width;
    ctx.strokeStyle = "#FFFFFF";
    ctx.fillStyle = "orange";
    ctx.lineWidth = 2;
    ctx.shadowOffsetX = 2;
    ctx.shadowBlur = 2;
    ctx.shadowColor = "rgba(10, 18, 28, 0.20)";
    ctx.beginPath();
    ctx.arc(x, y, 15, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.font = "16px IBM Plex Sans";
    ctx.fillStyle = "#FFFFFF";
    ctx?.fillText(count, x - textWidth / 2, y + 5);
    ctx.closePath();
  };

  const onClick = (e) => {
    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    const ctx = canvas.getContext("2d");
    const x = e.clientX - rect.left; // 减去li宽度一半、居中
    const y = e.clientY - rect.top;
    count += 1;
    console.log("ctx", ctx);
    if (ctx) {
      drawMark(ctx, x, y);
    }
  };

  useEffect(() => {
    // 动态赋值canvas宽高
    canvasRef.current.width = imgRef.current.width;
    canvasRef.current.height = imgRef.current.height;
  }, [imgRef]);
  return (
    <div ref={parentRef} className="wrap" onClick={onClick}>
      <img
        ref={imgRef}
        src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
        alt=""
      />
      <canvas ref={canvasRef}></canvas>
    </div>
  );
} 
c 复制代码
.wrap {
  width: 600px;
  height: 600px;
  margin: auto;
  position: relative;
  img {
    width: 100%;
    height: 100%;
  }
  canvas {
    position: absolute;
    left: 0;
    border: 1px solid green;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
  }
}
相关推荐
豆豆的java之旅6 分钟前
软考中级软件设计师 数据结构详细知识点(含真题+练习题,可直接复习)
java·开发语言·数据结构
sthnyph8 分钟前
QT开发:事件循环与处理机制的概念和流程概括性总结
开发语言·qt
大尚来也37 分钟前
Java 反射:从“动态魔法”到生产实战的避坑指南
开发语言
无心水1 小时前
Java时间处理封神篇:java.time全解析
java·开发语言·python·架构·localdate·java.time·java时间处理
m0_587958951 小时前
C++中的命令模式变体
开发语言·c++·算法
~无忧花开~1 小时前
React生命周期全解析
开发语言·前端·javascript·react.js·前端框架·react
剑心诀2 小时前
02 数据结构(C) | 线性表——顺序表的基本操作
c语言·开发语言·数据结构
哈__2 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-maps
javascript·react native·react.js
人间打气筒(Ada)2 小时前
如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
开发语言·后端·golang