Js實現任意位置縮放圖片,深入理解背後原理

ttentau發表於2024-04-16

前言

本文將用一個簡單的例子詳細講解如何用原生JS一步步實現完整的任意位置縮放圖片功能,無任何第三方依賴,指標事件
進行多端統一的事件監聽,乾貨滿滿。

完整程式碼

為提升閱讀體驗,正文中程式碼展示有部分省略處理,完整程式碼可以在碼上掘金檢視:
https://code.juejin.cn/pen/7356824634160840730

可在任意位置縮放、移動圖片

實現原理

實現圖片放大的關鍵點在於 CSS3 中的 transform
變換,該屬性應用於元素在2D或3D上的旋轉,縮放,移動,傾斜等等變換,透過設定 translate(x,y) 即可偏移元素位置,設定 scale
即可縮放元素,當然你也可以只設定 matrix 來完成上述所有操作,這涉及到矩陣變換的知識,本文使用的均是CSS提供的語法糖進行變換操作。

PC上的點選、移動,H5的手勢操作,都離不開DOM事件監聽。例如滑鼠移動事件對應 mousemove
,移動端因為沒有滑鼠則對應 touchmove,而本文將介紹如何僅透過指標事件來進行多端統一
的事件監聽。在監聽事件中我們可以透過 event 物件獲取各種屬性,例如常用的 offsetXoffsetY
相對偏移量,clientXclientY 距離視窗的橫座標和縱座標等。

理解transform-origin值

官方文件解釋為:**transform-origin**CSS 屬性讓你更改一個元素變形的原點。
https://developer.mozilla.org/zh-CN/docs/Web/CSS/transform-origin

我們可以簡單的理解為圖片縮放起點,這個值預設為圖片的正中心,所以我們進行放大或縮小都是依圖片中心來縮放。

css縮放圖片有兩種方法

一、修改transform-origin值進行縮放

優點:簡單快捷,容易理解

缺點:頻繁修改transform-origin值會抖動,需要計算修正量

將滑鼠當前的偏移量offsetX、offsetY 的值改變 transform-origin
來動態設定縮放的原點,再進行縮放,那麼最終效果就是依照最新的transform-origin值來進行縮放。

比如修改transform-origin值為90% 90%。再設定放大倍數scale為1.1。效果就是下圖的樣子

二、不修改transform-origin值,設定偏移量translate(x,y)進行縮放

我們利用滾輪事件監聽並改變 scale 值。重點是利用 deltaY 值的正負來判斷滾輪是朝上還是朝下:

let scale = 1;
image.addEventListener("wheel", function (e) {
  //d值永遠是正的0.1或者負的0.1,代表每次縮放的倍數
  let d = e.deltaY < 0 ? 0.1 : -0.1;
  scale = scale * (1 + d);
...
});

如上圖,怎麼樣從左圖變成右圖呢?由於我們未修改transform-origin,所以縮放始終都以圖片中心
為原點進行縮放,這顯然不符合我們的操作習慣,我們需要的是以滑鼠點為中心對圖片進行縮放。

可以清晰的看到,我們先將圖片放大0.1倍,再設定偏移量translate(x,y)就可以實現任意位置縮放

需要計算的translate(x,y)值,實際上就是放大後的圖片中心點與原始圖片中心點的差值


每次可偏移translate(x,y)最大值 = 長寬 * 放大倍數 / 2

例如:長400,寬為800的圖片。放大0.1倍之後,圖片的長為440,寬為880。如果是在中心點放大,那麼不需要移動圖片,xy等於0。如果在左上角放大,那麼圖片整體向右下角偏移
x = (440 - 400) / 2 = 20,y = (880 - 800) / 2 = 40.

let d = e.deltaY < 0 ? 0.1 : -0.1;
const max = {
  x: (d * rect.width) / 2,
  y: (d * rect.height) / 2,
};

滑鼠點的座標減去圖片的在頁面中的位置可以得到滑鼠在圖片中的位置

rect = image.getBoundingClientRect()
const mouseOffset = {
  x: e.clientX - rect.x,
  y: e.clientY - rect.y
}

最後用mouseOffset減去已偏移的xy,再剩以放大倍數d,再減去max,就是當前的translate(x,y)

  1. 因為mouseOffset的值,基本可以看作為一個常量,如果滑鼠不移動位置,那麼mouseOffset的值不會變,所以需要減去已偏移的xy,才能累計偏移量
  2. 再剩以放大倍數,得到的值,還不能用於偏移
  3. 第二步得到的這個值已經超過我們的最大偏移值max,所以需要減去max,最終就是當前的偏移量
// 計算每次的偏移量
x -= d * (mouseOffset.x - x) - max.x;
y -= d * (mouseOffset.y - y) - max.y;
image.style.transform = "translate3d(" + x + "px, " + y + "px, 0) scale(" + scale + ")";

移動圖片

移動圖片的實現是比較簡單的,在每次指標按下時我們記錄 clientXclientY 為初始值,移動時計算當前的值與初始點位的差值加到 translate 偏移量中即可另外當抬起動作結束時,會觸發 click 事件,所以注意加入全域性變數標記以及定時器進行一些判斷處理。

image.addEventListener("pointerdown", function (e) {
  isPointerdown = true;
  lastPointermove = {x: e.clientX, y: e.clientY};
}); 
image.addEventListener("pointermove", function (e) {
  if (isPointerdown) {
    const current = {x: e.clientX, y: e.clientY};
    x += current.x - lastPointermove.x;
    y += current.y - lastPointermove.y;
    lastPointermove = current;
    image.style.transform = "translate3d(" + x + "px, " + y + "px, 0) scale(" + scale + ")";
  }
  e.preventDefault();
}); 
image.addEventListener("pointerup", function (e) {
  isPointerdown = false;
});

結束

以上就是文章的全部內容,感謝看到這裡,希望對你有所幫助或啟發!創作不易,如果覺得文章寫得不錯,可以點贊收藏支援一下,也歡迎關注,我會更新更多實用的前端知識與技巧,期待與你共同成長~

相關文章