在React中用canvas对图片标注的实现

 更新时间:2022年05月17日 14:29:39   作者:大洋洋2020  
本文主要介绍了在React中用canvas对图片标注的实现 ,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在审核业务中难免会有需要对图片进行标注的需求,本次用一个最小demo来演示如何对图片进行矩形标注。

首先我们要理解canvas是一块画布,而这块画布需要在我们要标注的图片上层,图片和canvas外层的div用相对位置,内层的图片和canvas用绝对位置,即可保证canvas重叠于图片之上。如图:

我们来看下canvas的初始化,在img、canvas中都有ref属性,不同的是img的ref属性直接就是一个useRef引用,而canvas中的ref是一个回调函数。它在组件被加载或卸载时会立即执行,加载时ref回调接收当前组件实例作为参数,卸载时ref回调接收null作为参数。在initCanvas函数中,用canvas的ref引用承接了canvas节点,并且通过drawImage函数,初始化了一块400*400的画布,第一个参数为需要绘制到的上下文元素:

<img src={lancome} ref={imgInstance} className="App-logo" alt="logo" />
<canvas className="canvas" ref={initCanvas} width="400px" height="400px" />
const canvasRef = useRef(null);
const imgInstance = useRef(null);

const initCanvas =  useCallback((node) => {
      canvasRef.current = node;
      const context = node.getContext('2d');
      context.drawImage(imgInstance.current, 0, 0, 400, 400);
  }, []);

接下来,我们通过invalidLocations来保存之前的标注位置信息,addInvalidLocation函数是为了添加标注位置信息。最需要注意的是我们在useEffect中所监听的三个函数,startDraw、drawingDeal和drawingEnd。

鼠标落下时,startDraw为起始点的x,y坐标赋值,并且拖拽状态位isDrawing置为true。鼠标移动时,drawingDeal函数会边通过clearRect函数更新画布,边根据鼠标的最新位置通过highlightInvalid来更新标注,经过确定矩形位置大小,内容填充,描边三个步骤来绘制出矩形。鼠标抬起时,drawingEnd函数会通过addInvalidLocation函数添加标注位置,然后初始化参数。

const [invalidLocations, setInvalidLocations] = useState([]);

const addInvalidLocation = useCallback((newMark) => {
    setInvalidLocations([...invalidLocations, newMark]);
}, [invalidLocations])

const highlightInvalid = (context, x1, y1, x2, y2) => {
    context.beginPath();
    context.rect(x1, y1, x2 - x1, y2 - y1);
    context.fillStyle = 'rgba(255, 0, 0, 0.2)';
    context.fill();
    context.strokeStyle = '#FF0070';
    context.lineWidth = 1;
    context.stroke();
    console.log('drawing', x2, y2);
};

const clearRect = (drawContext) => {
    drawContext.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
};

useEffect(() => {
    const canvasElem = canvasRef.current;
    let x = 0; let y = 0;
    let isDrawing = false;
    const drawContext = canvasRef.current.getContext('2d');
    let canvasRect;
    const lastCursorPosition = {
      x: 0,
      y: 0,
    };
    const startDraw = (e) => {
      console.log(e.type, 'start');
      canvasRect = canvasRef.current.getBoundingClientRect();
      x = e.clientX - canvasRect.left;
      y = e.clientY - canvasRect.top;
      if (x < 0) x = 0;
      if (y < 0) y = 0;
      isDrawing = true;
    };
    const drawingDeal = (e) => {
      console.log(e.type, 'move');
      if (isDrawing) {
        const x1 = e.clientX - canvasRect.left;
        const y1 = e.clientY - canvasRect.top;
        clearRect(drawContext);
        highlightInvalid(drawContext, x, y, x1, y1);
        lastCursorPosition.x = x1;
        lastCursorPosition.y = y1;
      }
    };
    const drawingEnd = () => {
      if (isDrawing) {
        if (lastCursorPosition.x && lastCursorPosition.y) {
          const width = lastCursorPosition.x - x + 1;
          const height = lastCursorPosition.y - y + 1;
          addInvalidLocation({ x, y, width, height });
          lastCursorPosition.x = 0;
          lastCursorPosition.y = 0;
        }
        clearRect(drawContext);
        isDrawing = false;
        x = 0;
        y = 0;
      }
    };
    canvasElem.addEventListener('mousedown', startDraw);
    canvasElem.addEventListener('mousemove', drawingDeal);
    canvasElem.addEventListener('mouseup', drawingEnd);
    return () => {
      canvasElem.removeEventListener('mousedown', startDraw);
      canvasElem.removeEventListener('mousemove', drawingDeal);
      canvasElem.removeEventListener('mouseup', drawingEnd);
    };
}, [invalidLocations, addInvalidLocation]);

在添加完标注位置之后,模板中我们通过迭代返回绝对定位的div来实现已经标注过的矩形。

<div className="img-wrap">
    <img src={lancome} ref={imgInstance} className="App-logo" alt="logo" />
    <canvas className="canvas" ref={initCanvas} width="400px" height="400px" />
    {invalidLocations && invalidLocations.map((location, index) => { 
        const { width, height, x, y } = location;
            return <div
                     key={`${width}_${height}_${x}_${y}`}
                     tabIndex={-1}
                     className={'remark'}
                     style={{ width: `${width}px`, height: `${height}px`, left: `${x}px`, top: `${y}px` }}
            ></div>
    })}
</div>

最后效果:

到此这篇关于在React中用canvas对图片标注的实现 的文章就介绍到这了,更多相关React canvas对图片标注内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于React动态加载路由处理的相关问题

    关于React动态加载路由处理的相关问题

    这篇文章主要介绍了关于React动态加载路由处理的相关问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • React+Redux实现简单的待办事项列表ToDoList

    React+Redux实现简单的待办事项列表ToDoList

    这篇文章主要为大家详细介绍了React+Redux实现简单的待办事项列表ToDoList,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • React-Native中一些常用组件的用法详解(二)

    React-Native中一些常用组件的用法详解(二)

    这篇文章主要跟大家介绍了关于React-Native中一些常用组件的用法的相关资料,其中详细介绍了关于ScrollView组件、ListView组件、Navigator组件、TableBarIOS组件以及网络请求等相关内容,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-06-06
  • React useEffect、useLayoutEffect底层机制及区别介绍

    React useEffect、useLayoutEffect底层机制及区别介绍

    useEffect 是 React 中的一个 Hook,允许你在函数组件中执行副作用操作,本文给大家介绍React useEffect、useLayoutEffect底层机制及区别介绍,感兴趣的朋友一起看看吧
    2025-04-04
  • react echarts刷新不显示问题的解决方法

    react echarts刷新不显示问题的解决方法

    最近在写项目的时候遇到了一个问题,当编辑完代码后echarts图正常展示 , 可再次刷新页面 , echarts会消失,所以本文给大家介绍了react echarts刷新不显示问题原因和解决方法,需要的朋友可以参考下
    2024-02-02
  • React18中的useDeferredValue示例详解

    React18中的useDeferredValue示例详解

    这篇文章主要介绍了React18中的useDeferredValue的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • React 保留和重置State

    React 保留和重置State

    这篇文章主要为大家介绍了React 保留和重置State实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • React中useEffect原理的代码简单实现详解

    React中useEffect原理的代码简单实现详解

    React的useEffect钩子是React函数组件中处理副作用,本文将通过一个简单的例子解释如何用代码实现useEffect的基本原理,感兴趣的小伙伴可以了解下
    2023-12-12
  • React中useState的理解和使用案例

    React中useState的理解和使用案例

    Hook是React16.8的新增特性,它可以让你在不编写class的情况下使用state以及其他的React特性,本文中讲解的useState就是React中的其中一个Hook,这篇文章主要给大家介绍了关于React中useState理解和使用的相关资料,需要的朋友可以参考下
    2024-03-03
  • 浅谈React组件props默认值的设置

    浅谈React组件props默认值的设置

    本文主要介绍了浅谈React组件props默认值的设置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04

最新评论