在React中用canvas對圖片標注的實現(xiàn)
在審核業(yè)務(wù)中難免會有需要對圖片進行標注的需求,本次用一個最小demo來演示如何對圖片進行矩形標注。
首先我們要理解canvas是一塊畫布,而這塊畫布需要在我們要標注的圖片上層,圖片和canvas外層的div用相對位置,內(nèi)層的圖片和canvas用絕對位置,即可保證canvas重疊于圖片之上。如圖:
我們來看下canvas的初始化,在img、canvas中都有ref屬性,不同的是img的ref屬性直接就是一個useRef引用,而canvas中的ref是一個回調(diào)函數(shù)。它在組件被加載或卸載時會立即執(zhí)行,加載時ref回調(diào)接收當(dāng)前組件實例作為參數(shù),卸載時ref回調(diào)接收null作為參數(shù)。在initCanvas函數(shù)中,用canvas的ref引用承接了canvas節(jié)點,并且通過drawImage函數(shù),初始化了一塊400*400的畫布,第一個參數(shù)為需要繪制到的上下文元素:
<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函數(shù)是為了添加標注位置信息。最需要注意的是我們在useEffect中所監(jiān)聽的三個函數(shù),startDraw、drawingDeal和drawingEnd。
鼠標落下時,startDraw為起始點的x,y坐標賦值,并且拖拽狀態(tài)位isDrawing置為true。鼠標移動時,drawingDeal函數(shù)會邊通過clearRect函數(shù)更新畫布,邊根據(jù)鼠標的最新位置通過highlightInvalid來更新標注,經(jīng)過確定矩形位置大小,內(nèi)容填充,描邊三個步驟來繪制出矩形。鼠標抬起時,drawingEnd函數(shù)會通過addInvalidLocation函數(shù)添加標注位置,然后初始化參數(shù)。
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來實現(xiàn)已經(jīng)標注過的矩形。
<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>
最后效果:
到此這篇關(guān)于在React中用canvas對圖片標注的實現(xiàn) 的文章就介紹到這了,更多相關(guān)React canvas對圖片標注內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react源碼層深入刨析babel解析jsx實現(xiàn)
同作為MVVM框架,React相比于Vue來講,上手更需要JavaScript功底深厚一些,本系列將閱讀React相關(guān)源碼,從jsx -> VDom -> RDOM等一些列的過程,將會在本系列中一一講解2022-10-10React Native 混合開發(fā)多入口加載方式詳解
這篇文章主要介紹了React Native 混合開發(fā)多入口加載方式詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法
這篇文章主要介紹了React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12