一个以前的小项目要改,新增一个根据时间线显示不同月份绩效的需求。
没有设计稿,想了下怎么做出来好看。就有了以下这个想法
左侧显示时间线,滑动会显示不同的年份和月份,
右侧显示不同月份的统计数据。
为了显示这个想法不是很蠢,所以需要做的很 酷炫 ,
其实就是交互友好些,左右块都有相应的交互动作。
预想的效果
设想的是,点击左侧月份之后右侧会快速滚动到相应的月份,方便用户操作。
当然右侧下滑操作的时候左侧的时间线也会对应的滚动。
那么就要用到滚动侦测了,以前都是用的 UI 框架里边的,
所以这次也是直接去翻阅了这个项目所用到的 UI,MuseUI 的文档,
当然里边没有这个组件,不然也不会有这篇笔记了
去翻阅了一下 BootStrap
的 Scrollspy
源码,其实就是用到了内容元素的 offsetTop
和滚动条的监听
后端给的数据格式是类似这样的
{
date:'2020-01',
detail:{
a:100,
b:78,
total:178,
machines:967
},
note:'some text of 2020-01'
},
所以可以直接使用 computed 返回所有的月份,对,我使用的 Vue 作为框架。
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
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元素属性的问题)
然后是点击左侧时间轴右侧内容部分滚动到对应的月份,
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">
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
来侦听的了日期的改变,来触发左侧时间线的滚动,也实现了点击时间轴自动置顶当前月份的效果。
顺带写了个计时器,防止抖动….