使用JS實(shí)現(xiàn)任意位置縮放圖片功能
前言
本文將用一個(gè)簡(jiǎn)單的例子詳細(xì)講解如何用原生JS一步步實(shí)現(xiàn)完整的任意位置縮放圖片功能,無(wú)任何第三方依賴,指針事件 進(jìn)行多端統(tǒng)一的事件監(jiān)聽(tīng),干貨滿滿。
完整代碼
HTML:
<div class="wrapper">
<img id="image"
alt=""
src="https://images.pexels.com/photos/459203/pexels-photo-459203.jpeg"/>
</div>CSS:
html, body {
margin: 0;
padding: 0;
overflow: hidden;
}
.wrapper {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #000;
}
img {
max-height: 80vh;
touch-action: none;
}JavaScript:
window.onload = () => {
// 獲取dom
const wrapper = document.querySelector(".wrapper");
const image = document.getElementById("image");
// 全局變量
let rect,
x = 0,
y = 0,
scale = 1,
minScale = 0.2,
maxScale = 16,
isPointerdown = false, // 按下標(biāo)識(shí)
lastPointermove = { x: 0, y: 0 }; // 用于計(jì)算diff
rect = image.getBoundingClientRect()
// 拖拽查看
drag();
// 滾輪縮放
wheelZoom();
// 拖拽查看
function drag() {
// 綁定 pointerdown
image.addEventListener("pointerdown", function (e) {
isPointerdown = true;
lastPointermove = { x: e.clientX, y: e.clientY };
});
// 綁定 pointermove
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();
});
// 綁定 pointerup
image.addEventListener("pointerup", function (e) {
if (isPointerdown) {
isPointerdown = false;
}
});
}
// 滾輪縮放
function wheelZoom() {
wrapper.addEventListener("wheel", function (e) {
let d = e.deltaY < 0 ? 0.1 : -0.1;
let ratio = 1 + d;
let temp_scale = scale * ratio;
if (temp_scale > maxScale) return
if (temp_scale < minScale) return
scale = temp_scale
if (e.target.tagName === "IMG") {
const max = {
x: (d * rect.width) / 2,
y: (d * rect.height) / 2,
};
const mouseOffset = {
x: e.clientX - rect.x,
y: e.clientY - rect.y
}
// 計(jì)算每次的偏移量
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 + ")";
}
e.preventDefault();
});
}
}實(shí)現(xiàn)原理
實(shí)現(xiàn)圖片放大的關(guān)鍵點(diǎn)在于 CSS3 中的 transform 變換,該屬性應(yīng)用于元素在2D或3D上的旋轉(zhuǎn),縮放,移動(dòng),傾斜等等變換,通過(guò)設(shè)置 translate(x,y) 即可偏移元素位置,設(shè)置 scale 即可縮放元素,當(dāng)然你也可以只設(shè)置 matrix 來(lái)完成上述所有操作,這涉及到矩陣變換的知識(shí),本文使用的均是CSS提供的語(yǔ)法糖進(jìn)行變換操作。
PC上的點(diǎn)擊、移動(dòng),H5的手勢(shì)操作,都離不開(kāi)DOM事件監(jiān)聽(tīng)。例如鼠標(biāo)移動(dòng)事件對(duì)應(yīng) mousemove ,移動(dòng)端因?yàn)闆](méi)有鼠標(biāo)則對(duì)應(yīng) touchmove,而本文將介紹如何僅通過(guò)指針事件來(lái)進(jìn)行多端統(tǒng)一 的事件監(jiān)聽(tīng)。在監(jiān)聽(tīng)事件中我們可以通過(guò) event 對(duì)象獲取各種屬性,例如常用的 offsetX、offsetY 相對(duì)偏移量,clientX、clientY 距離窗口的橫坐標(biāo)和縱坐標(biāo)等。
理解transform-origin值
官方文檔解釋為:**transform-origin**CSS 屬性讓你更改一個(gè)元素變形的原點(diǎn)。 https://developer.mozilla.org/zh-CN/docs/Web/CSS/transform-origin
我們可以簡(jiǎn)單的理解為圖片縮放起點(diǎn),這個(gè)值默認(rèn)為圖片的正中心,所以我們進(jìn)行放大或縮小都是依圖片中心來(lái)縮放。
css縮放圖片有兩種方法
一、修改transform-origin值進(jìn)行縮放
優(yōu)點(diǎn):簡(jiǎn)單快捷,容易理解
缺點(diǎn):頻繁修改
transform-origin值會(huì)抖動(dòng),需要計(jì)算修正量
將鼠標(biāo)當(dāng)前的偏移量即 offsetX、offsetY 的值改變 transform-origin 來(lái)動(dòng)態(tài)設(shè)置縮放的原點(diǎn),再進(jìn)行縮放,那么最終效果就是依照最新的transform-origin值來(lái)進(jìn)行縮放。
比如修改transform-origin值為90% 90%。再設(shè)置放大倍數(shù)scale為1.1。效果就是下圖的樣子

二、不修改transform-origin值,設(shè)置偏移量translate(x,y)進(jìn)行縮放
我們利用滾輪事件監(jiān)聽(tīng)并改變 scale 值。重點(diǎn)是利用 deltaY 值的正負(fù)來(lái)判斷滾輪是朝上還是朝下:
let scale = 1;
image.addEventListener("wheel", function (e) {
//d值永遠(yuǎn)是正的0.1或者負(fù)的0.1,代表每次縮放的倍數(shù)
let d = e.deltaY < 0 ? 0.1 : -0.1;
scale = scale * (1 + d);
...
});

如上圖,怎么樣從左圖變成右圖呢?由于我們未修改transform-origin,所以縮放始終都以圖片中心 為原點(diǎn)進(jìn)行縮放,這顯然不符合我們的操作習(xí)慣,我們需要的是以鼠標(biāo)點(diǎn)為中心對(duì)圖片進(jìn)行縮放。
可以清晰的看到,我們先將圖片放大0.1倍,再設(shè)置偏移量translate(x,y)就可以實(shí)現(xiàn)任意位置縮放
需要計(jì)算的translate(x,y)值,實(shí)際上就是放大后的圖片中心點(diǎn)與原始圖片中心點(diǎn)的差值
每次可偏移translate(x,y)的最大值 = 長(zhǎng)寬 * 放大倍數(shù) / 2
例如:長(zhǎng)400,寬為800的圖片。放大0.1倍之后,圖片的長(zhǎng)為440,寬為880。如果是在中心點(diǎn)放大,那么不需要移動(dòng)圖片,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,
};
用鼠標(biāo)點(diǎn)的座標(biāo)減去圖片的在頁(yè)面中的位置可以得到鼠標(biāo)在圖片中的位置
rect = image.getBoundingClientRect()
const mouseOffset = {
x: e.clientX - rect.x,
y: e.clientY - rect.y
}
最后用mouseOffset減去已偏移的xy,再剩以放大倍數(shù)d,再減去max,就是當(dāng)前的translate(x,y)值
- 因?yàn)?code>mouseOffset的值,基本可以看作為一個(gè)常量,如果鼠標(biāo)不移動(dòng)位置,那么
mouseOffset的值不會(huì)變,所以需要減去已偏移的xy,才能累計(jì)偏移量 - 再剩以放大倍數(shù),得到的值,還不能用于偏移
- 第二步得到的這個(gè)值已經(jīng)超過(guò)我們的最大偏移值
max,所以需要減去max,最終就是當(dāng)前的偏移量
// 計(jì)算每次的偏移量
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 + ")";
移動(dòng)圖片
移動(dòng)圖片的實(shí)現(xiàn)是比較簡(jiǎn)單的,在每次指針按下時(shí)我們記錄 clientX、clientY 為初始值,移動(dòng)時(shí)計(jì)算當(dāng)前的值與初始點(diǎn)位的差值加到 translate 偏移量中即可另外當(dāng)抬起動(dòng)作結(jié)束時(shí),會(huì)觸發(fā) click 事件,所以注意加入全局變量標(biāo)記以及定時(shí)器進(jìn)行一些判斷處理。
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;
});
結(jié)束
到此這篇關(guān)于使用JS實(shí)現(xiàn)任意位置縮放圖片功能的文章就介紹到這了,更多相關(guān)JS任意位置縮放圖片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于javascript實(shí)現(xiàn)最簡(jiǎn)單的選項(xiàng)卡切換效果
這篇文章主要介紹了基于javascript實(shí)現(xiàn)最簡(jiǎn)單的選項(xiàng)卡切換效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
JavaScript CollectGarbage函數(shù)案例詳解
這篇文章主要介紹了JavaScript CollectGarbage函數(shù)案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
基于javascript的在火狐里面強(qiáng)制換行字符
火狐也不知道為什么,自己功能上,樣式上面,都無(wú)法讓其強(qiáng)制換行,這里用JS來(lái)實(shí)現(xiàn)火狐里面強(qiáng)制換行的方法。2010-06-06
Node.js實(shí)戰(zhàn) 建立簡(jiǎn)單的Web服務(wù)器
本章我們同樣通過(guò)實(shí)戰(zhàn)的演練,利用Node.js建立一個(gè)簡(jiǎn)單的Web服務(wù)器2012-03-03

