首页 > Vue >

vue-grid-layout自动添加新元素在空白处算法

时间: 作者:admin 浏览:

vue-grid-layout是一款栅格拖拽布局的vue组件,但在添加新卡片时不会自动放到剩余空位置,因为新卡片的x、y初始值都默认为0,导致新卡片与原有卡片重叠,影响体验;

于是想要实现在添加新卡片时,在画布上自动计算出可放的位置坐标,前面有空位足够放下则放在前面,不够位置放到最后一行的行头;

实现算法之前先给出vue-grid-layout的大概用法,顺便算法也一起写在方法里:

template部分

<template>
    <grid-layout
        :layout.sync="layoutData"
        :col-num="colNum"
        :row-height="rowHeight"
        :is-draggable="true"
        :is-resizable="true"
        :is-mirrored="false"
        :vertical-compact="true"
        :prevent-collision="false"
        :margin="[10, 10]"
        :use-css-transforms="false"
        @layout-mounted="layoutMounted"
        @layout-updated="layoutUpdated"
    >
        <grid-item
            v-for="item in layoutData"
            :x="item.x"
            :y="item.y"
            :w="item.w"
            :h="item.h"
            :i="item.i"
            :key="item.i"
            @resize="resizeEvent"
            @resized="resizedEvent"
        >
            <component :is="item.name" />
        </grid-item>
    </grid-layout>
</template>

javascript部分

    import VueGridLayout from 'vue-grid-layout';
    import a from './components/a.vue'
    import b from './components/b.vue'

export default{
    name:'vueGridLayout',
    components:{
        GridLayout: VueGridLayout.GridLayout,
        GridItem: VueGridLayout.GridItem,
        a,
        b
    },
    data(){
        return {
            layoutData:[{i: 0, x: 0, y: 0, w: 36, h: 28}, {i: 1, x: 36, y: 0, w: 36, h: 28}],
            colNum: 50,
            rowHeight: 10
        }
    },
    methods:{
        addNewItem( item ){
            item.i = this.layoutData.length//i的值可以根据自己实际情况设置
            const { x, y } = this.getPositionForNewItem( item )
            item.x = x
            item.y = y
            //插入到画布
            this.layoutData.push( item )
            //到此完结
        },
        //给新添加的卡片找到合适的位置
        getPositionForNewItem( newItem ){
            //1、确定边界
            let gridMap = [], maxX = this.colNum, maxY = 0
            //2、找到Y的最大值
            let allY = this.layoutData.map(v=>(item.y + item.h))
            maxY = allY.length?Math.max.apply(null, allY) : 0

            let isFind = false, findx = 0, findy = 0
            if( maxY ){
                //3、生成整个画布的方格地图
                let i, j;
                for(i = 0; i < maxX; i++){
                    gridMap[i] = []
                    for(j = 0; j < maxY; j++){
                        gridMap[i][j] = 0
                    }
                }
                //4、标注已被占用的方格
                let a, b, axw, byh;
                this.layoutData.forEach(({ x, y, w, h })=>{
                    axw = x + w; byh = y + h;
                    for(a = x; a < axw; a++){
                        for(b = y; b < byh; b++){
                            gridMap[a][b] = w
                        }
                    }
                })
                //5、根据以上标注的位置信息寻位,w表示已被占用,0表示未被占用
                let x, y, cx, cy, xw, yh;
                for(y = 0; y < maxY; y++){
                    yh = y + newItem.h
                    for(x = 0; x < maxX; x++){
                        if(gridMap[x] && gridMap[x][y]){
                            x = Math.min(maxX - 1, x + gridMap[x][y])
                        }
                        xw = x + newItem.w
                        if(!gridMap[x][y] && xw < maxX && yh < maxY){
                            //在画布内未被占用,则从这个位置开始寻位,看是否能找到足够的位置放新卡片
                            let isMatch = true
                            for(cy = y; cy < yh; c++){
                                for(cx = x; cx < xw; r++){
                                    if(gridMap[cx][cy]){
                                        x = cx + gridMap[cx][cy]//跳过组件的x,提升性能的关键一步
                                        isMatch = false//进来说明已被占用,跳过x,y位置,找下一个方格
                                        break
                                    }
                                }
                                //被占用则退出当前寻位循环
                                if( !isMatch ){
                                    break
                                }
                            }
                            //寻位结束:寻位的所有方格都没被占用的,则找到位置
                            if( isMatch ){
                                isFind = true
                                findx = x
                                findy = y
                                break
                            }
                        }
                    }
                    //找到则退出整个循环
                    if( isFind ){
                        break
                    }
                }
                //遍历完没有找到合适位置,则直接放在最后一行的开始位置x=0,y = maxY+1
                if( !isFind ){
                    findx = 0
                    findy = maxY + 1
                }
            }
            gridMap = []
            allY =  []
            return { x: findx, y: findy }
        }
    }
}

好了,整个代码都写完了,然而我忽然有点明白为什么vue-grid-layout官方不自己来实现这个这么好用的功能了,其实还是考虑到性能问题,看看上面的代码写了多少个循环就知道,组件数量稍微多一点,操作肯定会卡,所以虽然实现了这个功能,但是用不用就要看实际情况了,或者大家看看有没有比这个更好的实现方法?进群告诉我一下?实践过程中解决这个问题可以通过Worker开启新线程进行“异步”计算,防止同步大计算量的等待时间过长或者页面卡顿,有关Worker的用法后续会写新的篇章。

好了,今天就到这,快去试试吧

微信公众号
微信公众号:
  • 前端全栈之路(微信群)
前端QQ交流群
前端QQ交流群:
  • 794324979
  • 734802480(已满)

更多文章

栏目文章


Copyright © 2014-2023 seozhijia.net 版权所有-粤ICP备13087626号-4