# 一. 前言
之前有写过一个低代码工具, 即通过拖拽组件生成一个页面. 现在产品又多了一个需求, 就是在预览时, 用户可以拖拽缩放来查看这个预览页面
此时再去修改低代码的渲染区显示太费功夫, 因为我想到直接写一个给组件添加拖拽缩放功能的组件, 然后包裹预览组件.
# 二. 实现
# 1. 缩放
缩放功能很简单, 监听鼠标滚轮事件即可
<template>
<div
class="drag-wrap"
@mousewheel="handleMousewheel"
:style="{
transform: `scale(${transform.scale})`,
}"
>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
transform: {
scale: 1,
},
};
},
methods: {
handleMousewheel(ev) {
const down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
let scale;
if (down) {
// 滚轮向下
scale = this.transform.scale - 0.1;
} else {
// 滚轮向上
scale = this.transform.scale + 0.1;
}
if (scale > 0.5 && scale < 4) {
this.transform.scale = scale;
}
if (ev.preventDefault) {
ev.preventDefault();
}
return false;
},
},
};
</script>
<style lang="less" scoped>
.drag-wrap {
height: 100%;
}
</style>
# 2. 拖拽
拖拽功能分为 3 部分:
按下鼠标, 开始准备拖拽
拖动鼠标, 组件跟随移动
松开鼠标, 拖拽结束, 所有状态重新初始化
<template>
<div
class="drag-wrap"
@mousewheel="handleMousewheel"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseleave"
:style="{
transform: `scale(${transform.scale}) translate(${transform.x}px, ${transform.y}px)`,
}"
>
<slot></slot>
</div>
</template>
<script>
/**
* 鼠标按下的初始位置
*/
let startPosition = {};
// 是否开始拖动
let isStart = false;
export default {
data() {
return {
transform: {
x: 0,
y: 0,
scale: 1,
},
};
},
methods: {
handleMousewheel(ev) {
const down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
let scale;
if (down) {
// 滚轮向下
scale = this.transform.scale - 0.1;
} else {
// 滚轮向上
scale = this.transform.scale + 0.1;
}
if (scale > 0.5 && scale < 4) {
this.transform.scale = scale;
}
if (ev.preventDefault) {
ev.preventDefault();
}
return false;
},
handleMouseDown(event) {
event.preventDefault();
// 并记录当前位置
startPosition = {
clientX: event.clientX,
clientY: event.clientY,
};
isStart = true;
},
handleMouseMove(event) {
if (isStart) {
const { clientX, clientY } = startPosition;
// 获取鼠标的偏移量
const offsetX = event.clientX - clientX;
const offsetY = event.clientY - clientY;
const { x, y, scale } = this.transform;
this.transform = {
x: x + offsetX,
y: y + offsetY,
scale,
};
startPosition = {
clientX: event.clientX,
clientY: event.clientY,
};
}
},
handleMouseUp(event) {
isStart = false;
},
/**
* 当鼠标先离开.drag-wrap然后再放开鼠标时, 依然可以恢复状态
*/
handleMouseleave(event) {
isStart = false;
},
},
};
</script>
<style lang="less" scoped>
.drag-wrap {
height: 100%;
}
</style>
# 三. 需要注意的点
# 1. 为什么使用 translate 而不是 position
由于相对定位是依赖上一层定位元素, 使用定位可能会导致其子元素依赖的定位元素变化, 而导致一些奇怪的问题
# 2. 为什么使用 scale 而不是 zoom
zoom 会引起整个页面的重绘而 scale 缩放所占据的原始尺寸不变,只在当前元素进行重绘, 因此 scale 性能更好
zoom 缩放中心是左上角而 scale 缩放中心可调整
zoom 会受到最小字体大小为 12px 等限制