基于 Vue 实现魔方矩阵排列效果

bianmaren 发布于 2020-03-09 01:37:49    访问

标签 : Vue Js

前几天体验了下有赞微商城的店铺装修,里面的自定义魔方矩阵排列布局,可以很大程度方便客户对店铺进行一个自定义的排列。

我们来分析一下功能。

  • 首先,可以选择几行几列进行排列

  • 可以选择哪几块作为一个模块

WX20200309-013426@2x.png


下面是我的代码以及效果图

<html><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>魔方矩阵</title>

    <!-- 引入样式文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.4/lib/index.css">
    <!-- 引入 Vue 和 Vant 的 JS 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vant@2.4/lib/vant.min.js"></script>

    <style type="text/css">
        #app{
            padding: 40px;
        }
        .decorate-cube{
            position: relative;
        }
        .decorate-cube .cube-row:last-of-type .cube-item {
            border-right: 1px solid #e5e5e5;
        }
        .decorate-cube .cube-selected {
            position: absolute;
            background-color: #fff;
            border: 1px solid #ebedf0;
            text-align: center;
            color: #7d7e80;
            cursor: pointer;
            box-sizing: border-box;
        }
        .decorate-cube .cube-selected.cube-selected-click {
            background: #e0edff;
            border: 1px solid #155bd4;
            color: #155bd4;
            z-index: 2;
            cursor: auto;
        }
        .decorate-cube .cube-selected-text {
            font-size: 12px;
            width: 100%;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translateX(-50%) translateY(-50%);
        }
        .decorate-cube .cube-row {
            float: left;
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .decorate-cube .cube-item:first-child {
            border-top: 1px solid #e5e5e5;
        }
        .decorate-cube .cube-item {
            background: #f8f8f8;
            border-left: 1px solid #e5e5e5;
            border-bottom: 1px solid #e5e5e5;
            cursor: pointer;
            text-align: center;
            box-sizing: border-box;
        }
        .decorate-cube .cube-item.item-selecting{
            background: #e0edff;
        }
        .decorate-cube .cube-item.item-selecting .van-icon{
            display: none;
        }
        .decorate-cube .cube-item.item-selected {
            background: #e0edff;
            visibility: hidden;
        }
        .decorate-cube .cube-item.item-selected .van-icon{
            display: none;
        }

    </style>
</head>
<body>

<div id="app">

    <div style="margin-bottom: 20px">
        魔方密度:
        <select v-model="density">
            <option value='4' >4 x 4</option>
            <option value='5' >5 x 5</option>
            <option value='6' >6 x 6</option>
            <option value='7' >7 x 7</option>
        </select>
    </div>

    <div>
        <ul v-for="n in densityNum" >
            <li :class="['cube-item',{'item-selecting': isSelecting(i,n),'item-selected':isSelected(i,n) }]"
                v-for="i in densityNum"
                :style={'width':cubeItemWidth+'px','height':cubeItemHeight+'px'}
                :data-x="i" :data-y="n"
                @click="cubeItemClick($event)">
                <van-icon name="plus" color="#bbbbb" :style={'line-height':cubeItemHeight+'px'} />
            </li>
        </ul>

        <div
             v-for="(item, index) in cudeSelected"
             :style={'width':getCubeSelectedWidth(item)+'px','height':getCubeSelectedHeight(item)+'px','top':getCubeSelectedTop(item)+'px','left':getCubeSelectedLeft(item)+'px'} >
            <div>
                {{ Math.round(750/density*((parseInt(item.end.y) - parseInt(item.start.y) + 1))) }}
                x
                {{ Math.round(750/density*((parseInt(item.end.x) - parseInt(item.start.x) + 1))) }}
                像素</div>
        </div>

    </div>

</div>

<script>

    var vm = new Vue({
        el: '#app',
        data:{
            'density':4, //密度
            'cubeWidth':320, //魔方宽度
            'cubeHeight':320, //魔方高度
            'cudeSelecting':{
                'start':null,
                'end':null,
                'data':[]
            },
            'cudeSelected':[] //已经生成的单元
        },
        computed:{
            //密度值
            densityNum:function () {
                return parseInt(this.density);
            },
            //单元魔方高度
            cubeItemHeight:function () {
                return this.cubeHeight/this.density;
            },
            //单元魔方宽度
            cubeItemWidth:function () {
                return this.cubeWidth/this.density;
            }
        },
        methods:{
            //魔方点击事件
            cubeItemClick:function (event) {

                var el = event.currentTarget;
                var x = el.getAttribute('data-x');
                var y = el.getAttribute('data-y');
                var domclass = el.getAttribute('class');
                console.log('['+x+','+y+','+domclass+']执行了点击');

                var cudeSelectingStart = this.cudeSelecting.start;
                var coord = {x:x,y:y};

                if(-1 !== domclass.indexOf('item-selected')){
                    alert("已经被占用");
                    return;
                }

                if(null == cudeSelectingStart){
                    this.cudeSelecting.start = coord;
                    this.cudeSelecting.data.push(coord);
                }else{

                    this.cudeSelecting.end = coord;
                    this.cudeSelecting.data.push(coord);

                    //获取开始和结束之间所有魔方单元
                    var start = cudeSelectingStart;
                    var end = coord;

                    var x_start = Math.min(start.x,end.x);
                    var x_end = Math.max(start.x,end.x);

                    var y_start = Math.min(start.y,end.y);
                    var y_end = Math.max(start.y,end.y);

                    var real_start = {x:Math.min(start.x,end.x),y:Math.min(start.y,end.y)};
                    var real_end = {x:Math.max(start.x,end.x),y:Math.max(start.y,end.y)};

                    //清空正在选择的
                    vm.cudeSelecting.data.splice(0);

                    for(var i=x_start;i<=x_end;i++){
                        for(var j=y_start;j<=y_end;j++){
                            this.cudeSelecting.data.push({x:i,y:j});
                        }
                    }

                    var cudeSelectingData = this.cudeSelecting.data;
                    var cudeSelectedArr = [];

                    for(var i=0;i<cudeSelectingData.length;i++){
                        cudeSelectedArr.push(cudeSelectingData[i])
                    }

                    console.log(cudeSelectedArr);
                    console.log(real_start);
                    console.log(real_end);

                    //加入选中的
                    var cudeSelected = {
                        'start':real_start,
                        'end':real_end,
                        'data':cudeSelectedArr
                    }
                    this.cudeSelected.push(cudeSelected);

                    //清除正在选择的
                    this.cudeSelecting.start = null;
                    this.cudeSelecting.end = null;
                    vm.cudeSelecting.data.splice(0);

                }

            },
            //判断是否正在选择
            isSelecting:function (x,y) {
                var cudeSelectingData = this.cudeSelecting.data;
                for(var i = 0, len = cudeSelectingData.length; i < len; i++){
                    var coord = cudeSelectingData[i];
                    if(parseInt(coord.x) === parseInt(x) && parseInt(coord.y) === parseInt(y)){
                        return true;
                    }
                }
                return false;
            },
            //判断是否已经选择
            isSelected:function (x,y) {
                var list = this.cudeSelected;
                for(var i=0;i<list.length;i++){
                    var data = list[i].data;
                    for(var j=0;j<data.length;j++){
                        var coord = data[j];
                        if(parseInt(coord.x) === parseInt(x) && parseInt(coord.y) === parseInt(y)){
                            return true;
                        }
                    }
                }
                return false;
            },
            //计算选中层的宽度
            getCubeSelectedWidth:function (item) {
                return (parseInt(item.end.y) - parseInt(item.start.y) + 1) * this.cubeItemWidth;
            },
            //计算选中层的高度
            getCubeSelectedHeight:function (item) {
                return (parseInt(item.end.x) - parseInt(item.start.x) + 1) * this.cubeItemHeight;
            },
            //计算选中层的右边距离
            getCubeSelectedTop:function (item) {
                return (item.start.x-1) * this.cubeItemHeight;
            },
            //计算选中层的左边距离
            getCubeSelectedLeft:function (item) {
                return (item.start.y-1) * this.cubeItemWidth;
            }
        }
    });

</script>

</body></html>

运行效果如下:

WX20200309-013637@2x.png