系列文章:
- view+element大表格解决方案(一)——概述
- view+element大形状解决方案(2)——形状分割
- 视图+元素大尺寸解决方案(3)-锚定组件(中)
前言
上一篇文章基本实现了锚点组件的功能,剩下一些优化和功能升级在本文中完成。一是优化样式,按照百度百科的样式效果制作;其次,使用组件时使用v-if="pageBlock"
的判断,必须隐藏细节;最后,如果锚点很多,锚点应该自动移动到可见范围。
准备
要实现本文的内容,首先在表单组件的公司信息后面添加一些章节,这样锚点的数量就足够了,代码如下:
<div data-section="公司信息"></div>
<form2 ref="form2" :data="formDataMap.form2" />
<!-- 增加占位章节 -->
<div data-section="占位信息1" data-ismain></div>
<div data-section="xxx1"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="xxx2"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="xxx3"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="占位信息2" data-ismain></div>
<div data-section="yyy1"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="yyy2"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="yyy3"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="占位信息3" data-ismain></div>
<div data-section="zzz1"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="zzz2"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
<div data-section="zzz3"></div>
<div style="width:300px;height:200px;background:#ccc;">占位符</div>
同时更改.form-wrapper
的样式,代码如下:
.form-wrapper {
position: relative;
width: 100%;
// 修改height为合适的演示高度
height: 500px;
// 增加背景色,主要是为了方便理解截图
background: #efefef;
padding: 16px;
overflow-y: auto;
::v-deep input {
width: 280px;
}
}
此时效果如下:
风格优化
目标图案左侧有节点导轨,导轨上下两端各有一个空心圆;每个主节点都有一个对应的实心圆;当前节点的导轨上有一个三角形指示器。
将 .anchor-track
添加到原始 .anchor
,并将锚点的直接父级更改为 .anchor-list
。修改后的模板代码如下:
<template>
<div class="anchor">
<div class="anchor-track"></div>
<div class="anchor-list">
<div v-for="node in sections" :key="node.label" :label="node.index"
:class="[node.ismain?'anchor-main-node':'anchor-sub-node',{'anchor-node-active':currentSection===node.label}]"
@click="handleClick(node.label)">
{{ node.label }}
</div>
</div>
</div>
</template>
对应的款式代码如下:
.anchor {
position: relative;
width: 100%;
height: 100%;
}
.anchor-track {
position: absolute;
left: 4px;
top: -10px;
bottom: -10px;
width: 1px;
background: #aaa;
// 上下的空心圆圈
&::before {
content: '';
position: absolute;
top: 0;
left: -4px;
width: 10px;
height: 10px;
border-radius: 10px;
border: 1px solid #ccc;
background: #fff;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: -4px;
width: 10px;
height: 10px;
border-radius: 10px;
border: 1px solid #ccc;
background: #fff;
}
}
.anchor-list {
position: relative;
padding: 12px;
width: 100%;
height: 100%;
// 宽高尽量依赖外界容器
// 如果容器未处理,则使用默认最小值
min-width: 120px;
min-height: 120px;
overflow-x: visible;
overflow-y: auto;
// 隐藏滚动条
&::-webkit-scrollbar {
display: none;
}
}
.anchor-main-node {
position: relative;
margin: 8px 0;
font-size: 14px;
font-weight: bold;
color: #555;
cursor: pointer;
&::before {
content: attr(label);
margin-left: 6px;
margin-right: 6px;
}
// 新增实心点
&::after {
content: '';
position: absolute;
left: -11px;
top: 3px;
width: 8px;
height: 8px;
border-radius: 8px;
background: #666;
}
}
.anchor-sub-node {
position: relative;
margin: 8px 0;
padding-left: 22px;
font-size: 14px;
color: #666;
cursor: pointer;
&::before {
content: attr(label);
margin-right: 4px;
}
}
.anchor-node-active {
color: #38f;
// 新增三角
&::after {
content: '';
position: absolute;
left: -8px;
top: 0px;
width: 0px;
height: 0px;
border: 8px solid transparent;
border-left-color: #38f;
background: transparent;
border-radius: 0;
}
}
此时效果如下:
style 部分没什么特别要说的,都是用伪元素以绝对定位的方式指向合适的位置。
删除 v-if
使用锚点组件时,为防止锚点mounted
获取形状的圆顶结构,添加了 v-if 语句。这个方案暂时可以用,但是做成正式组件就不适合了,所以想办法把这个V-if去掉。人们很自然地想到组件中的外观属性pageBlock
。如果oldValue是null
并且newValue有值,则该状态确实已安装。但这样做会破坏汇编的语义并增加代码的不可读性。我采用的方法是在anchor组件文件夹中添加一个组件包装层,并在包装层中实现v-if。代码如下:
<template>
<anchor v-if="pageBlock" :page-block="pageBlock" />
</template>
<script>
import Anchor from './anchor'
export default {
components: {
Anchor
},
props: {
pageBlock: HTMLElement
}
}
</script>
<style scoped lang="scss">
</style>
这时可以将form组件中的v-if删除,测试后效果正常。
高亮显示的锚点始终显示
锚点还有一项功能没有实现,就是当章节向左侧滚动时,标记的锚点可以自动显示在面板上。如下图:
如何让锚点自动显示?当然,我们需要监控当前的锚点。如果锚点发生变化,则计算当前锚点在锚点面板中的位置。如果是中下位置,就让它在中间;如果锚点位于上半部分,则立即将面板返回到顶部。
添加的js如下:
watch: {
currentSection() {
this.showCurrentSectionsAnchor()
}
},
// mehthods里增加showCurrentSectionsAnchor方法
showCurrentSectionsAnchor() {
// 给锚点增加data-anchor属性,便于查找
const anchor = this.$refs['anchor'].querySelector(`[data-anchor=${this.currentSection}]`)
if (anchor) {
const wrapper = anchor.parentElement
const clientHeight = wrapper.clientHeight
const offsetTop = anchor.offsetTop
// 计算当前元素是否处于容器可视区域中间偏下的位置,如果是的,则让容器滚动使得元素可视居中
if (offsetTop > clientHeight / 2) {
wrapper.scrollTop = offsetTop - clientHeight / 2
} else {
wrapper.scrollTop = 0
}
}
}
要根据锚文本查找锚点,请将ref="anchor"
添加到组件根元素.anchor
。当 v-for 生成.anchor-list
中的锚点时,每个节点都绑定到:data-anchor="node.label"
。
测试效果,发现随着表单的滚动,锚点始终显示高亮的节点。但这时候有一个问题。单击锚点时,左侧形状不会滚动。这是怎么回事?我的猜测是,新的 showCurrentSectionsAnchor
方法更改了锚点容器的 scrollTop
,该容器基本上是一个滚动。此卷轴与handleClick
中的section.scrollIntoView
相冲突。在本例中,我向 scrollIntoView
添加了异步执行以避免同时滚动。修改后的handleClick
代码如下:
handleClick(label) {
// 设置当前锚点对应章节
this.currentSection = label
// 查找到到该章节的dom
const section = this.pageBlock.querySelector(`[data-section=${label}]`)
// 延迟执行
setTimeout(() => {
// 平滑滚动至该章节
section.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
})
}
此时完全正常。好了,关于锚点的时间已经足够了,下一篇文章将回到形状的主题。 感谢阅读,欢迎指正!
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。