繁琐实用系列(一):vue实现github活跃图
字数统计:867 阅读时长 ≈ 3分钟under 默认分类 tag Published on November 3rd , 2021 at 04:35 pm
根据一些实用、不难但是繁琐的功能,推出一个这个系列,方便大家以后直接拿来就用。
缘由
最新有老师看见其他网站有仿github活跃图的效果,让我安排一下。
去github仔细看了看,以一个想法是看看echarts有没有类似的,找了一会发现有个类似的,但是修改成本较大,果断造轮子。
效果图
依赖
使用node.js的package管理器安装iview
npm install iview --save
在webpack项目的main.js中加入
import iView from 'iview'; //引入iview
import 'iview/dist/styles/iview.css'; //引入iview的css样式文件,后面如果需要自己配置也可以新建文件进行修改
Vue.use(iView); //use使用这个插件
思路
1.先排除坐标说明,绘制最里面的每个小格子。
第一个格子的时间:上一年今天如果不是周一,那么推到下一个周一。
最后一个格子的时间:今天。
格子的数量:最后一个格子时间 - 第一个格子时间 。
最后一个列的个数:今天的星期数 或 格子总数量除以7取余。
格子的颜色:格子是用for循环变量的数组数据,设置每个格子data时候,带上提交次数属性,然后写个根据数量获取不同颜色的方法。
鼠标移入的内容:组件采用的iview的tooltip。根据项目集成的ui库可更改,或者手写一个也不难。
2.绘制x,y轴坐标值。
y坐标代表星期数,比较简单,直接添加格子容器节点的上面就行。
x坐标代表月份, 并非每列都有月份,只会在当前月份的第一列显示月份。
月份第一个列判断依据:每个格子我都赋予了具体的日期,判断前一列是否有不相同月份的日期,如果有不同的日期设置一个变量为true,如果没有重新设置成false,然后根据这个变量来决定本列是不是月份第一列。
3.绘制legend。
位置使用flex布局就行,不做过多解释。
左侧的slider用的iview。
代码
<template>
<div class="submission-chart">
<div class="calendar">
<div class="weeks">
<div class="week">周二</div>
<div class="week">周四</div>
<div class="week">周六</div>
</div>
<div class="column" v-for="(columnData, columnIndex) in dateData" :key="columnIndex">
<div class="title">{{columnData.title}}</div>
<div
class="date-wrapper"
v-for="(dateData, dateIndex) in columnData.data"
:key="dateIndex"
:style="`background:${getColor(dateData.number)};`"
>
<Tooltip placement="top" :delay="300" :content="`${dateData.date}:${dateData.number}次通过`">
<div class="date"></div>
</Tooltip>
</div>
</div>
</div>
<div class="operation">
<div class="slider">
<div class="slider-desc">0</div>
<div style="width:120px;">
<Slider :value="sliderValue" :max="12" range :tip-format="sliderFormat" @on-change="sliderChange"></Slider>
</div>
<div class="slider-desc">12+</div>
</div>
<div class="legend">
<div class="level-desc">少</div>
<div class="level level-1"></div>
<div class="level level-2"></div>
<div class="level level-3"></div>
<div class="level level-4"></div>
<div class="level level-5"></div>
<div class="level-desc">多</div>
</div>
</div>
</div>
</template>
<script>
import moment from 'moment'
export default {
name: 'submission-chart',
data () {
return {
dateData: [],
submissionRecord: {},
sliderValue: [0, 12]
}
},
props: {
profile: {
default: {},
type: Object
}
},
mounted () {
this.formatProblemData()
this.init()
},
methods: {
init () {
// 上一年信息
let prevYear = moment().format('YYYY') - 1
let prevTodayFormatStr = prevYear + '-' + moment().format('MM-DD')
let prevToday = moment(prevTodayFormatStr).format('YYYY-MM-DD')
// 上年今日的是星期几
let prevTodayWeekNum = moment(prevToday).weekday() || 7
// 初始日期(上年临近的星期一)
let firstMondayDate = prevTodayWeekNum > 1 ? moment(prevToday).add(8 - prevTodayWeekNum, 'days').format('YYYY-MM-DD') : prevToday
// 初始日期至今日的天数,包括今日
let days = moment().diff(moment(firstMondayDate), 'days') + 1
// 每周天数
let columns = 7
// 最大列数(周数)
let lineNums = Math.ceil(days / columns)
// 绘制图表的源数据
let dateData = []
let monthCN = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
let startSliderNum = this.sliderValue[0]
let endSliderNum = this.sliderValue[1]
for (let i = 0; i < lineNums; i++) {
// 最近一星期不一定满的
let weekColumn = (i === lineNums - 1 ? days % columns ? days % columns : columns : columns)
// 开始计算title(月份的图例)
// 思路:第一列直接根据第一天的月份
// 之后的嘛列数根据上一周的最后一天减去第一天的月份,如果大于1代表月份发生了改变,下一列的title显示最新的月份
let theWeekStartMonth = moment(firstMondayDate).add(i * 7, 'days').format('M')
let theWeekEndMonth = moment(firstMondayDate).add(i * 7 + weekColumn, 'days').format('M')
let title = (i === 0) ? monthCN[theWeekStartMonth - 1] : ''
let ifSwitchMonth = false
if (theWeekEndMonth - theWeekStartMonth) {
ifSwitchMonth = true
}
if (i && dateData[i - 1].ifSwitchMonth) {
title = monthCN[theWeekEndMonth - 1]
}
// 图表源数据格式:columns:列数,title:列标题,ifSwitchMonth:月份是否发生改变,data:每格数据
dateData.push({
columns: weekColumn,
title: title,
ifSwitchMonth: ifSwitchMonth,
data: []
})
for (let j = 0; j < dateData[i].columns; j++) {
let date = moment(firstMondayDate).add(i * 7 + j, 'days').format('YYYY-MM-DD')
let number = 0
// 提交次数在slider范围内再进行次数赋值
if ((this.submissionRecord[date] >= startSliderNum && this.submissionRecord[date] < endSliderNum) ||
(this.submissionRecord[date] > 12 && endSliderNum === 12)) {
number = this.submissionRecord[date]
}
// number:提交次数,date:提交日期
dateData[i].data.push({
number: number,
date: date
})
}
}
this.dateData = dateData
},
formatProblemData () {
let submissionRecord = {}
// let OIProblems = this.profile.oi_problems_status.problems || {}
// // 格式化profile中oi的提交记录数据,创建submissionRecord对象,将create_time作为key进行存储
// Object.keys(OIProblems).forEach(problemID => {
// if (OIProblems[problemID]['status'] === 0) {
// let date = moment(OIProblems[problemID]['create_time']).format('YYYY-MM-DD')
// // 第一次出现提交次数设置1,之后每次出现提交次数+1
// submissionRecord[date.toString()] = submissionRecord[date] ? ((submissionRecord[date])) + 1 : 1
// }
// })
// 处理你的业务逻辑
// submissionRecord 最后的格式应为 {'2020-01-01':10, '2020-01-02': 11}
this.submissionRecord = submissionRecord
},
getColor (number) {
// level color
// 左闭右开
let color = '#EBEDF0'
if (number >= 12) {
color = '#196127'
} else if (number >= 8) {
color = '#239A3B'
} else if (number >= 4) {
color = '#7BC96F'
} else if (number >= 1) {
color = '#C6E48B'
} else {
color = '#EBEDF0'
}
return color
},
sliderFormat (val) {
return '提交次数: ' + val
},
sliderChange (val) {
// 没有使用v-model绑定sliderValue而是采用回调的原因
// 1.拖拽1px sliderValue都会引起组件重绘,此组件计算嵌套了2个for循环,导致页面出现卡顿slider不流畅的情况
this.sliderValue = val
this.init()
}
}
}
</script>
<style lang="less" scoped>
.submission-chart {
width: 820px;
height: 180px;
background-color: #fff;
margin: auto;
margin-top: 20px;
padding: 0px 0;
font-size: 12px;
.calendar {
margin-left: 16px;
margin-right: 16px;
display: flex;
.weeks {
width: 30px;
margin-right: 3px;
margin-top: 22px;
.week {
margin-top: 13px;
width: 30px;
height: 14px;
}
}
.column {
width: 11px;
margin-right: 3px;
.title {
width: 14px;
height: 14px;
margin-bottom: 8px;
text-align: left;
overflow: visible;
white-space: nowrap;
}
.date-wrapper {
width: 11px;
height: 11px;
background: #EBEDF0;
margin-bottom: 3px;
.date {
width: 11px;
height: 11px;
:hover {
width: 13px;
height: 13px;
}
}
}
}
}
.operation {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
.slider {
display: flex;
justify-content: center;
align-items: center;
width: 200px;
.slider-desc {
width: 11px;
margin: 0 8px;
}
}
.legend {
display: flex;
justify-content:center;
align-items: center;
.level-desc {
margin-right: 6px;
margin-left: 3px;
}
.level {
margin-right: 3px;
width: 11px;
height: 11px;
}
.level-1 {
background: #EBEDF0;
}
.level-2 {
background: #C6E48B;
}
.level-3 {
background: #7BC96F;
}
.level-4 {
background: #239A3B;
}
.level-5 {
background: #196127;
}
}
}
}
</style>
使用
<submission-chart :profile="profile"/>
本文由simyng创作,
采用知识共享署名4.0 国际许可协议进行许可,转载前请务必署名
文章最后更新时间为:November 3rd , 2021 at 08:35 am