<template>
<div>
<Card :type="3" :performanceData='performanceData' />
<div class="card-container">
<div class="card-title">周正确率走势</div>
<div>
<canvas v-on:mousemove="visible" id="chart" width="360px" height="250px" style="width: 360px;height: 250px;pointer-events: all" :style="seen?';cursor:pointer;':''"></canvas>
</div>
<div v-show="seen" class="hover-item">
<div class="time">{{performance.range}}</div>
<div class="rate">
周正确率
<span class="big-number">{{performance.rate}}%</span>
</div>
</div>
</div>
</div>
</template>
<script>
import Card from '@/components/stu/performance/card'
export default {
props: {
performanceData: {
type: Object,
required: true
}
},
name: 'RateCard',
components: {
Card
},
data () {
return {
seen: false,
performance: {
range: '',
rate: 0
},
points: [
]
}
},
mounted () {
},
methods: {
visible: function (e) {
let canvas = document.getElementById('chart')
var rect = canvas.getBoundingClientRect()
let xy = {
x: 2 * Math.round(e.clientX - rect.left),
y: 2 * Math.round(e.clientY - rect.top)
}
let points = this.points
let obj = {}
obj = points.find((item) => {
return xy.x >= item.xAxis - 15 && xy.x <= item.xAxis + 15 && xy.y >= item.yAxis - 25 && xy.y <= item.yAxis + 15
})
if (obj) {
let temp1 = new Date(obj.date)
temp1.setDate(temp1.getDate() - 7)
let temp2 = new Date(obj.date)
temp2.setDate(temp2.getDate() - 1)
this.performance.range =
temp1.getFullYear() +
'-' +
(parseInt(temp1.getMonth() + 1) < 10 ? ('0' + parseInt(temp1.getMonth() + 1)) : parseInt(temp1.getMonth() + 1)) +
'-' +
(temp1.getDate() < 10 ? ('0' + temp1.getDate()) : temp1.getDate()) +
'~' +
temp2.getFullYear() +
'-' +
(parseInt(temp2.getMonth() + 1) < 10 ? ('0' + parseInt(temp2.getMonth() + 1)) : parseInt(temp2.getMonth() + 1)) +
'-' +
(temp2.getDate() < 10 ? ('0' + temp2.getDate()) : temp2.getDate())
this.performance.rate = obj.rate
this.seen = true
} else {
this.seen = false
this.performance.range = ''
this.performance.rate = 0
}
},
invisible: function () {
this.seen = false
},
goChart (dataArr) {
// 声明所需变量
let canvas, ctx
// 获得canvas上下文
canvas = document.getElementById('chart')
if (canvas && canvas.getContext) {
ctx = canvas.getContext('2d')
}
this.initChart(canvas, ctx, dataArr) // 图表初始化
},
initChart (canvas, ctx, dataArr) {
// 图表信息
let cMargin = 20
let cSpace = 70
/* 这里是对高清屏幕的处理,
方法:先将canvas的width 和height设置成本来的两倍
然后将style.height 和 style.width设置成本来的宽高
这样相当于把两倍的东西缩放到原来的 1/2,这样在高清屏幕上 一个像素的位置就可以有两个像素的值
这样需要注意的是所有的宽高间距,文字大小等都得设置成原来的两倍才可以。
*/
canvas.width = 720
canvas.height = 400
canvas.style.height = canvas.height / 2 + 'px'
canvas.style.width = canvas.width / 2 + 'px'
let cHeight = canvas.height - cMargin - cSpace
let cWidth = canvas.width - cMargin - cSpace
let originX = cMargin + cSpace
let originY = cMargin + cHeight
// 折线图信息
let tobalDots = dataArr.length
let dotSpace = parseInt(cWidth / tobalDots)
let maxValue = 0
for (var i = 0; i < dataArr.length; i++) {
var dotVal = parseInt(dataArr[i][1])
if (dotVal > maxValue) {
maxValue = dotVal
}
}
maxValue = 100
let totalYNomber = 4
// 运动相关
let ctr = 1
let numctr = 100
let speed = 6
ctx.translate(0.5, 0.5) // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
this.drawLineLabelMarkers(
ctx,
cHeight,
cWidth,
originX,
originY,
cMargin,
cSpace,
maxValue,
totalYNomber,
tobalDots,
dataArr,
dotSpace,
canvas
) // 绘制图表轴、标签和标记
this.drawLineAnimate(
ctx,
maxValue,
tobalDots,
dataArr,
dotSpace,
canvas,
cHeight,
ctr,
numctr,
originY,
originX,
speed,
cWidth,
cMargin,
cSpace,
totalYNomber
) // 绘制折线图的动画
},
drawLineLabelMarkers (
ctx,
cHeight,
cWidth,
originX,
originY,
cMargin,
cSpace,
maxValue,
totalYNomber,
tobalDots,
dataArr,
dotSpace,
canvas
) {
ctx.font = '24px Arial'
ctx.lineWidth = 2
ctx.fillStyle = '#566a80'
ctx.strokeStyle = '#E3E3E3'
// y轴
// this.drawLine(ctx,originX, originY, originX, cMargin);
// x轴
this.drawLine(ctx, originX, originY, originX + cWidth, originY)
// 绘制标记
this.drawMarkers(
ctx,
cHeight,
cWidth,
originX,
originY,
cMargin,
cSpace,
maxValue,
totalYNomber,
tobalDots,
dataArr,
dotSpace,
canvas
)
},
drawLine (ctx, x, y, X, Y) {
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(X, Y)
ctx.stroke()
ctx.closePath()
},
drawMarkers (
ctx,
cHeight,
cWidth,
originX,
originY,
cMargin,
cSpace,
maxValue,
totalYNomber,
tobalDots,
dataArr,
dotSpace,
canvas
) {
ctx.strokeStyle = '#E0E0E0'
// 绘制 y 轴 及中间横线
let oneVal = parseInt(maxValue / totalYNomber)
ctx.textAlign = 'right'
ctx.font = '32px Arial'
ctx.fillStyle = '#333333'
for (let i = 0; i <= totalYNomber; i++) {
let markerVal = i * oneVal
let xMarker = originX - 5
let yMarker = parseInt(cHeight * (1 - markerVal / maxValue)) + cMargin
ctx.fillText(markerVal + '%', xMarker, yMarker + 3, cSpace) // 文字
if (i > 0) {
this.drawLine(ctx, originX + 2, yMarker, originX + cWidth, yMarker)
}
}
ctx.font = '24px Arial'
ctx.fillStyle = '#999999'
// 绘制 x 轴 及中间竖线
ctx.textAlign = 'center'
// 隐藏x轴坐标
// for (let i = 0; i < tobalDots; i++) {
// let markerVal = dataArr[i][0]
// let xMarker = originX + i * dotSpace
// let yMarker = originY + 30
// ctx.fillText(markerVal, xMarker, yMarker, cSpace) // 文字
// // if(i>0){
// // this.drawLine(ctx, xMarker, originY-2, xMarker, cMargin);
// // }
// }
// 绘制标题 y
ctx.save()
ctx.rotate(-Math.PI / 2)
// ctx.fillText("访问量", -canvas.height/2, cSpace-10);
ctx.restore()
// 绘制标题 x
// ctx.fillText("月份", originX+cWidth/2, originY+cSpace/2+20);
},
drawLineAnimate (
ctx,
maxValue,
tobalDots,
dataArr,
dotSpace,
canvas,
cHeight,
ctr,
numctr,
originY,
originX,
speed,
cWidth,
cMargin,
cSpace,
totalYNomber
) {
ctx.strokeStyle = '#3B80FC' // "#49FE79";
ctx.lineWidth = 4
// 连线
ctx.beginPath()
for (let i = 0; i < tobalDots; i++) {
let dotVal = dataArr[i][1]
let barH = parseInt((((cHeight * dotVal) / maxValue) * ctr) / numctr) //
let y = originY - barH
let x = originX + dotSpace * i
// 如果没有数据就不绘制连线
if (dotVal !== '') {
if (i === 0) {
ctx.moveTo(x, y)
} else {
ctx.lineTo(x, y)
}
}
}
ctx.stroke()
// 背景
ctx.lineTo(originX + dotSpace * (tobalDots - 1), originY)
ctx.lineTo(originX, originY)
// 背景渐变色
// 柱状图渐变色
// let gradient = ctx.createLinearGradient(0, 0, 0, 300);
// gradient.addColorStop(0, 'rgba(133,171,212,0.6)');
// gradient.addColorStop(1, 'rgba(133,171,212,0.1)');
// ctx.fillStyle = gradient;
// ctx.fill();
// ctx.closePath();
// ctx.fillStyle = "#566a80";
// 绘制点
for (let i = 0; i < tobalDots; i++) {
let dotVal = dataArr[i][1]
let barH = parseInt((((cHeight * dotVal) / maxValue) * ctr) / numctr)
let y = originY - barH
let x = originX + dotSpace * i
// 没有数据的话就不绘制
if (dotVal !== '') {
this.drawArc(ctx, x, y, dataArr[i][0], dotVal) // 绘制点
}
// ctx.fillText(parseInt(dotVal * ctr / numctr), x + 15, y - 8) // 交点位置文字
}
if (ctr < numctr) {
ctr++
let that = this
setTimeout(function () {
ctx.clearRect(0, 0, canvas.width, canvas.height)
that.drawLineLabelMarkers(
ctx,
cHeight,
cWidth,
originX,
originY,
cMargin,
cSpace,
maxValue,
totalYNomber,
tobalDots,
dataArr,
dotSpace,
canvas
) // 绘制图表轴、标签和标记
that.drawLineAnimate(
ctx,
maxValue,
tobalDots,
dataArr,
dotSpace,
canvas,
cHeight,
ctr,
numctr,
originY,
originX,
speed,
cWidth,
cMargin,
cSpace,
totalYNomber
) // 绘制折线图的动画
}, speed)
}
},
drawArc (ctx, x, y, X, Y) {
if (x && y) {
let points = this.points
let obj = {}
obj = points.find((item) => {
return item.xAxis === x && item.date === X
})
if (!obj) {
this.points.push({
xAxis: x,
yAxis: y,
date: X,
rate: Y
})
} else {
if (obj.yAxis > y) {
// 删掉大的加入小的
// 找到已经被选中列表中的这一项的位置
let position = points.indexOf(obj)
// 去掉这个
points.splice(position, 1)
points.push({
xAxis: x,
yAxis: y,
date: X,
rate: Y
})
this.points = points
}
}
}
ctx.beginPath()
ctx.arc(x, y, 7, 0, Math.PI * 2)
ctx.fillStyle = '#3B80FC'
ctx.fill()
ctx.closePath()
}
},
filters: {},
created () {},
watch: {
performanceData (n, o) {
let data = n['week'].reverse()
// this.performance = data[0][0] + '~' + data[6][0]
let chartData = [
['']
].concat(data)
this.goChart(chartData)
}
}
}
</script>
<style scoped>
.card-container {
width: 100%;
height: 368px;
background: rgba(255, 255, 255, 1);
margin-bottom: 24px;
padding-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.card-title {
font-size: 16px;
font-weight: 400;
color: rgba(51, 51, 51, 1);
line-height: 22px;
margin-bottom: 65px;
}
.hover-item {
pointer-events: none;
cursor: pointer;
width: 200px;
height: 60px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #999999;
border-radius: 3px;
position: relative;
left: 20%;
top: -40%;
font-size: 14px;
}
.time {
color: #FFFFFF;
width: 90%;
}
.rate {
color: #FFFFFF;
width: 90%;
}
.big-number {
color: #FFFFFF;
font-size: 16px
}
</style>