电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

vue使用Split封裝通用拖拽滑動分隔面板組件

瀏覽:3日期:2022-10-01 15:36:26
前言

手動封裝一個類似Iview中的Split組件,可將一片區域,分割為可以拖拽調整寬度或高度的兩部分區域,最終效果如下:

vue使用Split封裝通用拖拽滑動分隔面板組件

vue使用Split封裝通用拖拽滑動分隔面板組件

開始基礎布局

在vue工程中創建SplitPane組件,引入頁面使用。

vue使用Split封裝通用拖拽滑動分隔面板組件

<template> <div class='page'> <SplitPane /> </div></template><script>import SplitPane from ’./components/split-pane’export default { components: { SplitPane }, data() { return {} }}</script><style scoped lang='scss'>.page { height: 100%; padding: 10px; background: #000;}</style>

// split-pane.vue<template> <div class='split-pane'> split </div></template><script>export default { data() { return {} }}</script><style scoped lang='scss'>.split-pane { background: palegreen; height: 100%;}</style>

vue使用Split封裝通用拖拽滑動分隔面板組件

SplitPane組件由三部分組成:區域1,區域2,以及滑動器。添加這三個元素,并分別添加class名,注意.pane為區域1和區域2共用。

<template><div class='split-pane'> <div class='pane pane-one'></div> <div class='pane-trigger'></div> <div class='pane pane-two'></div> </div></template>

將容器設置為flex布局,區域2的flex屬性設為1,則區域2會根據區域1的寬度變化自適應。

<style scoped lang='scss'>.split-pane { background: palegreen; height: 100%; display: flex; .pane-one { width: 50%; background: palevioletred; } .pane-trigger { width: 10px; height: 100%; background: palegoldenrod; } .pane-two { flex: 1; background: turquoise; }}</style>

vue使用Split封裝通用拖拽滑動分隔面板組件

可以看到設置區域1的寬度變化就是實現該組件的核心點。

除了橫向還要支持縱向布局,所以給組件添加一個direction屬性,該屬性由外部傳入,值為row 或 column,與父元素的flex-direction屬性綁定。

<template> <div :style='{ flexDirection: direction }'> <div class='pane pane-one'></div> <div class='pane-trigger'></div> <div class='pane pane-two'></div> </div></template><script>export default { props: { direction: { type: String, default: ’row’ } }, data() { return {} }}</script>

在橫向布局中,區域1設置width:50%,滑動器設置width:10px,而變為縱向布局后這兩個width應該變為height。所以刪除style中這兩個width設置,添加一個lengthType計算屬性,根據不同的direction在行內樣式中給這兩個元素分別設置寬高。

<template> <div :style='{ flexDirection: direction }'> <div :style='lengthType + ’:50%’'></div> <div :style='lengthType + ’:10px’'></div> <div class='pane pane-two'></div> </div></template>computed: { lengthType() { return this.direction === ’row’ ? ’width’ : ’height’ } }

同時在橫向布局中,區域1,區域2,滑動器的height都為100%,在縱向布局下都應該改為width: 100%。所以刪除原本的height設置,將direction綁定為容器的一個class,根據該class設置三個子元素兩種情況下100%的屬性。

<template> <div : :style='{ flexDirection: direction }'> <div :style='lengthType + ’:50%’'></div> <div :style='lengthType + ’:10px’'></div> <div class='pane pane-two'></div> </div></template><script>export default { props: { direction: { type: String, default: ’row’ } }, data() { return {} }, computed: { lengthType() { return this.direction === ’row’ ? ’width’ : ’height’ } }}</script><style scoped lang='scss'>.split-pane { background: palegreen; height: 100%; display: flex; &.row { .pane { height: 100%; } .pane-trigger { height: 100%; } } &.column { .pane { width: 100%; } .pane-trigger { width: 100%; } } .pane-one { background: palevioletred; } .pane-trigger { background: palegoldenrod; } .pane-two { flex: 1; background: turquoise; }}</style>

此時如果在頁面中給組件傳入direction='column',可以看到已經變為縱向

<template> <div class='page'> <SplitPane direction='column' /> </div></template>數據綁定

當前區域1的寬(高)度和滑動器的寬(高)度都是在樣式中寫死的,需要變為在js中綁定才能進行操作,首先將能用于計算的數字放在data中

data() { return { paneLengthPercent: 50, // 區域1寬度 (%) triggerLength: 10 // 滑動器寬度 (px) }}

然后通過computed返回兩個樣式中需要的字符串,同時為了保證滑動器在區域1和區域2的正中間,區域1的寬度應該減去滑動器寬度的一半。

computed: { lengthType() { return this.direction === ’row’ ? ’width’ : ’height’ }, paneLengthValue() { return `calc(${this.paneLengthPercent}% - ${this.triggerLength / 2 + ’px’})` }, triggerLengthValue() { return this.triggerLength + ’px’ } }

最后綁定在模板中

<template> <div : :style='{ flexDirection: direction }'> <div :style='lengthType + ’:’ + paneLengthValue'></div> <div :style='lengthType + ’:’ + triggerLengthValue'></div> <div class='pane pane-two'></div> </div></template>

vue使用Split封裝通用拖拽滑動分隔面板組件

事件綁定

想象一下拖拽滑動器的過程,第一步是在滑動器上按下鼠標,給滑動器添加mousedown事件

<div : @mousedown='handleMouseDown'></div>

按下鼠標后開始滑動,應該監聽mousemove事件,但注意不是在滑動器上,而是在整個文檔上監聽,因為鼠標有可能滑動到頁面任何位置。當用戶松開鼠標時,應該取消對整個文檔mousemove的監聽,所以在鼠標按下的那一刻,應該對document添加兩個事件:鼠標移動和鼠標松開

methods: { // 按下滑動器 handleMouseDown(e) { document.addEventListener(’mousemove’, this.handleMouseMove) document.addEventListener(’mouseup’, this.handleMouseUp) }, // 按下滑動器后移動鼠標 handleMouseMove(e) { console.log(’拖動中’) }, // 松開滑動器 handleMouseUp() { document.removeEventListener(’mousemove’, this.handleMouseMove) } }

vue使用Split封裝通用拖拽滑動分隔面板組件

我們實際要控制的是區域1的寬度,讓區域1的寬度等于當前鼠標距容器左邊的距離,也就是如果鼠標移動到下圖的圓圈位置,讓區域1的寬度等于中間的長度:

vue使用Split封裝通用拖拽滑動分隔面板組件

這個長度可以根據當前鼠標距頁面最左邊的距離減去容器距頁面最左邊的距離算出,也就是綠色長度等于紅色減藍色:

vue使用Split封裝通用拖拽滑動分隔面板組件

給容器添加ref為了獲取容器的dom信息

...<div ref='splitPane' : :style='{ flexDirection: direction }'>...

如果打印ref的getBoundingClientRect()可以看到如下信息:

console.log(this.$refs.splitPane.getBoundingClientRect())

vue使用Split封裝通用拖拽滑動分隔面板組件

其中left代表容器距離頁面左側的距離,width代表容器的寬度。通過鼠標事件對象event的pageX可以獲得當前鼠標距頁面左側的距離,則我們要求的鼠標距容器左側距離就可以算出來了。最后用這個距離除以容器寬度乘上100,就得到了這個距離的百分比數值,賦值給paneLengthPercent。

// 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() const offset = e.pageX - clientRect.left const paneLengthPercent = (offset / clientRect.width) * 100 this.paneLengthPercent = paneLengthPercent },

vue使用Split封裝通用拖拽滑動分隔面板組件

兼容縱向布局。

// 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() let paneLengthPercent = 0 if (this.direction === ’row’) { const offset = e.pageX - clientRect.left paneLengthPercent = (offset / clientRect.width) * 100 } else { const offset = e.pageY - clientRect.top paneLengthPercent = (offset / clientRect.height) * 100 } this.paneLengthPercent = paneLengthPercent },優化

此時看上去需求已經完成,但作為一個通用組件還有幾個要優化的地方。

優化一 抖動問題

把滑動器寬度設置大一些后可以發現一個抖動問題如下:

vue使用Split封裝通用拖拽滑動分隔面板組件

在滑動器兩側按下后輕輕移動就會出現大幅偏移,因為現在的計算邏輯始終認為鼠標在滑動器的正中間,沒有把滑動器寬度考慮進去。

在dota中定義一個當前鼠標距滑動器左(頂)側偏移量

data() { return { paneLengthPercent: 50, // 區域1寬度 (%) triggerLength: 100, // 滑動器寬度 (px) triggerLeftOffset: 0 // 鼠標距滑動器左(頂)側偏移量 } }

這個值等于鼠標距頁面左側距離減去滑動器距頁面左側距離(通過e.srcElement.getBoundingClientRect()),在每次滑動器被按下時進行賦值,也要區分橫向/縱向布局:紅 - 藍 = 綠

vue使用Split封裝通用拖拽滑動分隔面板組件

// 按下滑動器 handleMouseDown(e) { document.addEventListener(’mousemove’, this.handleMouseMove) document.addEventListener(’mouseup’, this.handleMouseUp) if (this.direction === ’row’) { this.triggerLeftOffset = e.pageX - e.srcElement.getBoundingClientRect().left } else { this.triggerLeftOffset = e.pageY - e.srcElement.getBoundingClientRect().top } },

有了這個triggerLeftOffset,設置區域1的寬度時就應該變成:鼠標距容器左側距離 減去 鼠標距滑動器左側的距離(triggerLeftOffset) 再加上滑動器寬度的一半。這樣就相當于把鼠標又定位回了滑動器正中間。

// 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() let paneLengthPercent = 0 if (this.direction === ’row’) { const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.width) * 100 } else { const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.height) * 100 } this.paneLengthPercent = paneLengthPercent },

此時不再有抖動問題

vue使用Split封裝通用拖拽滑動分隔面板組件

優化二 鼠標樣式

鼠標在滑動器上經過時應該改變樣式告訴用戶可以拖動,分別在橫向布局與縱向布局的滑動器css中添加鼠標樣式變化。

<style scoped lang='scss'>.split-pane { background: palegreen; height: 100%; display: flex; &.row { .pane { height: 100%; } .pane-trigger { height: 100%; cursor: col-resize; // 這里 } } &.column { .pane { width: 100%; } .pane-trigger { width: 100%; cursor: row-resize; // 這里 } } .pane-one { background: palevioletred; } .pane-trigger { background: palegoldenrod; } .pane-two { flex: 1; background: turquoise; }}</style>

vue使用Split封裝通用拖拽滑動分隔面板組件

優化三 滑動限制

作為一個通用組件,應該向外部提供設置滑動最小與最大距離的限制功能,接收min與max兩個props。

props: { direction: { type: String, default: ’row’ }, min: { type: Number, default: 10 }, max: { type: Number, default: 90 } },

在handleMouseMove加入判斷:

// 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() let paneLengthPercent = 0 if (this.direction === ’row’) { const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.width) * 100 } else { const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.height) * 100 } if (paneLengthPercent < this.min) { paneLengthPercent = this.min } if (paneLengthPercent > this.max) { paneLengthPercent = this.max } this.paneLengthPercent = paneLengthPercent }

vue使用Split封裝通用拖拽滑動分隔面板組件

優化四 面板默認寬度和滑動器寬度

還是作為一個通用組件,面板初始化比例與滑動器寬度應該也由外部使用者決定。將data中的paneLengthPercent 和 triggerLength轉移到props中,從外部接收。

props: { direction: { type: String, default: ’row’ }, min: { type: Number, default: 10 }, max: { type: Number, default: 90 }, paneLengthPercent: { type: Number, default: 50 }, triggerLength: { type: Number, default: 10 } }, data() { return { triggerLeftOffset: 0 // 鼠標距滑動器左(頂)側偏移量 } },

在頁面中則需傳入paneLengthPercent,注意paneLengthPercent必須是一個定義在data中的數據,并且要加上.sync修飾符,因為這個值要動態修改。

// page.vue<template> <div class='page'> <SplitPane direction='row' :paneLengthPercent.sync='paneLengthPercent' /> </div></template>... data() { return { paneLengthPercent: 30 } }...

然后在組件中handleMouseMove中通過this.$emit觸發事件的方式修改paneLengthPercent值。

// 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() let paneLengthPercent = 0 if (this.direction === ’row’) { const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.width) * 100 } else { const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.height) * 100 } if (paneLengthPercent < this.min) { paneLengthPercent = this.min } if (paneLengthPercent > this.max) { paneLengthPercent = this.max } this.$emit(’update:paneLengthPercent’, paneLengthPercent) // 這里 },

此時組件的要素信息都可以通過外部的props控制了。

優化五 插槽

作為一個容器組件不能添加內容不是等于白費,分別給兩個區域添加兩個具名插槽。

<template> <div ref='splitPane' : :style='{ flexDirection: direction }'> <div :style='lengthType + ’:’ + paneLengthValue'> <slot name='one'></slot> </div> <div : @mousedown='handleMouseDown'> </div> <div class='pane pane-two'> <slot name='two'></slot> </div> </div></template>優化六 禁止選中

在拖動過程中,如果區域中有文字內容可能會出現選中文字的情況,給滑動器添加禁止選中效果。

... .pane-trigger { user-select: none; background: palegoldenrod; }...結束組件完整代碼

保留各背景色僅為了文章展示需要,實際使用中刪除

<template> <div ref='splitPane' : :style='{ flexDirection: direction }'> <div :style='lengthType + ’:’ + paneLengthValue'> <slot name='one'></slot> </div> <div : @mousedown='handleMouseDown' ></div> <div class='pane pane-two'> <slot name='two'></slot> </div> </div></template><script>export default { props: { direction: { type: String, default: ’row’ }, min: { type: Number, default: 10 }, max: { type: Number, default: 90 }, paneLengthPercent: { type: Number, default: 50 }, triggerLength: { type: Number, default: 10 } }, data() { return { triggerLeftOffset: 0 // 鼠標距滑動器左(頂)側偏移量 } }, computed: { lengthType() { return this.direction === ’row’ ? ’width’ : ’height’ }, paneLengthValue() { return `calc(${this.paneLengthPercent}% - ${this.triggerLength / 2 + ’px’})` }, triggerLengthValue() { return this.triggerLength + ’px’ } }, methods: { // 按下滑動器 handleMouseDown(e) { document.addEventListener(’mousemove’, this.handleMouseMove) document.addEventListener(’mouseup’, this.handleMouseUp) if (this.direction === ’row’) { this.triggerLeftOffset = e.pageX - e.srcElement.getBoundingClientRect().left } else { this.triggerLeftOffset = e.pageY - e.srcElement.getBoundingClientRect().top } }, // 按下滑動器后移動鼠標 handleMouseMove(e) { const clientRect = this.$refs.splitPane.getBoundingClientRect() let paneLengthPercent = 0 if (this.direction === ’row’) { const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.width) * 100 } else { const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2 paneLengthPercent = (offset / clientRect.height) * 100 } if (paneLengthPercent < this.min) { paneLengthPercent = this.min } if (paneLengthPercent > this.max) { paneLengthPercent = this.max } this.$emit(’update:paneLengthPercent’, paneLengthPercent) }, // 松開滑動器 handleMouseUp() { document.removeEventListener(’mousemove’, this.handleMouseMove) } }}</script><style scoped lang='scss'>.split-pane { background: palegreen; height: 100%; display: flex; &.row { .pane { height: 100%; } .pane-trigger { height: 100%; cursor: col-resize; } } &.column { .pane { width: 100%; } .pane-trigger { width: 100%; cursor: row-resize; } } .pane-one { background: palevioletred; } .pane-trigger { user-select: none; background: palegoldenrod; } .pane-two { flex: 1; background: turquoise; }}</style>組件使用示例

保留各背景色僅為了文章展示需要,實際使用中刪除

<template> <div class='page'> <SplitPane direction='column' :min='20' :max='80' :triggerLength='20' :paneLengthPercent.sync='paneLengthPercent' > <template v-slot:one> <div> 區域一 </div> </template> <template v-slot:two> <div> 區域二 </div> </template> </SplitPane> </div></template><script>import SplitPane from ’./components/split-pane’export default { components: { SplitPane }, data() { return { paneLengthPercent: 30 } }}</script><style scoped lang='scss'>.page { height: 100%; padding: 10px; background: #000;}</style>

vue使用Split封裝通用拖拽滑動分隔面板組件

到此這篇關于vue使用Split封裝通用拖拽滑動分隔面板組件 的文章就介紹到這了,更多相關vue 拖拽滑動分隔面板 內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Vue
相關文章:
主站蜘蛛池模板: 天一线缆邯郸有限公司_煤矿用电缆厂家_矿用光缆厂家_矿用控制电缆_矿用通信电缆-天一线缆邯郸有限公司 | 动库网动库商城-体育用品专卖店:羽毛球,乒乓球拍,网球,户外装备,运动鞋,运动包,运动服饰专卖店-正品运动品网上商城动库商城网 - 动库商城 | Trimos测长机_测高仪_TESA_mahr,WYLER水平仪,PWB对刀仪-德瑞华测量技术(苏州)有限公司 | 视频教程导航网_视频教程之家_视频教程大全_最新视频教程分享发布平台 | 杜康白酒加盟_杜康酒代理_杜康酒招商加盟官网_杜康酒厂加盟总代理—杜康酒神全国运营中心 | 天津散热器_天津暖气片_天津安尼威尔散热器制造有限公司 | 礼至家居-全屋定制家具_一站式全屋整装_免费量房设计报价 | 外贮压-柜式-悬挂式-七氟丙烷-灭火器-灭火系统-药剂-价格-厂家-IG541-混合气体-贮压-非贮压-超细干粉-自动-灭火装置-气体灭火设备-探火管灭火厂家-东莞汇建消防科技有限公司 | 锡膏喷印机-全自动涂覆机厂家-全自动点胶机-视觉点胶机-深圳市博明智控科技有限公司 | 安徽控制器-合肥船用空调控制器-合肥家电控制器-合肥迅驰电子厂 安徽净化板_合肥岩棉板厂家_玻镁板厂家_安徽科艺美洁净科技有限公司 | 橡胶粉碎机_橡胶磨粉机_轮胎粉碎机_轮胎磨粉机-河南鼎聚重工机械制造有限公司 | 塑料撕碎机_编织袋撕碎机_废纸撕碎机_生活垃圾撕碎机_废铁破碎机_河南鑫世昌机械制造有限公司 | 激光内雕_led玻璃_发光玻璃_内雕玻璃_导光玻璃-石家庄明晨三维科技有限公司 激光内雕-内雕玻璃-发光玻璃 | 隆众资讯-首页_大宗商品资讯_价格走势_市场行情 | 欧必特空气能-商用空气能热水工程,空气能热水器,超低温空气源热泵生产厂家-湖南欧必特空气能公司 | 拉卡拉POS机官网 - 官方直营POS机办理|在线免费领取 | 防火门-专业生产甲级不锈钢钢质防火门厂家资质齐全-广东恒磊安防设备有限公司 | 定量包装机,颗粒定量包装机,粉剂定量包装机,背封颗粒包装机,定量灌装机-上海铸衡电子科技有限公司 | PVC地板|PVC塑胶地板|PVC地板厂家|地板胶|防静电地板-无锡腾方装饰材料有限公司-咨询热线:4008-798-128 | 创富网-B2B网站|供求信息网|b2b平台|专业电子商务网站 | 青岛侦探_青岛侦探事务所_青岛劝退小三_青岛调查出轨取证公司_青岛婚外情取证-青岛探真调查事务所 | 咖啡加盟,咖啡店加盟连锁品牌-卡小逗 | 广州活动策划公司-15+年专业大型公关活动策划执行管理经验-睿阳广告 | 手持式线材张力计-套帽式风量罩-深圳市欧亚精密仪器有限公司 | 5L旋转蒸发器-20L-50L旋转蒸发器-上海越众仪器设备有限公司 | 厂房出租-厂房规划-食品技术-厂房设计-厂房装修-建筑施工-设备供应-设备求购-龙爪豆食品行业平台 | 北京康百特科技有限公司-分子蒸馏-短程分子蒸馏设备-实验室分子蒸馏设备 | 反渗透水处理设备|工业零排放|水厂设备|软化水设备|海南净水设备--海南水处理设备厂家 | 杭州公司变更法人-代理记账收费价格-公司注销代办_杭州福道财务管理咨询有限公司 | 雷蒙磨,雷蒙磨粉机,雷蒙磨机 - 巩义市大峪沟高峰机械厂 | 春腾云财 - 为企业提供专业财税咨询、代理记账服务 | 提升海外网站流量,增加国外网站访客UV,定制海外IP-访客王 | 市政路灯_厂家-淄博信达电力科技有限公司 | 山东螺杆空压机,烟台空压机,烟台开山空压机-烟台开山机电设备有限公司 | 无菌实验室规划装修设计-一体化实验室承包-北京洁净净化工程建设施工-北京航天科恩实验室装备工程技术有限公司 | 代办建筑资质升级-建筑资质延期就找上海国信启航 | 定制异形重型钢格栅板/钢格板_定做踏步板/排水沟盖板_钢格栅板批发厂家-河北圣墨金属制品有限公司 | 吉祥新世纪铝塑板_生产铝塑板厂家_铝塑板生产厂家_临沂市兴达铝塑装饰材料有限公司 | 全自动包装机_灌装机生产厂家-迈驰包装设备有限公司 | 涂层测厚仪_光泽度仪_uv能量计_紫外辐照计_太阳膜测试仪_透光率仪-林上科技 | 北京银联移动POS机办理_收银POS机_智能pos机_刷卡机_收银系统_个人POS机-谷骐科技【官网】 |