两种方式实现图片标记

效果图
第一种:通过动态添加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;
  }
}
相关推荐
qq_4198540512 分钟前
CSS动效
前端·javascript·css
烛阴14 分钟前
3D字体TextGeometry
前端·webgl·three.js
桜吹雪36 分钟前
markstream-vue实战踩坑笔记
前端
南村群童欺我老无力.1 小时前
Flutter应用鸿蒙迁移实战:性能优化与渐进式迁移指南
javascript·flutter·ci/cd·华为·性能优化·typescript·harmonyos
亲爱的非洲野猪1 小时前
Java锁机制八股文
java·开发语言
C_心欲无痕1 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下1 小时前
恢复网站console.log的脚本
前端·javascript·vue.js
LawrenceLan1 小时前
Flutter 零基础入门(十二):枚举(enum)与状态管理的第一步
开发语言·前端·flutter·dart
Zoey的笔记本1 小时前
「支持ISO27001的GTD协作平台」数据生命周期管理方案与加密通信协议
java·前端·数据库
奔跑的呱呱牛2 小时前
geojson-to-wkt 坐标格式转换
javascript·arcgis