使用JS实现任意位置缩放图片功能
作者:zyronon
前言
本文将用一个简单的例子详细讲解如何用原生JS一步步实现完整的任意位置缩放图片功能,无任何第三方依赖,指针事件 进行多端统一的事件监听,干货满满。
完整代码
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, // 按下标识
lastPointermove = { x: 0, y: 0 }; // 用于计算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
}
// 计算每次的偏移量
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();
});
}
}实现原理
实现图片放大的关键点在于 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;
});
结束
到此这篇关于使用JS实现任意位置缩放图片功能的文章就介绍到这了,更多相关JS任意位置缩放图片内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
