vue實現一個組件其實很簡單但是要寫出一個好的可復用的組件那就需要多學習和鑽研一下,一個好的組件必須有其必不可少的有優點:一是能提高應用開發效率、測試性、復用性等;二是組件應該是高內聚、低耦合的;三是組件應遵循單向數據流的原則。
在實現我的圖片的拖拽組件我們的搞清其原理,在這里我使用的是mousedown,mousemove和mouseup來實現拖拽。如圖所示:
1.新建ElementDrag.vue文件內容如下:
<template>
<div class="drag-outer"
ref="dragWrap"
@mousemove="dragMousemove">
<div class="drag-inner"
ref="dragElement"
@mousedown="dragMousedown"
@mouseup.stop="isMousedown = false">
<slot></slot>
</div>
</div>
</template>
2. 定義moveStart用於記錄拖拽元素初始位置。定義isMousedown變量來判斷鼠標是否按下, 如果isMousedown === true鼠標移動就改變darg-inner位置。
<script>
export default {
name: 'ElementDrag',
data() {
return {
isMousedown: false, //鼠標是否按下
moveStart: {x: 0, y: 0}, //拖拽元素初始位置
translate: {x: 0, y: 0}, //計算拖拽元素在下下x,y方向各移動了多少
scale: 1, //拖拽元素縮放值
}
},
methods: {
dragMousedown() {
this.moveStart.x = event.clientX
this.moveStart.y = event.clientY
this.isMousedown = true
},
dragMousemove() {
if(this.isMousedown) {
this.translate.x += (event.clientX - this.moveStart.x) / this.scale
this.translate.y += (event.clientY - this.moveStart.y) / this.scale
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
this.moveStart.x = event.clientX
this.moveStart.y = event.clientY
}
}
}
}
</script>
3.樣式部分我們設置外層drag-outer用flex布局讓里面元素快速居中, user-drag: none;禁用圖片等元素的可拖拽屬性。
<style lang="scss" scoped>
.drag-outer {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
.drag-inner {
transform-origin: center center;
display: flex;
justify-content: center;
align-items: center;
cursor: move;
user-select: none;
>* {
-webkit-user-drag: none;
user-drag: none;
}
}
}
</style>
4.添加鼠標滾輪事件縮放drag-inner元素, e.wheelDelta為正表示放大,為負縮小,值越大表示滾動越快。通過scale控制拖拽元素縮放。同過對組件傳值scaleZoom來控制其縮放最大最小程度值。
...
methods: {
props: {
type: Object,
default(){
return {
min: 0.5,
max: 5
}
}
},
...
handleScroll(e) {
let speed = e.wheelDelta/120
if(e.wheelDelta > 0 && this.scale < this.scaleZoom.max) {
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}else if(e.wheelDelta < 0 && this.scale > this.scaleZoom.min){
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}
}
},
mounted() {
window.addEventListener('mousewheel',this.handleScroll,false)
}
5.以上代碼已經實現了功能我們預期的功能,但是為了更好的體驗,需要進一步優化組件。添加isHover來控制鼠標是否移動到了drag-outer以外,如果isHover為false這時鼠標滾輪滾動不會縮放元素,並且將isMousedown設置為false。再使用插槽slot預覽位置。
<template>
<div class="drag-outer"
ref="dragWrap"
@mouseenter="isHover = true"
@mouseleave="isHover = isMousedown = false"
@mousemove="dragMousemove">
<div class="drag-inner"
ref="dragElement"
@mousedown="dragMousedown"
@mouseup.stop="isMousedown = false">
<slot></slot>
</div>
</div>
</template>
....
data() {
return {
...,
isHover: false,
}
},
methods: {
...
handleScroll(e) {
if(this.isHover) {
let speed = e.wheelDelta/120
if(e.wheelDelta > 0 && this.scale < this.scaleZoom.max) {
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}else if(e.wheelDelta < 0 && this.scale > this.scaleZoom.min){
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}
}
}
}
貼上組件的最終代碼:
<template>
<div class="drag-outer"
ref="dragWrap"
:style="outerOptions"
@mouseenter="isHover = true"
@mouseleave="isHover = isMousedown = false"
@mousemove="dragMousemove">
<div class="drag-inner"
ref="dragElement"
:style="innerOptions"
@mousedown="dragMousedown"
@mouseup.stop="isMousedown = false">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'ElementDrag',
props: {
outerOptions: {
type: Object,
default () {
return {
background: 'rgba(0,0,0,0.9)'
}
}
},
innerOptions: {
type: Object,
default () {
return {
background: 'rgba(0,0,0,0.1)',
}
}
},
scaleZoom: {
type: Object,
default () {
return {
max: 5,
min: 0.2
}
}
}
},
data() {
return {
isMousedown: false,
isHover: false,
moveStart: {},
translate: {x: 0, y: 0},
scale: 1
}
},
methods: {
handleScroll(e) {
if(this.isHover) {
let speed = e.wheelDelta/120
if(e.wheelDelta > 0 && this.scale < this.scaleZoom.max) {
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}else if(e.wheelDelta < 0 && this.scale > this.scaleZoom.min){
this.scale+=0.04*speed
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
}
}
},
dragMousedown() {
this.moveStart.x = event.clientX
this.moveStart.y = event.clientY
this.isMousedown = true
},
dragMousemove() {
if(this.isMousedown) {
this.translate.x += (event.clientX - this.moveStart.x) / this.scale
this.translate.y += (event.clientY - this.moveStart.y) / this.scale
this.$refs.dragElement.style.transform = `scale(${this.scale}) translate(${this.translate.x}px, ${this.translate.y}px)`
this.moveStart.x = event.clientX
this.moveStart.y = event.clientY
}
}
},
mounted() {
window.addEventListener('mousewheel',this.handleScroll,false)
}
}
</script>
<style lang="scss" scoped>
.drag-outer {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
.drag-inner {
transform-origin: center center;
display: flex;
justify-content: center;
align-items: center;
cursor: move;
user-select: none;
>* {
-webkit-user-drag: none;
user-drag: none;
}
}
}
</style>
在home.vue文件使用:點擊體驗
<template>
<div class="home">
<ElementDrag>
<img src="https://img0.baidu.com/it/u=937276518,3474029246&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750">
</ElementDrag>
</div>
</template>
<script>
import ElementDrag from '@/components/ElementDrag'
export default {
name: 'Home',
components: {
ElementDrag
}
}
</script>
<style scoped>
.home {
width: 100vw;
height: 100vh;
}
</style>