欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

可視化學習:實現Canvas圖片局部放大鏡

freeflydom
2024年3月28日 16:49 本文熱度 699

前言

最近我在可視化課程中學習了如何在Canvas中利用像素處理來實現濾鏡效果,在這節課程的結尾留了一道局部放大鏡的題目,提示我們用像素處理的方式去實現這個效果,最終實現隨著鼠標移動將圖片局部放大,本著把學到的內容落地實踐的想法,我就去思考了一番,但很不幸,我思考了好幾天也沒思考出結果,因為剛開始我想的一直是在一個Canvas上來操作,但是一來我對Canvas API還并不是很熟悉,二來我對像素處理還不夠熟練,然后第三是如果原圖的部分像素被處理了,那下一次放大就會有問題,因此我最終放棄了這個思路,選擇了再增加一個Canvas來完成最終的效果,以下就是利用這種方式實現圖片局部放大的效果。

像素處理

在實現這個效果之前,我們先來了解一下如何處理像素,有些小伙伴可能不太清楚,所以這里簡單說一下,在屏幕上我們知道所有顯示的內容都是由像素點組成的,那么在處理像素之前,我們需要先獲取到像素信息,那么Canvas就是提供了一個API叫做getImageData讓我們可以獲取到畫布上的像素信息,最終這個API返回的是一個ImageData類型的值,關于這個API的具體描述可以參考對應的MDN頁面

ImageData類型的數據包含三個屬性,包括data、width、height。width和height簡單來說,就是被提取像素信息的區域的寬高,最主要的像素信息是在這個data屬性中。data屬性指向一個數組類型的值,準確來說是Uint8ClampedArray的實例,Uint8ClampedArray表示8 位無符號整型固定數組,也就是說其中的元素是0到255之間的整數,我們知道一個像素的顏色信息可以使用rgba四個分量表示,那么我們就得出在data數組中每四個元素就能表示一個像素點的信息,因此data數組的長度就是width * height * 4

了解完像素處理,我們就可以開始進行具體的實現了。

具體實現

<canvas ref="canvasRef" width="0" height="0"></canvas>

<canvas ref="magnifier" width="0" height="0"></canvas><!-- 放大鏡 -->

1. 準備工作

在實現放大效果之前,我們需要先把圖片加載到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);

}());

這里loadImage方法是通過Image對象來異步加載圖片,然后通過drawImage方法將圖片繪制到畫布上。

接著設置一個要放大的區域,也就是以鼠標坐標為中心,多少半徑以內的內容要被放大,這里我設置一個變量originSize用于存儲原圖大小,并設置一個5倍的放大倍數。

let originSize = 40; // 原圖大小

let zoom = 5; // 放大倍數


(async function() {

  // ...

  magnifier.value.width = originSize * zoom;

  magnifier.value.height = originSize * zoom;

}());

用作于放大鏡的magnifier,我們使用originSize * zoom來設置它的寬高。

2. 鼠標移動事件監聽

接下來就是主要的代碼實現。

首先是添加鼠標移動事件的監聽:

const addEvent = () => {

  canvasRef.value.addEventListener('mousemove', mouseDownHandler);

};


addEvent();

然后我們就來實現mouseDownHandler函數。

  • 首先我們獲取鼠標坐標在Canvas中的相對坐標,并通過Math.floor取整

const mouseDownHandler = e => {

  // 相對于畫布的坐標

  const center = {

    x: Math.floor(e.pageX - left),

    y: Math.floor(e.pageY - top)

  };

};

  • 然后利用getImageData方法獲取指定區域的像素信息,這里我們用到了OffscreenCanvas,它提供了一個可以脫離屏幕渲染的 canvas 對象,可以提升渲染性能;這樣我們就得到了待放大區域的像素信息。

const mouseDownHandler = e => {

  // 相對于畫布的坐標

  // ...

  // 待放大區域的imageData

  const originImageData = getImageData(img, [center.x - originSize / 2, center.y - originSize / 2, originSize, originSize]);

};

  • 現在我們需要一個ImageData類型的變量,用于存儲放大后的像素信息,因為最終要渲染到magnifier這個Canvas上,我們就使用magnifier的2d上下文對象調用createImageData方法來創建一個ImageData對象,關于這個方法的使用具體可查看MDN文檔

const mouseDownHandler = e => {

  // 相對于畫布的坐標

  // ...

  // 待放大區域的imageData

  // ...

  // 構建一個imageData

  const areaImageData = mContext.createImageData(magnifier.value.width, magnifier.value.height);

};

  • 接下來就是具體的像素遍歷和處理,按照areaImageData的寬高來進行遍歷,這里迭代的增量使用+zoom是因為,當我們放大zoom倍數之后,原圖1個像素的信息在magnifier使用zoom*zoom個像素來放大,也就是zoom*zoom個像素點的色值和原圖中對應的那個像素的色值是一樣的。在我們這段代碼中設置zoom為5,也就是放大后使用5*5=25個像素點表示之前的一個像素點。

const mouseDownHandler = e => {

  // 相對于畫布的坐標

  // ...

  // 待放大區域的imageData

  // ...

  // 構建一個imageData

  // ...

  let count = 0;

  for (let j = 0; j < originSize * zoom; j += zoom) {

    for (let i = 0; i < originSize * zoom; i += zoom) {


      // ...


    }

  }

};

  • 所以我們繼續使用兩個for循環k和m,把areaImageData的data數組中的對應元素賦值為原圖對應像素的色值,完成賦值后我們就可以通過putImageData方法將像素信息渲染到magnifier畫布上。

const mouseDownHandler = e => {

  // 相對于畫布的坐標

  // ...

  // 待放大區域的imageData

  // ...

  // 構建一個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);

};

至此我們就實現了基本的局部放大,但現在放大鏡不在原圖Canvas的上方,并且放大鏡是一個正方形,我們繼續簡單優化一下。

3. 簡單優化

  • 首先因為我對Canvas API還不太熟悉,所以我現在通過css把放大鏡改為圓形,并加上一個陰影box-shadow來優化視覺效果。

#magnifier {

  position: absolute;

  box-shadow: 0 0 10px 4px rgba(12, 12, 12, .5);

  border-radius: 50%;

}

  • 然后給兩個Canvas外層加一個div容器,把放大鏡設置絕對定位,把它放到鼠標坐標的位置,在鼠標移動過程中更新放大鏡的位置。

<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;

}

  • 這個時候放大鏡的位置就和我們預想的一致了,但是現在還有一個問題,就是放大鏡在原圖的上方,在移動的過程中會看到放大鏡的渲染有點卡頓,這是因為鼠標移動事件是加在原圖Canvas上的,當鼠標懸浮在放大鏡上時,這個移動事件的監聽就不連貫了,此時我們可以考慮把鼠標移動監聽加改為加在外層容器上,這樣就能看到移動過程中放大鏡的渲染是比較流暢了。

const addEvent = () => {

  containerRef.value.addEventListener('mousemove', mouseDownHandler);

};

至此就完成了簡單的局部放大效果,雖然還存在一些問題吧。

總結

以上的實現比較簡單粗暴,就是遍歷imageData然后賦值,不算什么很高明的思路,就當作是拋磚引玉吧。



該文章在 2024/3/28 16:54:00 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved