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