1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > vue 实现pc h5拖拽悬浮挂件 附带吸边效果

vue 实现pc h5拖拽悬浮挂件 附带吸边效果

时间:2018-08-10 16:09:51

相关推荐

vue 实现pc h5拖拽悬浮挂件 附带吸边效果

c

分清clientY pageY screenY layerY offsetY的区别

在我们想要做出拖拽这个效果的时候,我们需要分清这几个属性的区别,这几个属性都是计算鼠标点击的偏移值,我们需要对其进行了解才可以继续实现我们的拖拽效果

clientY 指的是距离可视页面左上角的距离pageY 指的是距离可视页面左上角的距离(不受页面滚动影响)screenY 指的是距离屏幕左上角的距离layerY 指的是找到它或它父级元素中最近具有定位的左上角距离offsetY 指的是距离它自己左上角的距离

在简单了解完这些个属性以后,有几个属性需要分清。

实现拖拽功能

1. h5实现拖拽

---html---<template><div><divref="drag"@click.prevent="onNext"@touchstart.prevent="down"@touchmove.prevent="move"@touchend.prevent="end":style="getPosition"><img :src="imageUrl" class="icon" /></div></div></template>

js

<script>export default {name: 'Drag',props:{positionInit:Object}data () {return {flags: false,position: {x: 0,y: 0},positionStyles: {top: 0,left: 0,right: 0,bottom: 0},nx: '',ny: '',maxW: '',maxH: '',initLeftRight: 20}},computed: {getPosition () {const objs = this.positionStyleslet str = ''if (objs.top !== undefined && objs.top !== null) {str = str + 'top:' + objs.top + 'px;'}if (objs.bottom !== undefined && objs.bottom !== null) {str = str + 'bottom:' + objs.bottom + 'px;'}if (objs.left !== undefined && objs.left !== null) {str = str + 'left:' + objs.left + 'px;'}if (objs.right !== undefined && objs.right !== null) {str = str + 'right:' + objs.right + 'px;'}return str}},wacth:{//初始值positionInit 从父组件获得,获得后给positionStyles赋值,此处太easy省略}mounted () {},methods: {onNext () { // 点击事件this.flags = false},// 实现拖拽down (event) {if (!this.h5CmsIconData.draggable) return falseevent.preventDefault()const dragDiv = this.$refs.draglet touchif (event.touches) {touch = event.touches[0]} else {touch = event}// 光标起始位置 touch.clientX/touch.clientY;// 左偏移量 offsetLeft 上偏移量 offsetTopthis.position.x = touch.clientX - dragDiv.offsetLeftthis.position.y = touch.clientY - dragDiv.offsetTop},move (event) {if (!this.h5CmsIconData.draggable) return falseevent.preventDefault()this.flags = trueconst dragDiv = this.$refs.dragif (this.flags) {let touchif (event.touches) {touch = event.touches[0]} else {touch = event}// 添加限制:只允许在屏幕内拖动// 页面宽高度减去悬浮框宽高// offsetWidth和offsetHeight对象自身的的宽度/高度this.maxW = document.documentElement.clientWidth - dragDiv.offsetWidth// 页面高度this.maxH = document.documentElement.clientHeight - dragDiv.offsetHeight// 根据初始touch位置计算移动距离:元素移动位置=元素初始位置+(光标移动后的位置-光标点击时的初始位置)this.nx = this.position.x + (touch.clientX - this.position.x)this.ny = this.position.y + (touch.clientY - this.position.y)if (this.nx <= 0) {this.nx = this.initLeftRight} else if (this.nx > this.maxW) {this.nx = this.maxW}if (this.ny <= 0) {this.ny = this.initLeftRight + monHeaderHeight} else if (this.ny >= this.maxH) {this.ny = this.maxH}dragDiv.style.left = this.nx + 'px'dragDiv.style.top = this.ny + 'px'}},// 鼠标释放时候的函数end (event) {if (!this.h5CmsIconData.draggable) return monHeaderHeight = document.getElementById('h5-common-header-box').clientHeightconst dragDiv = this.$refs.default_drag_compconst divH = this.maxH - dragDiv.offsetHeightconst halfMaxWidth = (this.maxW - dragDiv.offsetWidth) / 2// 吸边处理if (!this.flags) { // 点击事件处理this.onNext()return false}// debuggerif (this.nx > halfMaxWidth) {// 右吸边dragDiv.style.left = this.maxW - (parseInt(dragDiv.clientWidth) / 2) + 'px' // 20 = initLeftRight} else {// 左吸边dragDiv.style.left = this.initLeftRight + 'px'}// 上吸边 盒子高度顶部距离 < headerif (dragDiv.offsetTop < monHeaderHeight) {dragDiv.style.top = ((dragDiv.clientHeight / 2) + monHeaderHeight) + 'px'} else if (this.ny >= divH) { // 下吸边dragDiv.style.top = this.maxH - ((parseInt(dragDiv.clientHeight) / 2) + this.initLeftRight) + 'px'}this.flags = false}}}</script>

Css 定位fixed布局,这个很简单,无需浪费文笔。

接下来重点讲一下pc拖拽的布局和遇到的坑

大概的坑有几个

1.drag事件,move结束的时候获取x,y值,最后都会为0,0. 所以每次拖拽就会回到初始位置,左上角。而且drag有兼容性问题(最后选择用了鼠标事件,详情看代码)

浏览器兼容情况如下图

2.点击事件和拖拽事件互斥。每次拖拽结束,都会触发点击事件。

3.切换页面以后,每次都会更新全局拖拽组件,导致每次切换以后悬浮挂件回到初始位置。(我司实现需要记住上一次位置,不管是跳转页面点击切换都不会回到初始值)

4.拖拽太快导致move事件失效,获取不了end的x,y值 导致结束位置不对.

接下来看代码

2.pc实现拖拽

<template><div><divref="drag"draggable="true"@mousedown.prevent="down":style="getPosition"><img :src="imageUrl" class="icon" /></div></div></template><script>export default {name: 'Drag',props: {positionInit: Object},data () {return {flags: false,//点击事件开关position: {x: 0,y: 0},positionStyles: {top: 0,left: 0,right: 0,bottom: 0},nx: '',ny: '',maxW: '',maxH: '',initLeftRight: 20,commonHeaderHeight: ''}},computed: {getPosition () {const objs = this.positionStyleslet str = ''if (objs.top !== undefined && objs.top !== null) {str = str + 'top:' + objs.top + 'px;'}if (objs.bottom !== undefined && objs.bottom !== null) {str = str + 'bottom:' + objs.bottom + 'px;'}if (objs.left !== undefined && objs.left !== null) {str = str + 'left:' + objs.left + 'px;'}if (objs.right !== undefined && objs.right !== null) {str = str + 'right:' + objs.right + 'px;'}return str}},watch: {//解决切换页面,导致挂件回到初始值 通过判断上一次的结束位置是否>0来记住位置positionInit(val) {const positionStyles = this.positionStylesif ((positionStyles.left && positionStyles.left > 0) || (positionStyles.right && positionStyles.right > 0) || (positionStyles.top && positionStyles.top > 0) || (positionStyles.bottom && positionStyles.bottom > 0)) {this.positionStyles = positionStyles} else {// 初始值赋值 this.positionStyles = positionInit}}},mounted () {},methods: {goNext () { //点击事件 业务this.flags = false},// 实现拖拽down (event) {const dragDiv = this.$refs.draglet touchif (event.touches) {touch = event.touches[0]} else {touch = event}// 光标起始位置 touch.clientX/touch.clientY;// 左偏移量 offsetLeft 上偏移量 offsetTopthis.position.x = touch.clientX - dragDiv.offsetLeftthis.position.y = touch.clientY - dragDiv.offsetTop// 问题:拖拽太快导致move事件失效,获取不了end的x,y值 导致结束位置不对// 方法:通过dom绑定鼠标move和end 事件// 原理:vue 通过on 绑定只会绑定一次,如果太快容易失焦,通过dom绑定可解决// 注意: dom绑定以后,需要清楚绑定,不然就会埋下bugdocument.onmousemove = async (e) => {await this.move(e)}document.onmouseup = (e) => {this.end(e)document.onmousemove = nulldocument.onmouseup = null}},move (event) {event.preventDefault()const dragDiv = this.$refs.dragthis.flags = trueif (this.flags) {let touchif (event.touches) {touch = event.touches[0]} else {touch = event}// dragDiv.offsetWidth和offsetHeight对象自身的的宽度/高度this.maxW = document.documentElement.clientWidth - dragDiv.offsetWidththis.maxH = document.documentElement.clientHeight - dragDiv.offsetHeightthis.positionStyles.left = touch.clientXthis.positionStyles.top = touch.clientY}},end (event) {if (!this.flags) { // 点击事件处理this.goNext()return}let touchif (event.touches) {touch = event.touches[0]} else {touch = event}this.checkBian(touch.clientX, touch.clientY)this.flags = false},checkBian (x, y) {let positionStyles = monHeaderHeight = document.getElementById('pc-common-header-box').clientHeightconst dragDiv = this.$refs.dragdragDiv.removeEventListener('mousemove', this.move, true)dragDiv.removeEventListener('mouseup', this.move, true)const divH = this.maxH - dragDiv.offsetHeightconst halfMaxWidth = this.maxW / 2if (x > halfMaxWidth) {// 右吸边positionStyles = {...positionStyles,left: null,right: this.initLeftRight}} else {// 左吸边positionStyles = {...positionStyles,left: this.initLeftRight,right: null}}// 上吸边 盒子高度顶部距离 < headerif (y < monHeaderHeight) {positionStyles = {...positionStyles,top: this.initLeftRight + monHeaderHeight,bottom: null}} else if (y >= divH) { // 下吸边positionStyles = {...positionStyles,top: null,bottom: this.initLeftRight}} else { // 随拖动值clientY 移动positionStyles = {...positionStyles,top: y,bottom: null}}this.$set(this, 'positionStyles', positionStyles)}}}</script>css fixed定位省略

结语

到这里我们就已经把拖拽效果用Vue实现了,我们用了两种不同的方式实现了拖拽,但实际上换汤不换药,我们需要弄清楚pageY、screenY、clientY、layerY、offsetY等区别。当然我们同时也学习了Vue的一些方法,例如自定义指令等。

欢迎各位大佬指正,有更好的实现方式可多留言交流

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。