Split 面板分割

概述

1 .將一片區(qū)域,分割為可以拖拽調(diào)整寬度或高度的兩部分
2 .傳入?yún)?shù):
3 .父組件

<template>
    <div class="split">
        <div v-if="id==0" class="split_wraper">
//根據(jù)傳入的id來(lái)實(shí)現(xiàn)對(duì)應(yīng)的面板分割的布局
            <div v-if="num==1" class="split_one">
                111
            </div>
            <div v-else-if="num>=2" class="split_more">
                <div class="split_more_left" :style="computed_left">
                    <QA />
//左邊的模板
                </div>
                <div class="split_tool_shu" draggable 
                    @dragend="handleDragEnd"
                    @dragover="handleDrag">
                </div>
//拖拽的分割線,加的是drag事件,其中最主要的是dragend事件,現(xiàn)在綁了倆來(lái)確保一定不會(huì)出問(wèn)題
                <div class="split_more_right" ref="split_more_right">
                    <SplitSon 
                        v-for="(c,index) in num-1" 
                        :index="index"
                        :parent_height="right_height" 
                        :height_arr="right_arr_height">
                        <QQ />
//通過(guò)slot傳入自定義的組件,在子組件的情況下
                        </SplitSon>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
import QA from '../qaclient/qq'
import SplitSon from './son/split_son'
export default {
    props:{
        id:{
            type:Number,
            default:0,
            // 根據(jù)不同的代號(hào)編輯不同的效果,默認(rèn)代號(hào)是0,實(shí)現(xiàn)左右各自一半,但是右邊要開(kāi)小宮格。就是要避面頁(yè)面來(lái)回切換的問(wèn)題
        },
        num:{
            type:Number,
            default:5,
            // 要分割的數(shù)目,展現(xiàn)一種自適應(yīng)的效果,
        }
    },
    data:function(){
        return {
            width:80,
            right_height:0,
//右邊父元素的高度
            right_arr_height:[]
//右邊高度的數(shù)組分布情況。其實(shí)主要就是在操作這個(gè)數(shù)組
        }
    },
    methods:{
        handleDrag(e){  
            console.log(e)       
            this.width=Math.floor(((e.clientX/window.innerWidth)*100))
        },
        handleDragEnd(e){
            this.width=Math.floor(((e.clientX/window.innerWidth)*100))
        }
//左右是根據(jù)flex,自定義的情況來(lái)布局
    },
    computed:{
        computed_left(){
            return{
                width:`${this.width}%`
            }
        }
    },
    components:{
        QA,SplitSon
    },
    mounted(){
        if(this.num>2){
            this.right_height=this.$refs.split_more_right.clientHeight
            const item=Math.floor(100/(this.num-1))
            for(let i=1;i<this.num;i++){
                this.right_arr_height.push(item*i)
            }
           //根據(jù)寬度和當(dāng)前需要顯示的元素,來(lái)一個(gè)初始化元素的高度
        }
    }
}
</script>
<style lang="less" src="./index.less">
</style>

4 .子元素代碼

//這里是一個(gè)豎向的拖拽子組件
<template>
    <div :style="computedHeight" class="split_son">
//每一個(gè)組件的高度和寬度,拖拽其實(shí)就是在改變這個(gè)東西
        <slot></slot> 
//傳進(jìn)來(lái)的組件就顯示在slot里面,
        <div 
            v-if="this.index+1<this.height_arr.length"
//拖拽的那條線,最后一個(gè)元素要保證他沒(méi)有這條線
            draggable
            @dragend="handleDragEnd"
            @dragover="handleOver"
            class="split_son_heng">
            
        </div>
    </div>
</template>
<script>
export default {
    props:{
        index:{
            type:Number,
            required:true,
        },
        height_arr:{
            type:Array,
//所有元素的相對(duì)位移偏移保存數(shù)組
        },
        parent_height:{
            type:Number
        }
    },
    computed:{
          computedHeight(){
            if(this.index==0&&this.height_arr[0]){
                var height_pre=this.height_arr[0]
                return {
                    width:"100%",
                    height:`${height_pre}%`
                 }
            }else if(this.height_arr[1]){
                var height_pre=this.height_arr[this.index]-this.height_arr[this.index-1]
                return {
                    width:"100%",
                    height:`${height_pre}%`
                }
            }       
        } 
    },
    methods:{
        handleDragEnd(e){
            let offsetY=e.offsetY
            if(offsetY>0){
                // 向下拖拽              
                let newHeight=this.$parent.right_arr_height[this.index]+Math.floor((offsetY/this.parent_height)*100)
                if(this.index<this.height_arr.length){
                        if(newHeight>=this.$parent.right_arr_height[this.index+1]){
                            newHeight=this.$parent.right_arr_height[this.index+1]-1
                            // 防止拖拽超過(guò)最后一個(gè)
                        }
                }else{
                    if(newHeight>=100){
                        newHeight=100
                        // 防止拖拽超過(guò)最后一個(gè)
                    }
                }
                
                this.$parent.right_arr_height.splice(this.index,1,newHeight)
            }else{
                // 向上拖拽,向上和向下還是有點(diǎn)不一樣的

                const newOffset=-(offsetY)
                let newHeight=Math.floor((newOffset/this.parent_height)*100)
                if(this.index==0){
                    if(newHeight>this.height_arr[0]){
                        newHeight=1
                    }else{
                        newHeight=this.$parent.right_arr_height[0]-newHeight
                    }
                    // 拖拽第一個(gè),并且拖拽小于0的時(shí)候,讓他最小只能縮小到1%的高度
                }else{
                    if(this.$parent.right_arr_height[this.index]-newHeight>this.$parent.right_arr_height[this.index-1]){
                        newHeight=this.$parent.right_arr_height[this.index]-newHeight
                    }else{
                        newHeight=this.$parent.right_arr_height[this.index-1]+1
                    }
                }
                
                this.$parent.right_arr_height.splice(this.index,1,newHeight)
            }
        },
        handleOver(){

        },
        
    }
}
</script>
<style lang="less" src="./split_son.less"></style>

5 .核心思想

1 .[20,55,75,100]:基礎(chǔ)數(shù)據(jù)就是這個(gè),算的時(shí)候就是每個(gè)元素拖拽的那條線的位置相對(duì)于整個(gè)父元素的位置,這個(gè)很好求。
2 .拖拽的時(shí)候其實(shí)是改變這個(gè)數(shù)據(jù)里面的元素的大小
3 .最后渲染的高度計(jì)算其實(shí)就是當(dāng)前元素減掉前一個(gè),比如第一個(gè)元素的高度占總體高度的25%,第二個(gè)元素就是55-20=35,那么第二個(gè)元素的高度就是35%,非常容易理解

升級(jí)版

<template>
    <div class="li-split" ref="splitWrapper" :class="wrapperClasses">
         {{offset}}
       <div v-if="isHorizontal" :class="`${prefix}-horizontal`">
           <div class="li-split-left-pane" :style="computedLeft">
               <slot name="left"/>
           </div>
           <div class="li-split-trigger-heng-con" 
                @mousedown="handleMouseDown"
                :style="computedTrigger"           
            >
                <div class="li-split-trigger-heng-con-wrapper">
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                    <i class="li-split-trigger-heng-con-bar"></i>
                </div>       
           </div>
           <div class="li-split-right-pane" :style="computedRight">
               <slot name="right"/>
           </div>
       </div>
       <div v-else :class="`${prefix}-vertical`">

       </div>
    </div>
</template>
<script>
import {on,off}from "../../../utils/dom"

export default {
    name:"Split",
    // 我要把橫豎兩種分成兩個(gè)組件,因?yàn)檫@個(gè)想要兼容多個(gè),就是可以支持分割2個(gè)以上的間隔
    props:{
        min:{
            type:Number,
            default:10,
        },
        max:{
            type:Number,
            default:99,
        },
        isHorizontal:{
            type:Boolean,
            default:true
        }
    },
    data:function(){
        return {
            prefix:"li-split",
            offset:50,
            isMouse:false,
            wrapperHeight:0,
            wrapperWidth:0,
            wrapperLeft:0,
            wrapperRight:0,
            wrapperTop:0,
            wrapperBottom:0,
            left:100,
            right:100,
            minDistance:null,
            maxDistance:null,
        }
    },
    methods:{
        handleMouseDown(e){
            this.isMouse=true;
            this.initOffset=this.isHorizontal?e.pageX:e.pageY
            on(document,'mousemove',this.handleMove)
            on(document,'mouseup',this.handleUp)
            this.$emit("on-move-satrt")
        },
        handleMove(e){
            if(this.isMouse){
               if(this.isHorizontal){
                    // 只改變x位置
                    const left=e.pageX
                    if(left<this.wrapperLeft||left>this.wrapperRight){
                        return
                    }
                    if(this.minDistance&&left<this.minDistance){
                        this.offset=this.min
                        return 
                    }
                    if(this.maxDistance&&left>this.maxDistance){
                        this.offset=this.max
                        return 
                    }
                    this.left=left
                    this.offset=(left-this.wrapperLeft)/this.wrapperWidth*100
               }else{
                   const top=e.pageY
                   this.top=top
                //    只改變y位置
               }
               this.$emit("on-move")
            }else{
                // 沒(méi)有按下鼠標(biāo),這里不會(huì)跑到
            }
        },
        handleUp(e){
            this.isMouse=false
            off(document,'mousemove',this.handleMove)
            off(document,'mouseup',this.handleUp)
            this.$emit("on-move-end")
        },
        initWrapper(){
            const dom=this.$refs.splitWrapper.getBoundingClientRect()
            this.wrapperHeight=dom.height
            this.wrapperWidth=dom.width
            this.wrapperLeft=dom.left+document.documentElement.scrollLeft
            this.wrapperRight=this.wrapperLeft+dom.width
            this.wrapperTop=dom.top+document.documentElement.scrollLeft
            this.wrapperBottom=this.wrapperTop+dom.height

            if(this.min&&this.isHorizontal){
                // 有最小值,并且方向是橫向,所以要以寬度進(jìn)行計(jì)算
                this.minDistance=this.wrapperWidth*this.min/100
                console.log(this.minDistance)
            }

            if(this.max&&this.isHorizontal){
                this.maxDistance=this.wrapperWidth*this.max/100
            }

            if(this.min&&!this.isHorizontal){
                this.minDistance=this.wrapperHeight*this.min/100
            }

            if(this.max&&!this.isHorizontal){
                this.maxDistance=this.wrapperHeight*this.max/100
            }
        }
    },
    computed:{
        wrapperClasses(){
            return [

            ]
        },
        computedLeft(){
            return {
                "width":`${this.offset}%`
            }
        },
        computedRight(){
            return {
                "width":`${100-this.offset}%`
            }
        },
        computedTrigger(){
            return {
                position:"absolute",
                "left":`${this.left}px`,
            }
        }
    },
    components:{
        
    },
    mounted(){
        this.initWrapper()
    }
}
</script>
<style lang="less" src="./split.less">

</style>


@name:.li-split;
@trigger-color:#f8f8f9;
@trigger-border-color:#dcdee2;;

@{name}{
    position: relative;
    width:100%;
    height:100%;

    &-horizontal{
        height: 100%;
        display: flex;
        flex-direction: row;
    }

    &-trigger-heng-con{
        width:6px;
        height:100%;
        background:@trigger-color;
        border:1px solid @trigger-border-color;
        border-top:none;
        border-bottom:none;
        cursor: col-resize;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;

        align-items: center;
        justify-content: center;

        &-wrapper{
            // left:1px;
            // top:50%;
            // height:32px;
            // transform:translateY(-50%);
            display: flex;
            flex-direction: column;
        }

        &-bar{
            width: 4px;
            height: 1px;
            margin-top:3px; 
            background-color:rgba(23,35,61,.25) ;      
        }
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • abandon, desert, forsake, leave, give up abandon :強(qiáng)調(diào)永遠(yuǎn)或完全...
    sunxiaohang閱讀 3,766評(píng)論 0 3
  • 專業(yè)考題類型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項(xiàng)A選項(xiàng)B選項(xiàng)C選項(xiàng)D選項(xiàng)E選項(xiàng)F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 10,558評(píng)論 0 13
  • 冬日清晨,今天是這座城市難得的好天氣,暖洋洋的日光灑在肩膀,驅(qū)散了寒日里的冰冷,愜意又舒服,可當(dāng)走進(jìn)這條逼仄的巷子...
    我嘉禾閱讀 2,100評(píng)論 0 1
  • 文/夏上水 那一年 我坐在左邊你坐在右邊 你喜歡扎馬尾穿藍(lán)裙子 我喜歡留長(zhǎng)發(fā)反穿校服 那一年 我不會(huì)英語(yǔ)你不會(huì)數(shù)學(xué)...
    夏上水閱讀 435評(píng)論 14 35
  • 下雨前,魚會(huì)游到水面上呼吸,。因?yàn)橄掠昵按髿鈿鈮汉艿?,水里缺少氧氣,靠近水面的地方氧氣多一些,于是它們就游到水面呼吸了?/div>
    12張宇鵬閱讀 680評(píng)論 0 0

友情鏈接更多精彩內(nèi)容