【sgDrag_v2】[自主研发]由于html2canvas生成克隆元素周期不固定,采用dragstart、drag、dragend监听拖拽事件,美中不足就是有个禁用的鼠标样式

仔细看上面,拖拽过出现一个禁止的鼠标形状很恶心吧~


sgDrag_v2.vue组件源码

<template></template>
<script>
export default {
    name: 'sgDrag_v2',
    data() {
        return {
            timeout: null,
        }
    },
    props: [
        "data",//可以被拖拽的元素数组(必选)
        "targets",//需要被拖拽接触碰撞检测的元素(必选)
        "disabled",//屏蔽 
        "delay",//默认长按0.2秒 
    ],
    watch: {
        data: {
            handler(newValue, oldValue) {
                newValue ? this.__addDragsEvents(newValue) : this.__removeDragsEvents(oldValue);
            }, deep: true, immediate: true,
        },
        disabled: {
            handler(newValue, oldValue) {
                newValue ? this.__removeDragsEvents(this.data) : this.__addDragsEvents(this.data);
            }, deep: true, immediate: true,
        },
    },
    destroyed() {
        this.__removeDragsEvents(this.data);
    },
    methods: {
        // 初始化需要拖拽的DIV
        __addDragsEvents(doms) {
            (doms || []).forEach(dom => {
                this.__addMouseEvents(dom);
            });
        },
        __removeDragsEvents(doms) {
            (doms || []).forEach(dom => {
                this.__removeMouseEvents(dom);
                this.__removeDraggedEvents(dom);
            });
        },
        __addMouseEvents(dom) {
            this.__removeMouseEvents(dom);
            this.resetGrab(dom);
            dom.addEventListener('mousedown', this.mousedown);
            dom.addEventListener('mouseup', this.mouseup);
        },
        __removeMouseEvents(dom) {
            dom.setAttribute('draggable', false);
            this.resetGrab(dom);
            dom.removeEventListener('mousedown', this.mousedown);
            dom.removeEventListener('mouseup', this.mouseup);
        },
        __addDraggedEvents(dom) {
            this.__removeDraggedEvents(dom);
            dom.addEventListener('dragstart', this.dragstart);
            dom.addEventListener('drag', this.drag);
            dom.addEventListener('dragend', this.dragend);
        },
        __removeDraggedEvents(dom) {
            dom.removeEventListener('dragstart', this.dragstart);
            dom.removeEventListener('drag', this.drag);
            dom.removeEventListener('dragend', this.dragend);
        },
        mousedown(e) {
            if (this.disabled) return this.mouseup(e);
            // 长按delay毫秒开始监听拖拽(防止与其他单击事件冲突)
            let dom = e.currentTarget;
            this.timeout = setTimeout(() => {
                this.timeout = null;
                if (this.disabled) return;
                dom.setAttribute('draggable', true);
                dom.setAttribute('grab', 'down');
                this.$nextTick(() => {
                    this.dragstart(e);
                    this.__addDraggedEvents(dom);
                });
            }, typeof this.delay === 'undefined' ? 200 : this.delay);
        },
        mouseup(e) {
            if (this.disabled) return this.mouseup(e);
            this.resetGrab(e.currentTarget);
            if (this.timeout) {
                this.dragend(e);//放弃拖拽
                clearTimeout(this.timeout), (this.timeout = null);
            }
        },
        resetGrab(dom) {
            this.delay === 0 ? dom.setAttribute('grab', 'ready') : dom.removeAttribute('grab');
        },
        // 拖拽开始
        dragstart(e) {
            if (this.disabled) return this.mouseup(e);
            this.$emit(`dragStart`, this.getResult(e));
        },
        // 拖拽中
        drag(e) {
            if (this.disabled) return this.mouseup(e);
            this.$emit(`dragging`, this.getResult(e));
        },
        // 拖拽结束
        dragend(e) {
            if (this.disabled) return this.mouseup(e);
            let dom = e.currentTarget;
            dom.removeAttribute('draggable');
            this.resetGrab(dom);
            this.__removeDraggedEvents(dom);
            this.$emit(`dragEnd`, this.getResult(e));
        },
        getCrashDoms(e) {
            let targets = this.targets;
            targets['item'] && (targets = [].slice.call(targets));//NodeList对象
            Array.isArray(targets) || (targets = [targets]);//单个元素处理成数组
            return targets.filter(target => this.isMouseInTraget(target, e))
        },
        getResult(e) {
            return {
                $event: e,
                originDrag: e.currentTarget,
                originDragRect: e.currentTarget ? e.currentTarget.getBoundingClientRect() : null,
                crashDoms: this.getCrashDoms(e),
            }
        },
        //鼠标在元素内部
        isMouseInTraget(targetDom, mousePoint) {
            if (!mousePoint) return false;
            let tr = targetDom.getBoundingClientRect();
            let t = { x1: tr.x, x2: tr.x + tr.width, y1: tr.y, y2: tr.y + tr.height };//目标对象的四角顶点坐标
            let isIn =
                mousePoint.clientX >= t.x1 && mousePoint.clientX <= t.x2 &&
                mousePoint.clientY >= t.y1 && mousePoint.clientY <= t.y2;
            return isIn;
        },
    }
};
</script> 
<style lang="scss">
[grab="ready"] {
    cursor: grab !important;
}

[grab="down"] {
    cursor: grabbing !important;
}
</style>

应用组件

<template>
    <div class="sg-body">
        <sgDrag_v2 :data="data" :targets="targets" @dragStart="dragStart" @dragging="dragging" @dragEnd="dragEnd" />

        <div class=" drag-container">
            <div class="title">从此处拖拽出去</div>
            <div class="drag-div" :ref="`drag-div${i}`" v-for="(a, i) in items" :key="i" :index="i">{
   
   { a.label }}</div>
        </div>
        <div class="drag-in-container drag-container" ref="drag-in-container">
            <div class="title">放入此处</div>
            <div class="drag-div" :ref="`drag-div${i}`" v-for="(a, i) in dragInItems" :key="i" :index="i">{
   
   { a.label }}
            </div>
        </div>
    </div>
</template>
    
<script>
import sgDrag_v2 from "@/vue/components/sgDrag_v2";
export default {
    components: { sgDrag_v2 },
    data: () => ({
        data: [],//可以被拖拽的元素数组
        targets: [],//需要被拖拽接触碰撞检测的元素
        checkboxGroupValue: [],
        items: [...Array(20)].map((v, i) => ({ label: '显示文本' + i, value: i })),
        dragInItems: [],//动态拖拽进入的元素列表
    }),
    mounted() {
        this.data = [...Array(20)].map((v, i) => this.$refs[`drag-div${i}`][0]);
        this.targets = [this.$refs['drag-in-container']];
    },
    methods: {
        dragStart(d) {
            // console.log(`拖拽开始`, d);
        },
        dragging(d) {
            // console.log(`拖拽中`, d);
            let crashDoms = d.crashDoms;
            this.targets.forEach(dom => {
                crashDoms.includes(dom) ? dom.setAttribute('active', true) : dom.removeAttribute('active');
            });
        },
        dragEnd(d) {
            // console.log(`拖拽结束`, d);
            this.targets.forEach(dom => dom.removeAttribute('active'));
            // 如果翻下拖拽那一刻,正好在右侧区域内
            if (d.crashDoms.includes(this.targets[0])) {
                let index = parseInt(d.originDrag.getAttribute('index'));
                this.dragInItems.push(this.items.splice(index, 1)[0]);
            }
        },
    }
};
</script>
    
<style lang="scss" scoped>
.sg-body {
    position: absolute;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;

    .title {
        width: 100%;
        height: 50px;
        display: flex;
        justify-content: center;
        align-items: center;
        font-weight: bold;
        font-size: 18px;
    }

    .drag-div {
        width: 100px;
        height: 50px;
        background-color: #409EFF;
        margin: 2px;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .drag-container {
        width: 300px;
        height: 500px;
        overflow-y: auto;
        background-color: #00000011;
        display: flex;
        flex-wrap: wrap;
        align-content: flex-start;
    }

    .drag-in-container {
        background-color: #409EFF11;
        box-sizing: border-box;
        border: 1px dashed transparent;

        &[active] {
            border-color: #409EFF;
            background-color: #409EFF22;
        }
    }
}
</style>

sgDrag 1.0版本去这里↓ 

【sgDrag】[自主研发]基于Vue开发支持批量声明拖拽元素、被碰撞元素,拖拽全过程监听元素碰撞检测并返回拖拽原始元素、克隆元素及其getBoundingClientRect对象和碰撞接触元素数组_你挚爱的强哥的博客-CSDN博客【代码】[自主研发]基于Vue开发支持批量声明拖拽元素、被碰撞元素,拖拽全过程监听元素碰撞检测并返回拖拽原始元素、克隆元素及其getBoundingClientRect()对象和碰撞接触元素数组。https://blog.csdn.net/qq_37860634/article/details/131645686

猜你喜欢

转载自blog.csdn.net/qq_37860634/article/details/131676350