【sgRectSelect】自定义组件:Vue实现拖拽鼠标圈选、划区域、框选组件:矩形区域选中checkbox,并回调相关选中、取消选中的操作。

边框线虚线动画效果请参阅边框虚线滚动动画特效_虚线滚动效果_你挚爱的强哥的博客-CSDN博客【代码】边框虚线滚动动画特效。_虚线滚动效果https://blog.csdn.net/qq_37860634/article/details/130507289 

碰撞检测原理请前往 原生JS完成“一对一、一对多”矩形DIV碰撞检测、碰撞检查,通过计算接触面积(重叠覆盖面积)大小来判断接触对象DOM_js 碰撞检测_你挚爱的强哥的博客-CSDN博客这里就需要去遍历所有的target,计算每个重叠面积大小,挑出面积最大的那一个。stackArea=0代表没有重叠;stackArea >0代表有交集。为了方便计算比较,我们通常是在上面的代码基础上加一个面积大小判断,_js 碰撞检测https://blog.csdn.net/qq_37860634/article/details/121688431

还可以用此组件实现类似资源管理器的圈选效果

 文章来源地址https://uudwc.com/A/z3jv5


sgRectSelect框选组件源码  

<template>
    <div :class="$options.name" v-if="startPoint && endPoint" :style="style"
        :borderAnimate="borderAnimate === '' || borderAnimate">
        <slot></slot>
    </div>
</template>
  
<script>
import $g from "@/js/sg";
export default {
    name: 'sgRectSelect',
    data() {
        return {
            targets: [],//圈选目标数组
            startPoint: null,
            endPoint: null,
            style: {
                width: '0px',
                height: '0px',
                top: '0px',
                left: '0px',
            },
            oldSelectedDoms: [],//记录上一次圈选内容用于对比 
            disabled_: false,
            triggerRectElement_: null,
            rangeRectElement_: null,
            minDragDis_: null,
        }
    },
    props: [
        "data",//(必选)建议用一个复杂对象,方便后续识别操作
        /* data是一个数组格式:
        [
            {
                dom:文档对象,//必选
                index:'索引',
                id:'元素的id',
                refName:'ref别名',
                selectEvent:'选中后的操作',
                unSelectEvent:'取消选中后的操作',
            } ,
        ...
        ]
        也可以是一维数组,只包含将可能被选中的DOM数组
        */
        "disabled",//屏蔽
        "disabledWhenShowSels",//当出现对应['sel','sel','sel',...]的时候,屏蔽拖拽选区(譬如出现element的v-modal)
        "minDragDis",//最少拖拽的距离(单位px)缺省值20
        "borderWidth",
        "borderColor",
        "backgroundColor",
        "borderAnimate",//是否显示边框动画
        "triggerRectElement",//允许触发拖拽圈选的容器元素
        "rangeRectElement",//拖拽圈选的显示范围容器元素
    ],
    watch: {
        data: {
            handler(newValue, oldValue) {
                this.targets = newValue || [];
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        disabled: {
            handler(newValue, oldValue) {
                this.disabled_ = newValue;
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        disabled_: {
            handler(newValue, oldValue) {
                newValue ? this.__removeWindowEvents() : this.__addWindowEvents();
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        minDragDis: {
            handler(newValue, oldValue) {
                this.minDragDis_ = newValue;
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        triggerRectElement: {
            handler(newValue, oldValue) {
                this.triggerRectElement_ = newValue;
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        rangeRectElement: {
            handler(newValue, oldValue) {
                this.rangeRectElement_ = newValue;
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
    },
    destroyed() { this.__removeWindowEvents(); },
    methods: {
        __checkDisabled(d) {
            let aa = this.disabledWhenShowSels || [];
            aa && (Array.isArray(aa) || (aa = [aa]));
            let r = [];
            for (let i = 0, len = aa.length; i < len; i++) {
                let a = aa[i];
                let dom = document.querySelector(a);
                if (dom) {
                    r.push(dom);
                    return true;
                }
            }
            return r.length !== 0;
        },
        __setProperty(dom) {
            if (!dom) return;
            dom.style.setProperty("--borderWidth", this.borderWidth || '1px');
            dom.style.setProperty("--borderColor", this.borderColor || '#409EFF');
            dom.style.setProperty("--backgroundColor", this.backgroundColor || '#409EFF22');
        },
        __addWindowEvents() {
            this.__removeWindowEvents();
            addEventListener('mousedown', this.mousedown);
        },
        __addWindowEvents_mousemove_mouseup() {
            this.__removeWindowEvents_mousemove_mouseup();
            addEventListener('mousemove', this.mousemove);
            addEventListener('mouseup', this.mouseup);
        },
        __removeWindowEvents() {
            removeEventListener('mousedown', this.mousedown);
            this.__removeWindowEvents_mousemove_mouseup();
        },
        __removeWindowEvents_mousemove_mouseup() {
            removeEventListener('mousemove', this.mousemove);
            removeEventListener('mouseup', this.mouseup);
        },
        __mousedown(e) {
            this.__addWindowEvents_mousemove_mouseup();
            let { x, y } = e;
            this.startPoint = { x, y };
        },
        mousedown(e) {
            if (this.disabled) return;
            if (this.__checkDisabled()) return;
            if (this.triggerRectElement_
                && !$g.isMouseInTarget(this.triggerRectElement_, e, true)
                && this.$el
                && !$g.isMouseInTarget(this.$el, e, true)
            ) return;
            this.__mousedown(e);
        },
        mousemove(e) {
            if (this.disabled) return;
            if (this.__checkDisabled()) return;
            if (this.triggerRectElement_
                && !$g.isMouseInTarget(this.triggerRectElement_, e, true)
                && this.$el
                && !$g.isMouseInTarget(this.$el, e, true)
            ) return;
            if (this.startPoint) {
                this.endPoint = { x: e.clientX, y: e.clientY };
                let width = this.endPoint.x - this.startPoint.x;
                let height = this.endPoint.y - this.startPoint.y;
                let dragWdithDis = Math.abs(width), dragHeightDis = Math.abs(height);
                let style = {
                    left: (width > 0 ? this.startPoint.x : this.endPoint.x),
                    top: (height > 0 ? this.startPoint.y : this.endPoint.y),
                    width: dragWdithDis,
                    height: dragHeightDis,
                }
                // 不允许圈选区域超出指定元素范围
                if (this.rangeRectElement_) {
                    let { left, top, width, height } = this.rangeRectElement_.getBoundingClientRect();
                    style.left < left && (style.left = left);
                    style.top < top && (style.left = top);
                    style.left + style.width > left + width && (style.width = left + width - style.left);
                    style.top + style.height > top + height && (style.height = top + height - style.top);
                }
                Object.keys(style).forEach(k => { style[k] = `${style[k]}px` }), this.style = style;
                let minDragDis = this.minDragDis_ || 20;//最少拖拽的距离(单位px)缺省值20
                if (dragWdithDis > minDragDis && dragHeightDis > minDragDis) {
                    this.$nextTick(() => {
                        this.$emit('start', e);
                        this.__setProperty(this.$el);
                        // 当新圈选的对象数组和上次圈选的对象数组不同的时候执行
                        let newSelectedDoms = this.getSelectedDoms();
                        this.isSameDoms(newSelectedDoms, this.oldSelectedDoms) || this.$emit('select', this.oldSelectedDoms);
                    });
                }
            }
        },
        mouseup(e) {
            if (this.disabled) return;
            if (this.__checkDisabled()) return;
            this.__removeWindowEvents_mousemove_mouseup();
            this.oldSelectedDoms = [];
            this.startPoint = null;
            this.endPoint = null;
            this.$emit('end', e);
        },
        /* 此方法目的:
        1、解决不能用JSON.stringify(d)来判断引用对象数组的问题;
        2、解决每次圈选的元素没有先来后到的顺序关系 */
        isSameDoms(newSelectedDoms, oldSelectedDoms) {
            let newDoms = newSelectedDoms.map(v => v.dom);
            let isSame = true;
            // 从老数组里面剔除新数组里面没有的dom
            let oldSelectedDoms_temp = [];
            oldSelectedDoms.forEach(v => {
                if (newDoms.includes(v.dom)) {
                    oldSelectedDoms_temp.push(v);
                } else {
                    isSame = false;//只要有一个不同,就代表新圈选的内容和老的内容不同
                }
            });
            let oldDoms = oldSelectedDoms_temp.map(v => v.dom);
            newSelectedDoms.forEach(v => {
                if (oldDoms.includes(v.dom)) {
                } else {
                    oldSelectedDoms_temp.push(v);
                    isSame = false;//只要有一个不同,就代表新圈选的内容和老的内容不同
                }
            });
            this.oldSelectedDoms = oldSelectedDoms_temp;
            return isSame;
        },
        // 获取被选中的DOM
        getSelectedDoms() {
            let r = [];
            if (this.targets && this.targets.length && this.targets[0].dom) {
                r = this.targets.filter(v => {
                    let selected = $g.isCrash(v.dom, this.$el);
                    (v.selectEvent && selected) && v.selectEvent(v);//执行被框选后的方法
                    (v.unSelectEvent && !selected) && v.unSelectEvent(v);//执行取消框选后的方法
                    return selected ? true : false;
                });
            } else {
                let doms = (this.targets && this.targets.length) ? this.targets : this.$parent.$el.querySelectorAll(`*`);
                r = [].slice.call(doms || []).filter(targetDom => $g.isCrash(targetDom, this.$el));
            }
            // 获取被圈选的内容
            return r || [];
        },
    },
}
</script>
  
<style lang="scss" scoped>
.sgRectSelect {
    z-index: 999999; //根据情况自己拿捏
    position: fixed;
    box-sizing: border-box;
    border: var(--borderWidth) solid var(--borderColor);
    background-color: var(--backgroundColor);

    /*边框虚线滚动动画特效*/
    &[borderAnimate] {
        border: none;
        background: linear-gradient(90deg, var(--borderColor) 60%, transparent 60%) repeat-x left top/10px var(--borderWidth),
            linear-gradient(0deg, var(--borderColor) 60%, transparent 60%) repeat-y right top/var(--borderWidth) 10px,
            linear-gradient(90deg, var(--borderColor) 60%, transparent 60%) repeat-x right bottom/10px var(--borderWidth),
            linear-gradient(0deg, var(--borderColor) 60%, transparent 60%) repeat-y left bottom/var(--borderWidth) 10px, var(--backgroundColor);

        animation: border-animate .382s infinite linear;
    }

    @keyframes border-animate {
        0% {
            background-position: left top, right top, right bottom, left bottom;
        }

        100% {
            background-position: left 10px top, right top 10px, right 10px bottom, left bottom 10px;
        }
    }
}
</style>

应用组件 :

<template>
    <div class="sg-body">
        <sgRectSelect borderAnimate borderWidth="2px" borderColor="#F56C6C" backgroundColor="#F56C6C22"
            style="border-radius: 8px" @select="select" :data="data" />

        <el-checkbox-group v-model="checkboxGroupValue">
            <el-checkbox border :ref="`checkbox${i}`" v-for="(a, i) in checkboxs" :label="a.value" :key="i">{{ a.label
            }}</el-checkbox>
        </el-checkbox-group>
    </div>
</template>
    
<script>
import sgRectSelect from "@/vue/components/sgRectSelect";
export default {
    components: { sgRectSelect },
    data: () => ({
        data: [],
        checkboxGroupValue: [],
        checkboxs: [...Array(50)].map((v, i) => ({ label: '显示文本' + i, value: i }))
    }),
    mounted() {
        /* data是一个数组格式:
        [
            {
                dom:文档对象,//必选
                index:'索引',
                id:'元素的id',
                refName:'ref别名',
                selectEvent:'选中后的操作',
                unSelectEvent:'取消选中后的操作',
            } ,
        ...
        ]
        */
        this.data = [...Array(50)].map((v, i) => ({
            dom: this.$refs[`checkbox${i}`][0].$el,
            index: i,
            refName: `checkbox${i}`,
            /*  selectEvent: () => {
                 this.checkboxGroupValue = this.checkboxGroupValue.concat(i);
                 this.checkboxGroupValue = [...new Set(this.checkboxGroupValue)];
             },
             unSelectEvent: () => {
                 this.checkboxGroupValue = this.checkboxGroupValue.filter(v => v !== i);
             }, */
        }));
    },
    methods: {
        select(d) {
            this.checkboxGroupValue = [];
            d.forEach(v => {
                this.checkboxGroupValue = this.checkboxGroupValue.concat(v.index);
                this.checkboxGroupValue = [...new Set(this.checkboxGroupValue)];
            });
            console.log(`选中的对象`, d);
        },
    }
};
</script>
    
<style lang="scss" scoped>
.sg-body {
    position: absolute;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}
</style>
阅读剩余 89%

原文地址:https://blog.csdn.net/qq_37860634/article/details/131616362

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

上一篇 2023年09月01日 00:14
下一篇 2023年09月01日 00:23