需求:长按鼠标左键框选区域,松开后放大该区域,继续框选继续放大,反向框选恢复原始状态
实现思路:根据鼠标的落点,放大要显示的内容(内层盒子),然后利用水平偏移和垂直偏移,让外层展示的窗口(外层盒子)只看到刚刚框选的大概区域,具体代码如下
<template>
<div>
<div
class="selectable_container"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
>
<div
class="zoomable_element"
:style="{
userSelect: 'none',
left: innerLeft + 'px',
top: innerTop + 'px',
width: innerWidth + 'px',
height: innerHeight + 'px',
}"
>
<img
src="./img/test1.jpg"
style="
width: 100%;
height: 100%;
user-select: none;
pointer-events: none;
"
alt=""
/>
</div>
<div class="selectable_element" id="selectable_element"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
startX: 0,
startY: 0,
endX: 0,
endY: 0,
isSelecting: false, //是否正在款选
closeFlag: false, //是否退出放大状态
offsetinner_left: 0, //外层容器水平偏移
offsetinner_top: 0, //外层容器垂直偏移
outerWidth: 0, //外层盒子宽度
outerHeight: 0, //外层盒子高度
zoomRatio: 1,
innerWidth: "100%", //内层盒子宽度 初始状态等于外层盒子
innerHeight: "100%", //内层盒子高度
innerTop: 0, //内层盒子垂直偏移
innerLeft: 0, //内层盒子水平偏移
selectionLeft: 0, //框选区域水平偏移
selectionTop: 0, //框选区域垂直偏移
selectionWidth: 0, //框选区域宽度
selectionHeight: 0, //框选区域高度,
};
},
mounted() {
const dom_mask = window.document.querySelector(".selectable_container");
const rect_select = dom_mask.getClientRects()[0];
this.offsetinner_left = rect_select.left; //水平偏移
this.offsetinner_top = rect_select.top; //垂直偏移
this.outerWidth = Math.ceil(rect_select.width);
this.outerHeight = Math.ceil(rect_select.height);
this.innerWidth = this.outerWidth;
this.innerHeight = this.outerHeight;
},
methods: {
handleMouseDown(event) {
if (event.button === 0) {
// 判断是否为鼠标左键按下
this.startX = event.clientX - this.offsetinner_left;
this.startY = event.clientY - this.offsetinner_top;
this.isSelecting = true;
var dom = document.getElementById("selectable_element");
if (dom) {
dom.style.left = this.startX + "px";
dom.style.top = this.startY + "px";
}
}
},
handleMouseMove(event) {
if (this.isSelecting) {
this.closeFlag = false;
this.endX = event.clientX - this.offsetinner_left;
this.endY = event.clientY - this.offsetinner_top;
var selectionLeft, selectionTop, selectionWidth, selectionHeight;
selectionWidth = Math.abs(this.endX - this.startX);
selectionHeight = Math.abs(this.endY - this.startY);
// 右下
if (this.endY >= this.startY && this.endX >= this.startX) {
selectionLeft = this.startX;
selectionTop = this.startY;
}
// 左下
else if (this.endY >= this.startY && this.endX <= this.startX) {
selectionLeft = this.endX;
selectionTop = this.startY;
}
// 右上
else if (this.endY <= this.startY && this.endX >= this.startX) {
selectionLeft = this.startX;
selectionTop = this.endY;
}
// 左上
else if (this.endY <= this.startY && this.endX <= this.startX) {
selectionLeft = this.endX;
selectionTop = this.endY;
this.closeFlag = true;
}
selectionLeft = Math.ceil(selectionLeft);
selectionTop = Math.ceil(selectionTop);
selectionWidth = Math.ceil(selectionWidth);
selectionHeight = Math.ceil(selectionHeight);
var dom = document.getElementById("selectable_element");
if (dom) {
dom.style.left = selectionLeft + "px";
dom.style.top = selectionTop + "px";
dom.style.width = selectionWidth + "px";
dom.style.height = selectionHeight + "px";
}
this.selectionLeft = 0 - this.innerLeft + selectionLeft;
this.selectionTop = 0 - this.innerTop + selectionTop;
this.selectionWidth = selectionWidth;
this.selectionHeight = selectionHeight;
}
},
handleMouseUp(event) {
// 判断是否为鼠标左键松开
if (event.button === 0 && this.isSelecting) {
// 左上清除
if (this.closeFlag) {
this.isSelecting = false;
this.closeFlag = false;
var dom = document.getElementById("selectable_element");
if (dom) {
dom.style.left = "0px";
dom.style.top = "0px";
dom.style.width = "0px";
dom.style.height = "0px";
}
this.innerWidth = this.outerWidth;
this.innerHeight = this.outerHeight;
this.innerLeft = 0;
this.innerTop = 0;
return;
}
this.isSelecting = false;
this.zoomRatio = Math.min(
this.outerWidth / this.selectionWidth,
this.outerHeight / this.selectionHeight
).toFixed(2);
this.zoomRatio = Number(this.zoomRatio);
// console.log(this.zoomRatio);
var innerWidth = Math.ceil(this.innerWidth * this.zoomRatio);
var innerHeight = Math.ceil(this.innerHeight * this.zoomRatio);
var innerLeft = 0 - this.selectionLeft * this.zoomRatio;
var innerTop = 0 - this.selectionTop * this.zoomRatio;
// 居中处理
innerLeft =
innerLeft +
(this.outerWidth - this.selectionWidth * this.zoomRatio) / 2;
innerTop =
innerTop +
(this.outerHeight - this.selectionHeight * this.zoomRatio) / 2;
// 补位处理
if (innerWidth + innerLeft < this.outerWidth) {
// console.log("水平补位");
innerLeft = innerLeft + this.outerWidth - (innerWidth + innerLeft);
}
if (innerHeight + innerTop < this.outerHeight) {
// console.log("垂直补位");
innerTop = innerTop + this.innerHeight - (innerHeight + innerTop);
}
this.innerWidth = innerWidth;
this.innerHeight = innerHeight;
this.innerLeft = innerLeft;
this.innerTop = innerTop;
var dom = document.getElementById("selectable_element");
if (dom) {
dom.style.left = "0px";
dom.style.top = "0px";
dom.style.width = "0px";
dom.style.height = "0px";
}
}
},
},
};
</script>
<style lang="scss" scoped>
// 外层可视窗口
.selectable_container {
position: relative;
width: 800px;
height: 450px;
border: 1px solid #ccc;
overflow: hidden;
}
// 框选动作临时盒子
.selectable_element {
position: absolute;
border: 1px solid red;
}
// 内层内容盒子 需要缩放
.zoomable_element {
position: absolute;
left: 0;
top: 0;
}
</style>