iT邦幫忙

2024 iThome 鐵人賽

DAY 18
0
Modern Web

一起來玩圖像編輯器:Fabric.js 的實戰修煉系列 第 18

Day18-fabric.js 進階組合技! 畫布縮放與平移:實現像 illustrator 一樣的縮放和拖動功能

  • 分享至 

  • xImage
  •  

欸~ 畫布上面物件太多,我想拉近一點看可以嗎?
欸~ 畫布佔版面太大了,我想拉遠一點看可以嗎?
欸~ 我想仔細確認一下上傳的圖看起來對不對,可以往右移嗎?

這 不 是 來 了 嗎 !
實現如同 illustrator 一般的拖拉大法!

demo

使用說明:

  • 滾動鼠標滾輪,或是按鈕來放大或縮小畫布。
  • 按住 Alt 鍵,然後按下並拖動鼠標來平移畫布。

主要實現了以下功能:

  1. 使用 Fabric.js 創建一個畫布,並添加了一些示例對象(一個紅色矩形和一個藍色圓形)。
  2. 實現了鼠標滾輪縮放功能。滾動鼠標滾輪可以放大或縮小畫布,縮放的中心點是鼠標所在的位置
  3. 實現按鈕縮放功能,縮放的中心點是畫面中心位置
  4. 實現了畫布平移功能。按住 Alt 鍵的同時按下並拖動鼠標可以移動整個畫布。
  5. 設置了縮放的最大和最小限制,以防止過度縮放。

實作解析:

實行鼠標滾輪的縮放

// 鼠標滾輪縮放
canvas.on("mouse:wheel", function (opt) {
  const delta = opt.e.deltaY;
  zoom = canvas.getZoom();
  zoom *= 0.999 ** delta;
  
  if (zoom > 20) zoom = 20;
  if (zoom < 0.01) zoom = 0.01;
  canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);

  opt.e.preventDefault();
  opt.e.stopPropagation();

  scaleText.innerText = Math.round(zoom * 100).toString();
});
  1. canvas.on("mouse:wheel", function (opt) { ... });
    為畫布添加了一個鼠標滾輪事件監聽器。當用戶滾動鼠標滾輪時,會觸發這個 function。

  2. const delta = opt.e.deltaY;
    獲取鼠標滾輪的滾動量。deltaY 是一個數值,向上滾動為負值,向下滾動為正值。
    稍後用為增減 zoom 的指標

  3. zoom = canvas.getZoom();
    獲取當前畫布的縮放倍率。先取得才知道要改多少馬

  4. zoom *= 0.999 ** delta;
    計算新的縮放級別。
    這裡使用了指數運算來實現平滑的縮放效果:
    這個運算通常用於需要逐漸減少(指數衰減)某個值的情況,例如在動畫中用於計算逐漸減少的速度或透明度或是在物理模擬或動畫效果中。
    意思是將 0.999 這個數字提升到 delta 次方。

    詳細說明
    • 0.999:基數(base),這是一個接近於 1 的數字。

    • **:指數運算符,用於計算基數的指數次方。

    • delta:指數(exponent),這是一個變量,表示基數要提升的次數。
      ex:
      0.999 ** 2 => 0.999²
      0.999 ** 98 => 0.999⁹⁸

    • delta 為正(向下滾動)時,0.999 ** delta 會小於 1,縮小畫布。

    • delta 為負(向上滾動)時,0.999 ** delta 會大於 1,放大畫布。
      簡單說,就是以指數性放大、指數性縮小

  5. if (zoom > 20) zoom = 20;if (zoom < 0.01) zoom = 0.01;
    這兩行設置縮放的上限和下限,防止過度縮放或縮小。

執行縮放

  1. canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
    使用 Fabric.js 的 zoomToPoint 方法來執行縮放。這個方法接受兩個參數:
    • 縮放的中心點(這裡使用鼠標當前在畫布上的位置){ x: opt.e.offsetX, y: opt.e.offsetY }
    • 新的縮放級別 zoom

最後阻止瀏覽器預設行為,與阻止事件冒泡

  1. opt.e.preventDefault();
    阻止瀏覽器預設的滾動行為。

  2. opt.e.stopPropagation();
    阻止事件冒泡,確保其他元素不會接收到這個滾輪事件。

力求實現了一個平滑的縮放效果,縮放的中心點是鼠標所在的位置,並且設置了縮放的上下限。
使用指數運算 0.999 ** delta 可以讓縮放感覺更自然,而不是線性的縮放。

實行畫面平移

分為三個部分:

  • 點擊滑鼠後=> 開始拖拉&紀錄當前位置
  • 滑鼠平移時 => 使用按下當下的數值來計算目前位移量
  • 滑鼠放開後 => 終止移動狀態&將鼠標改回一般

點擊滑鼠後

let panning = false;
let lastPosX, lastPosY;

// 鼠標按下開始平移
canvas.on("mouse:down", function (opt) {
  const evt = opt.e;
  if (evt.altKey === true) {
    panning = true; // 設定滑鼠已按下
    
    // 紀錄按下去當下的位置
    lastPosX = evt.clientX;
    lastPosY = evt.clientY;
    canvas.setCursor("grab");
  }
});

滑鼠平移時

// 鼠標移動進行平移時
canvas.on("mouse:move", function (opt) {
//如果按下滑鼠,且同時按了 alt 鍵 才會觸發
  if (panning && opt.e.altKey) {
    const evt = opt.e;
    
    // 把目前的 x,y 位移量同步到畫布上
    canvas.viewportTransform[4] += evt.clientX - lastPosX;
    canvas.viewportTransform[5] += evt.clientY - lastPosY;
    canvas.requestRenderAll();
    
    // 更新當前位置
    lastPosX = evt.clientX; 
    lastPosY = evt.clientY;
  }
});

等等,這裡的 canvas.viewportTransform[4]canvas.viewportTransform[5] 是什麼??

viewportTransform 是一个数组,里面有6个元素,默认值是 [1, 0, 0, 1, 0, 0]
从下标0开始,它们分别代表:
[0]: 水平缩放(x轴方向)
[1]: 水平倾斜(x轴方向)
[2]: 垂直倾斜(y轴方向)
[3]: 垂直缩放(y轴方向)
[4]: 水平移动(x轴方向)
[5]: 垂直移动(y轴方向)
其实这涉及到线性代数的知识,在 canvas 中,transform() 方法也可以称为“变换矩阵”。

我们把 viewportTransform 的6个元素分别用 a, b, c, d, e, f 来代替:viewportTransform[a, b, c, d, e, f]

然后这样排列一下,看上去会不会觉得没那么“凌乱”了。
https://ithelp.ithome.com.tw/upload/images/20240822/20168354KdyqCDE1wX.png
---from Fabric.js 变换视窗-腾讯云开发者社区-腾讯云 (tencent.com)
(他舉了蠻多例子,想更了解不同數值會造成的影響可以看這邊)

滑鼠放開後

// 鼠標釋放結束平移
canvas.on("mouse:up", function () {
  panning = false;
  canvas.setCursor("default");
});

設定好結束的狀態,結束這回合的位移。


最後,放上今日的🌰:
fabricjs 畫布縮放與平移


上一篇
Day17-fabric.js 進階組合技!想要在畫布範圍之外的控制點也可以被看到
下一篇
Day19-修改畫布 prototype 的入門-拆解 fabric-history prototype (1)
系列文
一起來玩圖像編輯器:Fabric.js 的實戰修煉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言