1. 引言
在 Web 应用中,支持DOM元素缩放与拖拽是一项常见的需求,尤其在图片查看器、流程图操作中。本文将详细解析一段用于实现缩放与拖拽交互的 JavaScript 代码。
2.预览
3. 代码解析
3.1 代码概述
这段代码定义了一系列函数,用于实现对目标元素的鼠标滚轮缩放和拖拽平移功能。其核心逻辑包括:
- 监听鼠标滚轮事件,实现缩放功能。
- 监听鼠标按下、移动和释放事件,实现拖拽功能。
- 使用
transform
进行位移和缩放变换。
3.2 代码核心变量
let scale = 1; // 当前缩放比例
let callback = null; // 缩放回调函数
let translateX = 0, translateY = 0; // 平移偏移量
let isDragging = false; // 是否处于拖拽状态
let startX, startY; // 记录鼠标拖拽起始位置
let target = null; // 目标元素
let wrapper = null; // 容器元素
这些变量用于存储当前缩放比例、拖拽状态、鼠标起始位置等信息,以便在交互过程中进行更新。
4. 事件绑定
4.1 初始化函数 zoomInit
该函数用于初始化缩放和拖拽功能,绑定鼠标滚轮、拖拽等事件。
export function zoomInit(dom1, dom2, cb) {
callback = cb;
scale = 1;
wrapper = dom1.value;
target = dom2.value;
在 wrapper
(容器元素)上监听滚轮事件,实现缩放:
wrapper.addEventListener("wheel", function (event) {
event.preventDefault();
const rect = target.getBoundingClientRect();
const offsetX = event.clientX - rect.left;
const offsetY = event.clientY - rect.top;
const originX = offsetX / rect.width;
const originY = offsetY / rect.height;
wheelZoomFunc({scaleFactor: event.deltaY, originX, originY});
});
在 wrapper
上监听 mousedown
、mousemove
、mouseup
事件,实现拖拽功能:
wrapper.addEventListener("mousedown", function (event) {
isDragging = true;
startX = event.clientX - translateX;
startY = event.clientY - translateY;
target.style.cursor = "grabbing";
});
wrapper.addEventListener("mousemove", function (event) {
if (!isDragging) return;
translateX = event.clientX - startX;
translateY = event.clientY - startY;
updateTransform();
});
wrapper.addEventListener("mouseup", function () {
isDragging = false;
target.style.cursor = "grab";
});
}
5. 变换更新
5.1 更新 transform
样式
function updateTransform() {
target.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
}
该函数用于更新 target
的 transform
样式,实现缩放和平移。
5.2 处理缩放 wheelZoomFunc
export function wheelZoomFunc({scaleFactor, originX = 0.5, originY = 0.5, isExternalCall = false}) {
let ratio = 1.1;
if (scaleFactor && scaleFactor > 0) {
ratio = 1 / ratio;
}
const rect = target.getBoundingClientRect();
let newScale = isExternalCall ? scaleFactor : scale * ratio;
newScale = Math.max(0.5, Math.min(newScale, 5));
const deltaScale = newScale / scale;
translateX -= (originX - 0.5) * rect.width * (deltaScale - 1);
translateY -= (originY - 0.5) * rect.height * (deltaScale - 1);
scale = newScale;
callback && callback((scale * 100).toFixed(0));
updateTransform();
}
该函数通过 scaleFactor
控制缩放,并调整 translateX
和 translateY
以确保缩放围绕鼠标焦点进行。
6. 重置功能
6.1 resetImage
函数
export function resetImage() {
scale = 1;
translateX = 0;
translateY = 0;
isDragging = false;
startX = null;
startY = null;
target.style.transform = "translate3d(0px, 0px, 0px) scale(1)";
}
该函数用于将 target
归位,恢复初始缩放比例和位移。
7. 使用教程
7.1 初始化
zoomInit(boxRef, contentRef, (val) => {
console.log(`缩放百分比${val}%`)
})
7.2 放大缩小按钮外部使用
7.2.1 放大
wheelZoomFunc({scaleFactor: num.value / 100 + 0.1, isExternalCall: true})
7.2.2 缩小
wheelZoomFunc({scaleFactor: num.value / 100 - 0.1, isExternalCall: true})
8. 示例代码
源码地址GitHub:github.com/SpanManX/vu…
源码地址Gitee:gitee.com/testcjw/vue…
9. 源码
let scale = 1; // 当前缩放比例
let callback = null; // 缩放回调函数
let translateX = 0, translateY = 0; // 平移偏移量
let isDragging = false; // 是否处于拖拽状态
let startX, startY; // 记录鼠标拖拽起始位置
let target = null; // 目标元素
let wrapper = null; // 容器元素
export function zoomInit(dom1, dom2, cb) {
callback = cb;
scale = 1;
wrapper = dom1.value
target = dom2.value
// 在 wrapper 内部监听鼠标滚轮缩放
wrapper.addEventListener("wheel", function (event) {
event.preventDefault(); // 阻止默认滚动行为
const rect = target.getBoundingClientRect();
const offsetX = event.clientX - rect.left;
const offsetY = event.clientY - rect.top;
const originX = offsetX / rect.width;
const originY = offsetY / rect.height;
wheelZoomFunc({scaleFactor: event.deltaY, originX, originY});
});
// 鼠标按下,开始拖拽
wrapper.addEventListener("mousedown", function (event) {
isDragging = true;
startX = event.clientX - translateX;
startY = event.clientY - translateY;
target.style.cursor = "grabbing";
// setTimeout(() => {
// target.setPointerCapture(event.pointerId);
// },100)
});
// 鼠标移动,执行拖拽
wrapper.addEventListener("mousemove", function (event) {
if (!isDragging) return;
translateX = event.clientX - startX;
translateY = event.clientY - startY;
updateTransform();
});
// 鼠标松开,停止拖拽
wrapper.addEventListener("mouseup", function () {
isDragging = false;
target.style.cursor = "grab";
});
}
/**
* 重置大小和位置
**/
export function resetImage() {
scale = 1;
translateX = 0
translateY = 0;
isDragging = false;
startX = null
startY = null;
target.style.transform = "translate3d(0px, 0px, 0px) scale(1)";
}
function updateTransform() {
target.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
}
export function wheelZoomFunc({scaleFactor, originX = 0.5, originY = 0.5, isExternalCall = false}) {
let ratio = 1.1;
if (scaleFactor && scaleFactor > 0) {
ratio = 1 / ratio;
}
const rect = target.getBoundingClientRect();
let newScale = isExternalCall ? scaleFactor : scale * ratio;
newScale = Math.max(0.5, Math.min(newScale, 5));
const deltaScale = newScale / scale;
translateX -= (originX - 0.5) * rect.width * (deltaScale - 1);
translateY -= (originY - 0.5) * rect.height * (deltaScale - 1);
scale = newScale;
callback && callback((scale * 100).toFixed(0));
updateTransform();
}
10. 结论
这段代码提供了一个完整的DOM缩放与拖拽交互方案,主要通过监听鼠标事件并动态修改 transform
来实现。通过 wheelZoomFunc
处理缩放,并结合 mousedown
、mousemove
、mouseup
事件实现拖拽,使得交互体验更加自然。