可視化學(xué)習(xí):實現(xiàn)Canvas圖片局部放大鏡
當前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
前言最近我在可視化課程中學(xué)習(xí)了如何在Canvas中利用像素處理來實現(xiàn)濾鏡效果,在這節(jié)課程的結(jié)尾留了一道局部放大鏡的題目,提示我們用像素處理的方式去實現(xiàn)這個效果,最終實現(xiàn)隨著鼠標移動將圖片局部放大,本著把學(xué)到的內(nèi)容落地實踐的想法,我就去思考了一番,但很不幸,我思考了好幾天也沒思考出結(jié)果,因為剛開始我想的一直是在一個Canvas上來操作,但是一來我對Canvas API還并不是很熟悉,二來我對像素處理還不夠熟練,然后第三是如果原圖的部分像素被處理了,那下一次放大就會有問題,因此我最終放棄了這個思路,選擇了再增加一個Canvas來完成最終的效果,以下就是利用這種方式實現(xiàn)圖片局部放大的效果。 像素處理在實現(xiàn)這個效果之前,我們先來了解一下如何處理像素,有些小伙伴可能不太清楚,所以這里簡單說一下,在屏幕上我們知道所有顯示的內(nèi)容都是由像素點組成的,那么在處理像素之前,我們需要先獲取到像素信息,那么Canvas就是提供了一個API叫做getImageData讓我們可以獲取到畫布上的像素信息,最終這個API返回的是一個ImageData類型的值,關(guān)于這個API的具體描述可以參考對應(yīng)的MDN頁面。 ImageData類型的數(shù)據(jù)包含三個屬性,包括data、width、height。width和height簡單來說,就是被提取像素信息的區(qū)域的寬高,最主要的像素信息是在這個data屬性中。data屬性指向一個數(shù)組類型的值,準確來說是Uint8ClampedArray的實例,Uint8ClampedArray表示8 位無符號整型固定數(shù)組,也就是說其中的元素是0到255之間的整數(shù),我們知道一個像素的顏色信息可以使用rgba四個分量表示,那么我們就得出在data數(shù)組中每四個元素就能表示一個像素點的信息,因此data數(shù)組的長度就是 了解完像素處理,我們就可以開始進行具體的實現(xiàn)了。 具體實現(xiàn)<canvas ref="canvasRef" width="0" height="0"></canvas> <canvas ref="magnifier" width="0" height="0"></canvas><!-- 放大鏡 --> 1. 準備工作在實現(xiàn)放大效果之前,我們需要先把圖片加載到Canvas上: (async function() { const img = await loadImage('src/assets/girl1.jpg'); canvasRef.value.width = img.width; canvasRef.value.height = img.height; context.drawImage(img, 0, 0); }()); 這里 接著設(shè)置一個要放大的區(qū)域,也就是以鼠標坐標為中心,多少半徑以內(nèi)的內(nèi)容要被放大,這里我設(shè)置一個變量originSize用于存儲原圖大小,并設(shè)置一個5倍的放大倍數(shù)。 let originSize = 40; // 原圖大小 let zoom = 5; // 放大倍數(shù) (async function() { // ... magnifier.value.width = originSize * zoom; magnifier.value.height = originSize * zoom; }()); 用作于放大鏡的magnifier,我們使用 2. 鼠標移動事件監(jiān)聽接下來就是主要的代碼實現(xiàn)。 首先是添加鼠標移動事件的監(jiān)聽: const addEvent = () => { canvasRef.value.addEventListener('mousemove', mouseDownHandler); }; addEvent(); 然后我們就來實現(xiàn)
const mouseDownHandler = e => { // 相對于畫布的坐標 const center = { x: Math.floor(e.pageX - left), y: Math.floor(e.pageY - top) }; };
const mouseDownHandler = e => { // 相對于畫布的坐標 // ... // 待放大區(qū)域的imageData const originImageData = getImageData(img, [center.x - originSize / 2, center.y - originSize / 2, originSize, originSize]); };
const mouseDownHandler = e => { // 相對于畫布的坐標 // ... // 待放大區(qū)域的imageData // ... // 構(gòu)建一個imageData const areaImageData = mContext.createImageData(magnifier.value.width, magnifier.value.height); };
const mouseDownHandler = e => { // 相對于畫布的坐標 // ... // 待放大區(qū)域的imageData // ... // 構(gòu)建一個imageData // ... let count = 0; for (let j = 0; j < originSize * zoom; j += zoom) { for (let i = 0; i < originSize * zoom; i += zoom) { // ... } } };
const mouseDownHandler = e => { // 相對于畫布的坐標 // ... // 待放大區(qū)域的imageData // ... // 構(gòu)建一個imageData // ... let count = 0; for (let j = 0; j < originSize * zoom; j += zoom) { for (let i = 0; i < originSize * zoom; i += zoom) { for (let k = j; k < j + zoom; k ++) { for (let m = i; m < i + zoom; m ++) { const index = (k * originSize * zoom + m) * 4; areaImageData.data[index] = originImageData.data[count]; areaImageData.data[index + 1] = originImageData.data[count + 1]; areaImageData.data[index + 2] = originImageData.data[count + 2]; areaImageData.data[index + 3] = originImageData.data[count + 3]; } } count += 4; } } mContext.putImageData(areaImageData, 0, 0); }; 至此我們就實現(xiàn)了基本的局部放大,但現(xiàn)在放大鏡不在原圖Canvas的上方,并且放大鏡是一個正方形,我們繼續(xù)簡單優(yōu)化一下。 3. 簡單優(yōu)化
#magnifier { position: absolute; box-shadow: 0 0 10px 4px rgba(12, 12, 12, .5); border-radius: 50%; }
<div class="canvas-container" ref="containerRef" :style="{width: containerWidth + 'px'}"> <canvas ref="canvasRef" width="0" height="0"></canvas> <canvas ref="magnifier" width="0" height="0" id="magnifier" :style="position"></canvas> </div> const position = reactive({ left: 0, top: 0 }); const containerWidth = ref(0); containerWidth.value = img.width; // 在鼠標移動過程中更新放大鏡的位置 position.top = (center.y - originSize * zoom / 2) + 'px'; position.left = (center.x - originSize * zoom / 2) + 'px'; .canvas-container { position: relative; overflow: hidden; }
const addEvent = () => { containerRef.value.addEventListener('mousemove', mouseDownHandler); }; 至此就完成了簡單的局部放大效果,雖然還存在一些問題吧。 總結(jié)以上的實現(xiàn)比較簡單粗暴,就是遍歷imageData然后賦值,不算什么很高明的思路,就當作是拋磚引玉吧。 該文章在 2024/3/28 16:54:00 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |