vue2精简方式实现鼠标在方框内拖拽效果源码

前言:最终效果如图如下:

鼠标在橙色盒子上按下,可同时拖拽橙色和灰色盒子在大方框中移动,盒子不会超出方框范围。松开鼠标即停止拖动。

鼠标在灰色盒子上按下,可拖动灰色盒子在橙色盒子中移动,灰色盒子不会超出橙色盒子范围,鼠标松开即停止拖动
在这里插入图片描述

原理如下:

分别监听橙色盒子和灰色盒子的鼠标按下事件,在鼠标按下的那一刹那,将鼠标的“鼠标x”,“鼠标y”坐标保存下来,使用offsetLeft和offsetTop属性算出鼠标在盒子中的位置x,y,鼠标移动的时候,鼠标在盒子中的位置x,y不变,用鼠标的“鼠标x”,“鼠标y”减去鼠标在盒子中的位置x,y,就是盒子移动的距离,根据盒子移动的距离,来设置盒子的left和top距离,就可以随意的控制盒子的位置(盒子必须是position: absolute定位),这样就可以实现鼠标拖动盒子移动的效果了。

(本案例中是两个盒子都可以移动,原理同上,跟一个盒子原理一样。本案例中是将灰色小盒子的鼠标按下事件mousedown代理给了橙色大盒子,所以只需要绑定一个mousedown事件,就分别可以监听两个盒子的mousedown事件,并在js方法中通过e.target区分两个盒子,实际上还是监听了两个盒子的mousedown)

html和css代码结构:

html结构:
<div class="program" ref="bigBox">       // 黑色大方框
        <div class="middle-box" ref="middleBox" @mousedown="middleDown($event)">     // 橙色盒子
            <div class="small-box" ref="smallBox"></div>    // 灰色盒子
        </div>
</div>
//  给middle-box橙色盒子绑定鼠标按下事件@mousedown(此处使用事件冒泡特性,将small-box灰色盒子的鼠标按下事件,通过冒泡的方式代理给橙色盒子,
//  这样只绑定了一个事件,就可以监听到2个盒子的鼠标按下事件,使用e.target属性区分两个盒子)

css:
.program {
    position: relative;
    margin: 30px auto;
    width: 400px;
    height: 400px;
    text-align: center;
    border: 1px solid #000;
    .middle-box {
        position: absolute;
        left: 0;
        top: 0;
        width: 100px;
        height: 100px;
        background-color: rgb(255, 160, 93);
        .small-box {
            position: absolute;
            left: 0;
            top: 0;
            width: 50px;
            height: 50px;
            background-color: #646464;
        }
    }
}

js代码结构介绍:

  1. 页面挂载完之后,获取黑色大方框盒子的信息,存储在bigBox变量中。
  2. 在middleDown方法中使用e.target.className === ‘middle-box’,来判断当前按下的是橙色盒子还是黑色盒子(middle-box为橙色盒子),使用flag变量来记录当前点击的盒子。
  3. 如果是点击橙色盒子,就把橙色盒子的信息存进box中,如果是点击灰色盒子,就把灰色盒子的信息存进box中,方便后续使用。
data () {
        return {
            bigBox: null,
            x: 0,
            y: 0,
            flag: true
        }
},
 mounted () {
        this.bigBox = this.$refs.bigBox;
},
methods: {
		middleDown (e) {
		            console.dir(this.bigBox, 'console.dir============');
		            console.log(this.bigBox, 'console.log============');
		            this.flag = e.target.className === 'middle-box';
		            const box = this.flag ? this.bigBox.children[0] : this.bigBox.children[0].children[0]
			        this.x = e.x - box.offsetLeft;
	            	this.y = e.y - box.offsetTop;
		}
}
  1. 通过console.dir打印的控制台信息可以看到,元素对象上都会有offsetLeft和offsetTop属性
    在这里插入图片描述
  2. 用e.x - box.offsetLeft, e.y - box.offsetTop,分别求出下图中,横向两条绿色线,和竖向两条绿色线加起来的长度,存在x和y中。
    在这里插入图片描述
    ** [注] :使用console.dir()可以打印出元素对象的详细信息,使用console.log只能打印简略信息。但console.dir()只会打印括号中的第一个内容。**
    在这里插入图片描述
  3. 在middleDown方法中监听页面的鼠标移动mousemove和页面的鼠标弹开mouseup事件。
  4. 在鼠标移动事件mousemove里面根据鼠标e.x和e.y移动的距离,分别设置盒子的左右边距(盒子必须是position: absolute定位,通过设置盒子的left和top来控制盒子的位置)来实现盒子跟着鼠标移动。
  5. 在鼠标弹开事件mouseup中,结束监听mousemove事件,此时盒子固定在当前位置上,不在跟随鼠标移动,实现鼠标弹起时停止拖拽的效果。
methods: {
		middleDown (e) {
            console.dir(this.bigBox, 'console.dir============');
            console.log(this.bigBox, 'console.log============');
            this.flag = e.target.className === 'middle-box';
            const box = this.flag ? this.bigBox.children[0] : this.bigBox.children[0].children[0]
            this.x = e.x - box.offsetLeft;
            this.y = e.y - box.offsetTop;
            // console.dir(this.bigBox.children[0], '2');
            window.addEventListener('mousemove', this.mouseMove)
            window.addEventListener('mouseup', this.middleUp)
        },
        middleUp () {
            window.removeEventListener('mousemove', this.mouseMove)
        },
        mouseMove (e) {
            setTimeout(() => {
                const maxNam = this.flag ? 300 : 50;
                const box = this.flag ? this.bigBox.children[0] : this.bigBox.children[0].children[0]
                const moveX = (e.x - this.x) >= maxNam ? maxNam : ((e.x - this.x) <= 0 ? 0 : (e.x - this.x))
                const moveY = (e.y - this.y) >= maxNam ? maxNam : ((e.y - this.y) <= 0 ? 0 : (e.y - this.y))
                box.style.left = moveX + 'px';
                box.style.top = moveY + 'px';
            }, 50);
       }
}
  1. 在mouseMove事件中,首先应设置节流阀节省性能,使用setTimeout方法50毫秒执行一次位置赋值操作,给盒子设置新的位置(毫秒数根据自己情况调节)
  2. 为了不超出外围盒子,设置maxNam为橙色或灰色盒子移动的最大距离,如下图所示:橙色大盒子上下左右移动的最大距离为0-300px,灰色小盒子上下左右移动的最大距离为0-50px。
  3. 用e.x - this.x算出盒子应跟着鼠标移动的距离,如果小于0,就设置移动距离为0。如果大于300或50,就设置移动距离为300或50,然后把移动的距离赋值给盒子的left和top,实现盒子在框里面移动。
    在这里插入图片描述

完整代码如下:

<template>
    <div class="program" ref="bigBox">
        <div class="middle-box" ref="middleBox" @mousedown="middleDown($event)">
            <div class="small-box" ref="smallBox"></div>
        </div>
    </div>
</template>

<script>
export default {
    components: {},
    data () {
        return {
            bigBox: null,
            x: 0,
            y: 0,
            flag: true
        }
    },
    mounted () {
        this.bigBox = this.$refs.bigBox;
    },
    methods: {
        middleDown (e) {
            console.dir(this.bigBox, 'console.dir============');
            console.log(this.bigBox, 'console.log============');
            this.flag = e.target.className === 'middle-box';
            const box = this.flag ? this.bigBox.children[0] : this.bigBox.children[0].children[0]
            this.x = e.x - box.offsetLeft;
            this.y = e.y - box.offsetTop;
            window.addEventListener('mousemove', this.mouseMove)
            window.addEventListener('mouseup', this.middleUp)
        },
        middleUp () {
            window.removeEventListener('mousemove', this.mouseMove)
        },
        mouseMove (e) {
            setTimeout(() => {
                const maxNam = this.flag ? 300 : 50;
                const box = this.flag ? this.bigBox.children[0] : this.bigBox.children[0].children[0]
                const moveX = (e.x - this.x) >= maxNam ? maxNam : ((e.x - this.x) <= 0 ? 0 : (e.x - this.x))
                const moveY = (e.y - this.y) >= maxNam ? maxNam : ((e.y - this.y) <= 0 ? 0 : (e.y - this.y))
                box.style.left = moveX + 'px';
                box.style.top = moveY + 'px';
            }, 50);
        }
    },
}
</script>
<style lang="less" scoped>
.program {
    position: relative;
    margin: 30px auto;
    width: 400px;
    height: 400px;
    text-align: center;
    border: 1px solid #000;
    .middle-box {
        position: absolute;
        left: 0;
        top: 0;
        width: 100px;
        height: 100px;
        background-color: rgb(255, 160, 93);
        .small-box {
            position: absolute;
            left: 0;
            top: 0;
            width: 50px;
            height: 50px;
            background-color: #646464;
        }
    }
}
</style>

  • 41
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值