柱状图
滚动图表首先得有一些柱子呀,那我们就来早柱子,这些都是个宽度相等,高度按比例*设定好的最高高度
的柱子。通过flex布局就可以实现这些。
<div class="move__wrap">
<div class="move__item"
v-for="member in list"
:key="member.id"></div>
</div>
// style
<style lang='scss' scoped>
.move {
&__wrap {
display: flex;
align-items: flex-end;
height: 100%;
}
}
</style>
复制代码
给父级设置flex,设置交叉轴的对其方式为flex-end就可以把柱子都立起来了。但是可以看到日期和柱子是同时滚动的,它们应该同级的标签。
<div class="move__wrap">
<div class="move__item"
v-for="member in list"
:key="member.id">
// 柱子
<div class="child__chart"></div>
// 日期
<div></div>
</div>
</div>
// style
<style lang='scss' scoped>
.move {
&__wrap {
display: flex;
align-items: flex-end;
height: 100%;
}
&__item{
display: flex;
flex-wrap: wrap;
width: 38px;
}
}
</style>
复制代码
设置两个弹性盒子之后柱状图和日期都已经出来了。需要注意的一点是每个柱状图之间是有间隙的,但是日期是没有间隙的。
我们不能直接给move__item设置左外边距,将 child__chart设置宽度为37并且设置margin-left:1就有了如下图表
这里还需要定一个图标的最高的高度。比如想定最高的高度为180,我这里的做法是找出list中最大的一个值,其他项以它为标准做百分百比运算。
// @param {Array} list 数组项为数字即步数
<div class="move__item"
v-for="member in list"
:key="member.id">
<div class="child__chart"
:style="{height:member/maxStep*180+'px'}"></div>
</div>
// js
computed:{
// 找出最大值参考
maxStep() {
let max = 0
this.seriesDataList.forEach(number => {
max = number > max ? number : max
})
return max
},
}
复制代码
动起来
到此柱状图我们已经完成了,现在的目标就是让这个图表滑动起来。 这里我们需要了解一些css属性。
overflow-x : 当一个块级元素的内容在水平方向发生溢出时,应该截断溢出内容,或者显示滚动条,或者直接显示溢出内容。 visible | hidden | clip | scroll | auto 。
overflow-y : 同上。
-webkit-scrollbar : CSS伪类选择器影响了一个元素的滚动条的样式 可以设置css属性,这里使用none值隐藏滚动条
以上属性设置了是否滚动及滚动条的样式,我们还需要知道形成滚动的条件。
- 有父子两个元素
- 子元素的高/宽 > 父元素的高/宽
你也可以点击这里查看更多
光有滚动还不行,我们还需要监听滚动到哪里了获取到信息。用到了target.addEventListener(type, listener[, options])
EventTarget.addEventListener() 方法 : 将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
- type 表示监听事件类型的字符串这里我们用到的是scroll
- listener 需要执行的函数,考虑到之后要根据条件去removeEventListener移除事件监听,我们可以在data函数中定义一个有名的listener
- options 表示是在冒泡阶段或者捕获阶段会触发listener,默认为false即在冒泡阶段触发。
- eventTarget 事件目标可以是一个文档上的元素我们在
mounted钩子中
通过ref获取到我们目标值上,需要注意的滚动监听事件要绑定在滚动元素父级的DOM
data(){
return {
scrollListenHandle: () => {
console.log(this.$refs.scroll.scrollLeft)
this.scrollLength = this.$refs.scroll.scrollLeft
}
}
},
mounted() {
this.$refs.scroll.addEventListener('scroll', this.scrollListenHandle)
}
复制代码
现在我们已经设置好x轴方向的滚动效果和了滚动事件来看看效果吧
高亮
我们已经将图表滚起来了,我们需要将当前选中的日期高亮起来。这里涉及到一个参考,默认让图标可视区域的中心点的柱子高亮。
怎么拿到这个中心点呢?假设我们一页可以显示九根柱子,每根柱子的宽度是38px,当我们向右移动38px时,则第六根柱子会亮起来。计算公式可以是`(移动的距离+半个屏幕宽度)/38`就是当前移动时需要高亮的柱子的序号
data(){
// 滚动时不断更新scrollLength滚动距离
scrollListenHandle: () => {
this.scrollLength = this.$refs.scroll.scrollLeft
},
},
mounted() {
// 获取当前屏幕的宽度
this.screenWidth = screen.width
},
computed:{
// 获取到当前高亮的目标值的序号
selectIndex() {
return parseInt(
(this.scrollLength + this.screenWidth / 2) / singleWidth
)
},
}
复制代码
通过滚动事件将滚动的距离更新,通过计算属性得到当前选中柱子的序号,有了这个序号我们可以通过vue的动态绑定class设定高亮的样式。html代码如下:
<div ref="scroll"
class="chart__move"
@touchmove="onTouchMove">
<div ref="child"
class="move__child"
:style="{width:AllLength + 'px'}">
<div v-for="(item,index) in chartList"
:key="index"
class="child__wrap">
<div class="child__chart"
:class="{'child__chart--white':selectIndex === index}"
:style="{height:item/maxStep*180+'px'}"></div>
<div class="child__date"
:class="{'child__date--blue':selectIndex === index}">{{dateList[index]}}</div>
</div>
</div>
</div>
复制代码
按照我们的做法高亮的柱子
范围限定在 this.screenWidth / 2 ~ 总长度 - this.screenWidth / 2
在除了这个返回之外的柱子我们只能通过点击让它高亮,
毕竟已经到头了或者到尾了没办法移动了
。这里又涉及到一个问题,当我们点击柱子时,如果还在
范围限定在 this.screenWidth / 2 ~ 总长度 - this.screenWidth / 2
中的柱子,当我们点击它时,应该将它移动到中间的位置上来。
这里我们需要通过this.$refs.scroll.scrollLeft赋值来达到效果
。
页面一共九根柱子时,点击第6根柱子时,将序号传给 onSelectDateClick事件,我们要做的两件事,让第6根柱子亮起来。这很简单,使用:class动态绑定即可。还需要做的是将滚动元素的scrollLeft值增加38px(一个柱子的宽度)。这样做效果是达到了,但是我们需要点击的时候需要有一个过渡的效果,也就是慢慢移过去。可以设置一个过渡时间。好的下面就开始干吧!
// 定义数字的含义避免魔法数 而且方便统一修改
const singleWidth = 38 // 单个柱子的宽度
const duration = 100 // 过渡时间
export default {
methods:{
onSelectDateClick(index) {
// 获取到高亮的index
this.clickIndex = index
// 目标的scroolLeft
const targetLeft = index * singleWidth - this.screenWidth / 2
// 当前的scrollLeft
this.currentLeft = this.$refs.scroll.scrollLeft
// 每秒的速度
this.speed = (targetLeft - this.currentLeft) / duration
// 记下开始的时间
this.startTime = new Date()
// 开始过渡函数
this.update()
},
update() {
// 定时更新 this.$refs.scroll.scrollLeft
this.rAF = setInterval(() => {
const time = new Date() - this.startTime
this.$refs.scroll.scrollLeft = time * this.speed + this.currentLeft
// 如果时间超过我们预定的过渡时间就停止更新
if (time > duration) {
clearInterval(this.rAF)
}
})
},
}
}
复制代码
为了避免干扰html 动态绑定class设置为
:class="{'child__chart--white':clickIndex===index}"
复制代码
来看下效果
看起来不错,我们继续把selectIndex加上,现在类名高亮受两个index决定 :class="{'child__chart--white':selectIndex === index||clickIndex===index}"
复制代码
当我通过点击改变 scrollLeft的值时会也会触发滚动事件,当selectIndex和clickIndex不同时会出现两个高亮的柱子。我们需要一个值来区分现在是现在是滚动还是点击事件。
// 给滚动区域的父级加一个touchMove事件
onTouchMove() {
this.isCenter = true
this.clickIndex = -1
}
复制代码
通过touchMove事件通过isCenter来判断是否在滚动,如果在滚动将clickIndex置为-1,这样滚动的时候clickIndex为-1
// 判断isCenter是否在滚动否则返回-1
selectIndex() {
if (this.isCenter) {
return parseInt(
(this.scrollLength + this.screenWidth / 2) / singleWidth
)
} else {
return -1
}
},
复制代码
经过上面的判断处理,clickIndex和scrollIndex就不会重复,不会出现两条高亮线的问题。
移除滚动事件
当数据列表宽度小于一个屏幕时或卸载页面的时候,我们需要移除监听事件,优化性能。
watch:{
'chartList.length'(newLength) {
if(newLength*singleWidth < this.screenWidth){
this.$refs.scroll.removeEventListener('scroll', this.scrollListenHandle)
}
}
},
destroyed() {
this.$refs.scroll.removeEventListener('scroll', this.scrollListenHandle)
}
复制代码
小结
使用scroll-x:scroll实现滚动效果,通过this.$refs.scroll.removeEventListener('scroll',this.scrollListenHandle)来监听滚动的距离,通过计算得出高亮的柱子是哪条。通过点击事件改变this.$refs.scroll.scrollLeft的来实现动画的效果。