概述
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) ;
}
}
}