一个以前的小项目要改,新增一个根据时间线显示不同月份绩效的需求。
没有设计稿,想了下怎么做出来好看。就有了以下这个想法
左侧显示时间线,滑动会显示不同的年份和月份,
右侧显示不同月份的统计数据。

为了显示这个想法不是很蠢,所以需要做的很 酷炫 ,
其实就是交互友好些,左右块都有相应的交互动作。
预想的效果
设想的是,点击左侧月份之后右侧会快速滚动到相应的月份,方便用户操作。
当然右侧下滑操作的时候左侧的时间线也会对应的滚动。
那么就要用到滚动侦测了,以前都是用的 UI 框架里边的,
所以这次也是直接去翻阅了这个项目所用到的 UI,MuseUI 的文档,
当然里边没有这个组件,不然也不会有这篇笔记了
去翻阅了一下 BootStrap
的 Scrollspy
源码,其实就是用到了内容元素的 offsetTop
和滚动条的监听
后端给的数据格式是类似这样的
1 2 3 4 5 6 7 8 9 10
| { date:'2020-01', detail:{ a:100, b:78, total:178, machines:967 }, note:'some text of 2020-01' },
|
所以可以直接使用 computed 返回所有的月份,对,我使用的 Vue 作为框架。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| computed: { timeline() { let list = []; const data = this.sourceData; if (!data) return list; data.forEach(item => { const date = item.date.split("-"); const year = list.find(item => item.year === date[0]); if (year) { year.months.push(date[1]); } else { list.push({ year: date[0], months: [date[1]] }); } }); return list; }, },
|
尝试过直接使用对象,用年份作为字段名,但是使用 v-for
循环的时候会按照升序打印出来,
折腾了有一会放弃了,还是使用数组,记不得前段时间自己是怎么直接用对象实现的时间分组的了 😂 -> Js 对象 调整属性排序是否有意义
其实差不多只是匹配的时候稍微麻烦点需要用到 find()
方法。
然后也根据后台返回的数据来计算右侧内容部分每一个月份的 offsetTop
1 2 3 4 5 6 7 8 9 10 11 12
| computed:{ offsetList() { const list = this.$refs["month-detail"]; let data = list.map(el => { return { date: el.getAttribute("date"), offset: el.offsetTop - 100 }; }); return data; } }
|
这块其实很简单,直接在循环输出的时候注册 ref
即可,然后直接遍历 DOM 元素数组保存 offsetTop
。(但是如果是动态改变的DOM就不能使用 computed
来计算了,具体查看 使用VueJS的计算属性监听DOM元素属性的问题)
然后是点击左侧时间轴右侧内容部分滚动到对应的月份,
1 2 3 4 5 6 7 8 9 10 11 12 13
| methods:{ toMonth(year, month) { this.currTime = `${year}-${month}`; const detailItem = this.offsetList.find( item => item.date === this.currTime ); this.$refs["wrap"].scrollTo({ top: detailItem.offset, behavior: "smooth" }); }, }
|
这里有一个 Js 的新东西 behavior: "smooth"
是原生滚动的一个新 API,应该是新 API 哈,以前都没有见到过,这次才发现有这个 Option,也可以考虑使用 CSS 来实现,但是听说兼容性堪忧。
最后加上右侧内容的滚动事件绑定 <div class="wrap" ref="wrap" @scroll="scrollSpyNav">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| watch: { currTime() { this.scrollTimeline(); } }, methods:{ scrollSpyNav(e) { clearTimeout(this.timer); const offsetTop = e.target.scrollTop; const curr = this.offsetList.find(item => item.offset >= offsetTop); this.timer = window.setTimeout(() => { this.currTime = curr.date; }, 300); }, scrollTimeline() { const el = this.$refs["month-block"].find( item => item.getAttribute("date") === this.currTime ); this.$refs["timeline"].scrollTo({ top: el.offsetTop - 50, behavior: "smooth" }); }, }
|
直接使用了 watch
来侦听的了日期的改变,来触发左侧时间线的滚动,也实现了点击时间轴自动置顶当前月份的效果。
顺带写了个计时器,防止抖动….
🌰 DEMO
兼容
- JavaScript - scrollTo [MDN]

- CSS - scroll-behavior:smooth [Can I use]
