11.Vue技术栈开发实战-从SplitPane组件谈Vue中如何“操作”DOM

本节代码对照:

https://github.com/lison16/vue-cource/blob/master/src/components/split-pane/split-pane.vue

https://github.com/lison16/vue-cource/blob/master/src/views/split-pane.vue

 

vue中是数据驱动视图
 

创建组件的文件夹叫做split-pane

在路由列表里面注册一下

添加这个vue页面

创建组件的vue文件,设置name为SplitPane

创建index.js.把组件引进来,并导出去

引入这个组件,然后注册这个组件。

容器中分成两个区域,通过拖动来改变两个区域的比例。

还可以上下,嵌套使用。



分为三个部分,左边的区域,右边的区域,然后是中间可拖动的部分

我们首先来定义三个div。它的宽和高应该和父容器是一样的,所以这里我们设置它的高度和宽度是100%

宽度其实可以不设置,div是一个块级元素,它本身的宽度就是和它容器的宽度是一样的

less里面的写法,有层级管理的关系的class类型,可以嵌套着写。他们有一个共同的class是pane。左边是pane-left右边是pane-right

它们的高度也是和父容器一样高

&-left就是拼接了父级别的pane。所以这里就相当于是.pane-left

我们给左边加了一个背景色,可以看到它的颜色和父容器是一样宽的。

再给右侧的div设置一个背景色是橘黄色的,可以看到俩div成了上下布局了,因为他俩的宽度都是100%和父容器的宽度一样。所以右侧的就被挤下来了

想让这俩div在一排上显示。给.pane设置宽度为50%。值是宽度变窄了, 还没有在一排上显示。

可能有个默认的样式是居中的,

src根目录下的 App.vue 把这里注释掉。因为设置了居中显示,所以导致了上面的问题。


这里改成float:left。这样就上到一行里面显示了。

如果想设置左边是30%的宽度,右边是70%的宽度
把float:left和width都去掉。

给他俩一个position,都设置为绝对定位

设置绝对定位后,它会往上找,去他的父元素一级一级的找,它会根据有定位属性的父元素来定位。如果没有它就一直找到html标签。

所以这里地方我们要给wrapper一个相对定位的属性。这样又不会使wrapper的元素受到影响。 

让他俩的父元素的top位置都是0,左边设置宽度为30%。右边这个呢?也想让他用30%的这个值,top:0我们在.pane已经设置了,这里会继承过来,


这样设置右边就是70%的宽度,用到了同样的变量30%。这样就是两边加起来就是100%

把左边改成20%

右边的left也改成20%。这样他俩加起来就是100%

30%的这个值,想通过js变量去控制,这样这个值就不能写在css里面了。我们给它绑定一个变量,变量在data里面定义。

30%通过计算属性计算出来。

这样就把左边的30%的css属性注释掉。

右边是通过left的值,修改它的宽度。所以右边我们绑定的css属性是left




通过一个按钮改变这个变量的值来试一下

把这个按钮放在了左侧的面板里面


通过点击左侧按钮的面板,改变左侧pane的宽度

每次点击宽度变小。Vue里面,这样通过数据驱动样式的改变,这样避免了数据和视图不对应

定义trigger中间拖动条




它要显示在中间也是需要设置绝对定位。设置了top为0,它显示在了最左侧。

也是给中间的条绑定style属性。但是绑定上left后,视图中红色的调不见了。

中间的条,实际上在这里,说明可能是被元素遮住了。

通过设置一个z-index来看下

红色的条 实际上是盖在,右侧的

我们希望他在最中间显示。
首先中间条的宽度我们通过属性传递进来,默认是8.我们定义个props属性 triggerWidth

首先是向左偏移30%和left的宽度是一样的,然后它需要减少4个像素,来达到在最中间,这里我们用到css3的一个方法calc(),它允许你在里面做一些计算。

30%的偏移再减去4像素,就可以这么写

减去的是triggerWidth除以2


return `calc(${this.leftOffset * 100}% - ${this.triggerWidth / 2}px)`

 




这里绑定的是 triggerWidh 后面是px

 

<div class="pane pane-trigger-con" :style="{ left: triggerLeft, width: `${triggerWidth}px` }"></div>

 



中间只用到了width属性

说明我们这里的计算属性并没有生效

我们这里把计算出来的值打印出来看一下


这里的单词拼错, 应该是calc

红色的条被左侧遮住了一半

被右侧也遮住了一半

鼠标拖动中间trigger条

这就用到了鼠标的三个事件了
首先绑定一个mousemove事件

我们会用到参数里面的pageX就是鼠标距离页面左侧的像素,

只要能算出来鼠标距离左侧有多少像素,我们只要知道这个容器距离页面左侧多少,用鼠标的距离减去容器的距离页面的距离,就是左侧left的宽度了。

获取dom元素用ref


那么怎么获取父容器距离左边的距离呢?获取dom容器呢 我们用ref属性,给父容器div加一个ref属性为outer

getBoundingClientRect

理解:getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。

2.返回值类型:
 rectObject.top:元素上边到视窗上边的距离;

rectObject.right:元素右边到视窗左边的距离;

rectObject.bottom:元素下边到视窗上边的距离;

rectObject.left:元素左边到视窗左边的距离;

参考简书:https://www.jianshu.com/p/824eb6f9dda4
 
 


获取到dom后,它有个方法叫做getBoundingClientRect,这个方法返回一个对象,

这个方法返回一个对象,对象里面返回一个属性,

先来看下它返回的值

对象里面包含了这些属性,这些属性在IE9下也是支持的,所以兼容性是没有问题的。

所以这里我们减去left


鼠标的事件,有时候鼠标会超过这个框,应该绑定在页面级别。

所以我们把mouseMove事件绑定子在document上。就是鼠标在中间的条上,按下的时候绑定这个事件。
所以这里中间的条的事件,改成mousedown事件

鼠标按下我们给document绑定一个事件,它的回调事件是下面的handleMousemove事件


当点击这个条的时候,然后鼠标在页面上移动,console内不断的输出的

偏移量除以容器的总宽度,再乘以100,就是百分比。

outerRect.width是容器的宽度,也就是dom对象的总的宽度

乘以100 去掉

this.leftOffset = (offset / outerRect.width)

 


现在这个条就随着鼠标的移动而移动了

我们希望按下鼠标的时候 开始移动,松开鼠标的时候就应该停止了。
所以我们就需要有个状态,鼠标是按下还是抬起

只有为true的时候才能移动。

mouseup的事件

这样只有鼠标按下才能移动,鼠标抬起就不能移动了。

点击中间的条,靠左半边点,或者靠右半边的时候点击会出现这个偏移量的问题。
在鼠标按下的时候,有一个瞬间的偏移。出现问题的原因是我们在注册事件的时候忽略了 中间的条 也有个宽度。

所以在条上有个初始的偏移量,它的初始是0


我们就要给这个偏移量设置一个值,当我们按下的时候。我要把这个宽度也计算在内。

减去这个条距离页面左侧的距离。就是鼠标距离这个条的左侧距离。

因为这个事件是发生在这个条上的,

this.initOffset = event.pageX - event.srcElement.getBoundingClientRect().left

 



在move的时候,我们要减去这个值

还需要再加上条一半的宽度

这个时候再去拖动,就不会有一瞬间的移动了

设置最大值和最小值。

拖动最右边应该有个头,这里完全拖出去了。

设置最小的百分比值,是0.1

当你的拖动到百分之0.1的时候,左侧的值就不会再变了。



除法的逻辑加到上面去

如果偏移量小于这个最小值的话,那么就用这个最小值。


到了最小值 再拖,就拖不动了。

我们再来设置一个最大值,最大值设置为0.9

超过最大值,就是最大值。



拖动过程中,选中的问题

拖动过程中,偶尔会做了一些选中的操作


通过设置一个样式。这样他就不会在这个元素上选中操作了。

初始偏移量

设置初始的偏移

调用的时候就可以设置初始的偏移了 。

这里就注释掉,不用了。

这里就改成value的形式

我们是通过拖动来修改这个值的,那么有个问题就是。子组件如果想改变父组件的值,不能直接通过这种赋值的形式。还是要通过事件来触发 ,告诉父组件,父组件内做事件的响应,

再父组件内,把这个传入百分比值改成变量

然后给它绑定一个事件

回调函数里面有个参数,我们接收这个参数,并赋值给偏移量的变量值



在这里我们要抛出这个事件

拖动和修改都没有问题了。

 

<split-pane :value="offset" @input="handleInput"></split-pane>

 

 

另外一种方式vue提供的语法糖

v-model简写的形式



.sync修饰符


方法注释掉

子组件内,事件也不抛出去了。改用update:value



这么写首先给value绑定值offset,同时会给你绑定一个事件会更新这个offset的值,

子组件内要触发一个事件,固定的就叫做update。

update后面的:value就是我们这里父组件的属性名

最后 就是你要更新的值

在区域内填充内容

就用到了我们之前讲到的slot插槽的概念



右边的right给压到中间的条下面了。给右侧绑定一个triggerWidth除以2
加一个paddingLeft的属性 是中间调的宽度除以2


这样它的内容就不会被压到了。


同理left也有一个paddingRight属性

鼠标的样式

鼠标放上去,要让用户知道这是一个可以拖动的,我们就需要给它设置一个css的演示
鼠标的样式记不住呢,推荐大家一个网站
http://css-cursor.techstream.org/


鼠标放上去会显示对应的鼠标样式。


左右拖动的效果就是这个了



 

结束

posted @ 2020-05-24 12:47  高山-景行  阅读(1788)  评论(0编辑  收藏  举报