前言
本文將用一個簡單的例子詳細講解如何用原生JS一步步實現完整的任意位置縮放圖片功能,無任何第三方依賴,指標事件
進行多端統一的事件監聽,乾貨滿滿。
完整程式碼
為提升閱讀體驗,正文中程式碼展示有部分省略處理,完整程式碼可以在碼上掘金檢視:
https://code.juejin.cn/pen/7356824634160840730可在任意位置縮放、移動圖片
實現原理
實現圖片放大的關鍵點在於 CSS3 中的 transform
變換,該屬性應用於元素在2D或3D上的旋轉,縮放,移動,傾斜等等變換,透過設定 translate(x,y)
即可偏移元素位置,設定 scale
即可縮放元素,當然你也可以只設定 matrix
來完成上述所有操作,這涉及到矩陣變換的知識,本文使用的均是CSS提供的語法糖進行變換操作。
PC上的點選、移動,H5的手勢操作,都離不開DOM事件監聽。例如滑鼠移動事件對應 mousemove
,移動端因為沒有滑鼠則對應 touchmove
,而本文將介紹如何僅透過指標事件來進行多端統一
的事件監聽。在監聽事件中我們可以透過 event
物件獲取各種屬性,例如常用的 offsetX
、offsetY
相對偏移量,clientX
、clientY
距離視窗的橫座標和縱座標等。
理解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)
值
- 因為
mouseOffset
的值,基本可以看作為一個常量,如果滑鼠不移動位置,那麼mouseOffset
的值不會變,所以需要減去已偏移的xy,才能累計偏移量 - 再剩以放大倍數,得到的值,還不能用於偏移
- 第二步得到的這個值已經超過我們的最大偏移值
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 + ")";
移動圖片
移動圖片的實現是比較簡單的,在每次指標按下時我們記錄 clientX
、clientY
為初始值,移動時計算當前的值與初始點位的差值加到 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;
});
結束
以上就是文章的全部內容,感謝看到這裡,希望對你有所幫助或啟發!創作不易,如果覺得文章寫得不錯,可以點贊收藏支援一下,也歡迎關注,我會更新更多實用的前端知識與技巧,期待與你共同成長~