要求x轴根据数据长度自适应
y轴根据数据最大值取长度值
<template><div ref="cvsContainer" class="cvs-container"><canvas ref="cvs" class="canvas"></canvas></div>
</template><script setup>
import {computed, defineProps, onMounted, ref} from "vue";onMounted(() => {initLine()
})const data = defineProps({list: {type: Array,default: () => [1,2,3,4,5,6,7,8,9,10]}
});const max = computed(() => {return Math.max(...data.list)
})
console.log(max.value)
const dataSize = computed(() => {return data.list.length
})
console.log(dataSize.value)const cvs = ref(null);
const cvsContainer = ref(null);
const initLine = () => {const container = cvsContainer.valueconst width = container.offsetWidthconst height = container.offsetHeightcvs.value.width = widthcvs.value.height = heightconst ctx = cvs.value.getContext('2d')ctx.beginPath()ctx.moveTo(50, 50)ctx.lineTo(50, 400)ctx.lineTo(600, 400)ctx.stroke()// 画x刻度// x轴总长550,我们用数据总长度计算出每个刻度要画多长,这里刻度数量=数据长度,例如10cm想要分成2个刻度,那就是开头和结束两个刻度,就等于10个要分成一段也就是10/(2-1)const xScale = 550 / (dataSize.value-1);for (let i = 1; i <= dataSize.value; i++) {ctx.beginPath()ctx.strokeStyle = 'blue'ctx.lineWidth = 1ctx.moveTo(50 + i * xScale, 400)ctx.lineTo(50 + i * xScale, 390)ctx.stroke()ctx.fillText(i, 47 + (i-1) * xScale, 415,)}// 画y刻度// (我们只显示7个y轴刻度)我们计算出最大值分成7份每份有多长const yScale = (max.value / 7).toFixed(0);console.log('yScale', yScale)for (let i = 0; i <= 7; i++) {ctx.beginPath()ctx.strokeStyle = 'blue'ctx.lineWidth = 1ctx.moveTo(50, 400 - i * 50)ctx.lineTo(60, 400 - i * 50)ctx.stroke()ctx.fillText(i * yScale, 50 - max.value.toString().length * 8, 403 - i * 50,)}// 画折线for (let i = 0; i < data.list.length; i++) {setTimeout(() => {ctx.beginPath()ctx.strokeStyle = 'red'ctx.lineWidth = 1ctx.moveTo(50 + i * xScale, 400 - data.list[i] * 350 / max.value)ctx.lineTo(50 + (i + 1) * xScale, 400 - data.list[i + 1] * 350 / max.value)ctx.stroke()ctx.fillText(data.list[i], 45 + i * xScale, 400 - data.list[i] * 350 / max.value,)}, 500 / data.list.length * i)}
}
</script><style lang="scss" scoped>
.cvs-container {width: 1200px;height: 800px;background-color: white;border-radius: 15px;.canvas {}
}</style>
增加了鼠标移动的数值提示框
<template><div ref="cvsContainer" class="cvs-container"><canvas ref="cvs" class="canvas"></canvas><div v-show="pageData.pointerShow" class="pointer" :style="`left:${pageData.pointerX}px;top: ${pageData.pointerY}px;`">{{pageData.pointer}}</div></div>
</template><script setup>
import {computed, defineProps, onMounted, reactive, ref} from "vue";onMounted(() => {initLine()
})const data = defineProps({list: {type: Array,default: () => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]},xAxios: {type: Array,default: () => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
});const pageData = reactive({pointer:0,pointerX:0,pointerY:0,pointerShow:false
});const max = computed(() => {return Math.max(...data.list)
})
console.log(max.value)const cvs = ref(null);
const cvsContainer = ref(null);
const initLine = () => {const container = cvsContainer.valueconst width = container.offsetWidthconst height = container.offsetHeightcvs.value.width = widthcvs.value.height = heightconst ctx = cvs.value.getContext('2d')ctx.beginPath()ctx.moveTo(50, 50)ctx.lineTo(50, 400)ctx.lineTo(600, 400)ctx.stroke()// 画x刻度// x轴总长550,我们用数据总长度计算出每个刻度要画多长,这里刻度数量=数据长度,例如10cm想要分成2个刻度,那就是开头和结束两个刻度,就等于10个要分成一段也就是10/(2-1)const xScale = 550 / (data.xAxios.length - 1);for (let i = 0; i < data.xAxios.length; i++) {ctx.beginPath()ctx.strokeStyle = '#66666666'ctx.lineWidth = 0.5ctx.moveTo(50 + i * xScale, 400)ctx.lineTo(50 + i * xScale, 50)ctx.stroke()ctx.fillText(data.xAxios[i], 47 + i * xScale, 415,)}// 画y刻度// (我们只显示7个y轴刻度)我们计算出最大值分成7份每份有多长const yScale = (max.value / 7);console.log('yScale', yScale)for (let i = 0; i <= 7; i++) {ctx.beginPath()ctx.strokeStyle = '#66666666'ctx.lineWidth = 0.5ctx.moveTo(50, 400 - i * 50)ctx.lineTo(600, 400 - i * 50)ctx.stroke()ctx.fillText((i * yScale).toFixed(0), 50 - max.value.toString().length * 8, 403 - i * 50,)}// 画折线for (let i = 0; i < data.list.length; i++) {// 这里使用定时器渲染,模拟动画setTimeout(() => {ctx.beginPath()ctx.strokeStyle = 'rgba(31,121,211,.7)'ctx.lineWidth = 2ctx.moveTo(50 + i * xScale, 400 - data.list[i] * 350 / max.value)ctx.lineTo(50 + (i + 1) * xScale, 400 - data.list[i + 1] * 350 / max.value)ctx.stroke()// 字体颜色ctx.fillText(data.list[i], 45 + i * xScale, 400 - data.list[i] * 350 / max.value)}, 500 / data.list.length * i) // 渲染总时长/数据长度=每个数据渲染时长,使用定时器模拟动画}// 获取元素的边界信息const rect = cvs.value.getBoundingClientRect();// 绑定鼠标移动事件cvs.value.addEventListener('mousemove', (e) => {// 计算鼠标在元素内部的相对位置const x = e.clientX - rect.left;const y = e.clientY - rect.top;// 打印出相对位置if (x>50){const index = Math.round((x - 50) / xScale);// 四舍五入// 获取当前点的数据const value = data.list[index];// 更新提示框的值pageData.pointer = valuepageData.pointerX=x+15pageData.pointerY=y+15}})cvs.value.addEventListener('mouseleave', () => {pageData.pointerShow = false;})cvs.value.addEventListener('mouseenter',()=>{pageData.pointerShow = true;})
}
</script><style lang="scss" scoped>
.cvs-container {width: 1200px;height: 800px;background-color: white;border-radius: 15px;position: relative;.canvas {}.pointer{position: absolute;width: 100px;height: 50px;font-size: 24px;border-radius: 10px;background-color: #0675c5;display: flex;justify-content: center;align-items: center;color: white;box-shadow: 3px 3px 6px rgba(0, 0, 0, 0.3);}
}</style>