图片可拖拽对比轮子
刀刀
12/31/2024
0 字
0 分钟
效果
多张效果图中可以两两对比,效果图可拖拽移动位置,可通过鼠标滚轮放大缩小。
拖拽
思路
拖拽事件分为以下三步:
- 鼠标移入图片标签内,捕获所有鼠标按下的事件
setCapture
- 获取鼠标移动事件
onmousemove
,获取其移动的偏移量clientX
与clientY
- 获取鼠标松开事件,取消所有事件捕获
releaseCapture
需要注意的要点:
- 当前移动的是哪个图片(通过传参来判断)
- 图片
dom
元素是动态创建的,不是固定写死的,因此要动态为元素绑定移动事件
代码
<img class='bigger_img_left' :src="$SN_DESIGN_BASE_IMG + imgs[0].imgUrl" ref="leftImg" :style="leftStyle" :width="width+'px'" :height="height+'px'">
initImgMoveFn(){
const that = this
this.$nextTick(() => {
// 左侧图片元素获取
let imgs_left = document.querySelector('.bigger_img_left')
imgs_left && that.onMove(imgs_left, 'leftStyle')
// 右侧图片元素获取
let imgs_right = document.querySelector('.bigger_img_right')
imgs_right && that.onMove(imgs_right, 'rightStyle')
})
},
// 绑定移动事件,obj为触发事件的dom元素,data为该元素的style样式对象
onMove(obj, data) {
const that = this
obj.onmousedown = function(event){
/* 设置box1捕获所有鼠标按下的事件
* setCapture()
* - 只有IE支持,但是在火狐中调用时不会报错,而如果使用chrome调用,会报错
*/
obj.setCapture && obj.setCapture();
event = event ||window.event
//div的偏移量,鼠标.clientX-元素.offsetLeft;鼠标.clientY-元素.offsetTop
var ol = event.clientX -obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
//为document绑定一个onmousemove事件
document.onmousemove = function(event){
event = event ||window.event
//获取鼠标的坐标
var left = event.clientX-ol;
var top = event.clientY-ot;
//修改box1的位置
that[data].left = left+"px";
that[data].top = top+"px";
};
//为元素绑定一个鼠标松开事件
document.onmouseup = function(){
//当鼠标松开时,被拖拽元素固定在当前位置 onmouseup
//取消document的onmousemove事件
document.onmousemove = null;
document.onmouseup = null;
//当鼠标松开时,取消对事件的捕获
obj.releaseCapture && obj.releaseCapture();
};
/*
* 当我们拖拽一个网页的内容时,浏览器会默认去搜索引擎中搜索内容
* 此时会导致拖拽功能的异常,这是浏览器提供的默认行为
* 如果不希望发生这个行为,则可以通过return false来取消默认行为
*/
return false;
};
},
知识点
document.releaseCapture
非标准方法,使用前请注意跨浏览器支持。
如果该 document 中的一个元素之上当前启用了鼠标捕获,则释放鼠标捕获。通过调用
element.setCapture()
实现在一个元素上启用鼠标捕获。element.setCapture
非标准方法,使用前请注意跨浏览器支持。
在处理一个 mousedown 事件过程中调用这个方法来把全部的鼠标事件重新定向到这个元素,直到鼠标按钮被释放或者
document.releaseCapture()
被调用。括号内如果被设置为
true
, 所有事件被直接定向到这个元素; 如果是false
, 事件也可以在这个元素的子元素上触发。注意
该方法已弃用,请不要在新网站中使用。
$nextTick
Vue
是异步执行dom
更新的,一旦观察到数据变化,Vue
就会开启一个队列,然后把在同一个事件循环(event loop)
当中观察到数据变化的watcher
推送进这个队列。如果这个watcher
被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和Dom
操作。而在下一个事件循环时,Vue
会清空队列,并进行必要的DOM
更新。 当设置vm.someData = 'new value'
,DOM
并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM
更新。如果此时你想要根据更新的DOM
状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新DOM
,可以在数据变化之后立即使用Vue.nextTick(callback)
。这样回调函数在DOM
更新完成后就会调用。
滚轮缩放
思路
事件 wheel
在滚动鼠标滚轮或操作其他类似输入设备时触发。因此可以通过它监听整个 window
的鼠标滚轮事件。触发事件后:
- 判断当前触发事件的鼠标
x
轴坐标,如果小于50%,则左边图片缩放,反之右边图片缩放(因为两张图片平分所有空间) - 判断当前图片的宽高,如果小于等于0则停止缩小
- 判断当前执行的操作是放大还是缩小,触发事件后可通过
e.deltaY
参数获取到当前滚轮的方向
代码
mounted() {
// 监听页面滚动事件
window.addEventListener("wheel", this.onWhell);
},
onWhell(e) {
if (window.innerWidth / 2 > e.clientX) {
// 左侧图片的宽高
if (e.deltaY > 0) {
this.width += 20
this.height += 10
} else {
if(this.width<=30 || this.height<=30) return
this.width -= 20
this.height -= 10
}
} else {
// 右图片的宽高
if (e.deltaY > 0) {
this.w += 20
this.h += 10
} else {
if(this.w<=30 || this.h<=30) return
this.w -= 20
this.h -= 10
}
}
},
总体代码
<template>
<div>
<div class='box' v-if='showPreview'>
<div class='close' @click='closeFn'>×</div>
<div class='top'>
<div class='left'>
<template v-if='imgs[0] && imgs[0].id'>
<div class='name' @click='changeImgFn(0)'>{{imgs[0].name}}(点我切换)效果图</div>
<div class='btns' @click='handleDownLoadFn(0,0)'>下载完整图</div>
<div class='btns' @click='handleDownLoadFn(0,1)'>下载裁剪图</div>
</template>
</div>
<div class='right'>
<template v-if='imgs[1] && imgs[1].id'>
<div class='name' @click='changeImgFn(1)'>{{imgs[1].name}}(点我切换)效果图</div>
<div class='btns' @click='handleDownLoadFn(1,0)'>下载完整图</div>
<div class='btns' @click='handleDownLoadFn(1,1)'>下载裁剪图</div>
</template>
</div>
</div>
<div class='bottom'>
<div ref="left_box">
<div class='list_box' v-if='!imgs[0] || !imgs[0].id'>
<div class='list' v-for='item in resultList' :key='item.id' @click='handlePreviewFn(item, 0)'>{{item.name}}</div>
</div>
<template v-else>
<img class='bigger_img_left' :src="$SN_DESIGN_BASE_IMG + imgs[0].imgUrl" ref="leftImg" :style="leftStyle"
:width="width+'px'" :height="height+'px'">
<div class='thumbnail'>
<img :src="$SN_DESIGN_BASE_IMG + imgs[0].thumbnail" width="100px" height="100px">
<div>裁剪效果</div>
</div>
</template>
</div>
<div>
<div class='list_box' v-if='!imgs[1] || !imgs[1].id'>
<div class='list' v-for='item in resultList' :key='item.id' @click='handlePreviewFn(item, 1)'>{{item.name}}</div>
</div>
<template v-else>
<img class='bigger_img_right' :src="$SN_DESIGN_BASE_IMG + imgs[1].imgUrl" alt="" :width="w+'px'" :height="h+'px'" :style="rightStyle">
<div class='thumbnail'>
<img :src="$SN_DESIGN_BASE_IMG + imgs[1].thumbnail" width="100px" height="100px">
<div>裁剪效果</div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
resultList: {
type: Array,
default: () => []
}
},
data() {
return {
showPreview:false,
width: 320,
height: 160,
w: 320,
h: 160,
leftStyle: {
position: 'absolute',
// transition: 'all 0.3s',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
},
rightStyle: {
position: 'absolute',
// transition: 'all 0.3s',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
},
imgs: [],
}
},
mounted() {
// 监听页面滚动事件
const that = this
window.addEventListener("wheel", that.onWhell);
},
methods: {
initImgMoveFn(){
const that = this
this.$nextTick(() => {
let imgs_left = document.querySelector('.bigger_img_left')
imgs_left && that.onMove(imgs_left, 'leftStyle')
let imgs_right = document.querySelector('.bigger_img_right')
imgs_right && that.onMove(imgs_right, 'rightStyle')
})
},
onMove(obj, data) {
console.log(obj, data)
const that = this
obj.onmousedown = function(event){
//设置box1捕获所有鼠标按下的事件
/*
* setCapture()
* - 只有IE支持,但是在火狐中调用时不会报错
* 而如果使用chrome调用,会报错
*/
obj.setCapture && obj.setCapture();
event = event ||window.event
//div的偏移量,鼠标.clientX-元素.offsetLeft
//div的偏移量,鼠标.clientY-元素.offsetTop
var ol = event.clientX -obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
//为document绑定一个onmousemove事件
document.onmousemove = function(event){
event = event ||window.event
//当鼠标移动时被拖拽的元素跟随鼠标移动 onmousemove
//获取鼠标的坐标
var left = event.clientX-ol;
var top = event.clientY-ot;
console.log(left)
console.log(top)
//修改box1的位置
that[data].left = left+"px";
that[data].top = top+"px";
};
//为元素绑定一个鼠标松开事件
document.onmouseup = function(){
//当鼠标松开时,被拖拽元素固定在当前位置 onmouseup
//取消document的onmousemove事件
document.onmousemove = null;
document.onmouseup = null;
//当鼠标松开时,取消对事件的捕获
obj.releaseCapture && obj.releaseCapture();
};
/*
* 当我们拖拽一个网页的内容时,浏览器会默认去搜索引擎中搜索内容
* 此时会导致拖拽功能的异常,这是浏览器提供的默认行为
* 如果不希望发生这个行为,则可以通过return false来取消默认行为
*/
return false;
};
},
changeImgFn(e) {
this.$set(this.imgs, e, {})
},
// 关闭效果图预览
closeFn() {
this.showPreview = false
this.imgs = []
this.width = 320
this.height = 160
this.w = 320
this.h = 160
this.leftStyle = {
position: 'absolute',
// transition: 'all 0.3s',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
}
this.rightStyle = {
position: 'absolute',
// transition: 'all 0.3s',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
}
this.$emit('closeFn')
},
// 点击下载按钮:type左0还是右1;which完整图0还缩略图1
handleDownLoadFn(type, which) {
const that = this
var image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
var url = canvas.toDataURL("image/png",0.7); //得到图片的base64编码数据
var a = document.createElement("a"); // 生成一个a元素
var event = new MouseEvent("click"); // 创建一个单击事件
a.download = which ? that.imgs[type].name+'裁剪图.png' : that.imgs[type].name+'.png'; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
let imgsrc = which ? this.imgs[type].thumbnail :this.imgs[type].imgUrl
image.src = this.$SN_DESIGN_BASE_IMG + imgsrc
console.log(image)
},
// 点击选择图片
handlePreviewFn(e, i) {
this.$set(this.imgs, i, e)
this.initImgMoveFn()
},
onWhell(e) {
if (window.innerWidth / 2 > e.clientX) {
if (e.deltaY > 0) {
this.width += 20
this.height += 10
} else {
if(this.width<=30 || this.height<=30) return
this.width -= 20
this.height -= 10
}
} else {
if (e.deltaY > 0) {
this.w += 20
this.h += 10
} else {
if(this.w<=30 || this.h<=30) return
this.w -= 20
this.h -= 10
}
}
},
}
}
</script>
<style lang='less' scoped>
.box {
position: fixed;
top: 0;
left: 0;
width: 100vw;height: 100vh;
background-color: rgba(0,0,0,.5);
z-index:9999;
.bigger_img_left,
.bigger_img_right {
cursor: move;
}
.close {
position: absolute;
top: 10px;
right: 20px;
font-size: 25px;
cursor: pointer;
}
.top {
height: 60px;
line-height: 60px;
background-color: #fff;
> div {
display: inline-block;
height: 100%;
width: 50%;
overflow: hidden;
padding-left: 25px;
> div {
display: inline-block;
}
.name {
cursor: pointer;
&:hover {
color: #1890ff;
}
}
.btns {
height: 50px;
line-height: 50px;
margin-top: 5px;
padding: 0 20px;
border: 1px solid #ccc;
border-radius: 10px;
cursor: pointer;
margin-left: 20px;
box-sizing: border-box;
&:hover {
background-color: #1890ff;
color: #fff;
border: 1px solid #1890ff;
}
}
.name {
font-size: 20px;
font-weight: 600;
}
}
}
.bottom {
height: calc(100vh - 60px);
> div {
display: inline-block;
position: relative;
height: 100%;
width: 50%;border: 1px solid red;
overflow: hidden;
.list_box {
height: 500px;
margin-top: 200px;
overflow-y: auto;
}
.list {
background-color: #fff;
width: 50%;
height: 60px;
line-height: 60px;
text-align: center;
margin: 10px auto;
cursor: pointer;
&:hover {
color: #fff;
background-color: #1890ff;
}
&:first-child {
margin-top: 200px;
}
}
.thumbnail {
position: absolute;
top: 0;
right: 0;
width: 120px;
text-align: center;
padding-top: 10px;
height: 140px;
background-color: #fff;
box-sizing: border-box;
}
}
}
}
</style>