类似于这种样式。鼠标放上去还要有内容显示。
代码如下
<!DOCTYPE html>
<html>
<head>
<title>Canvas Line Loop</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
background-color: #333;
margin: 200px 0 0 200px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="800" height="800"></canvas>
<script>
// 获取 Canvas 元素
const canvas = document.getElementById('myCanvas');
// 获取画布上下文
const ctx = canvas.getContext('2d');
//有多少数据分成几份
let outDataArr = [10, 20, 50, 10, 20, 10, 20, 50, 10, 20, 50, 20, 50, 10, 20, 20, 50, 10, 20, 10, 20, 50, 60, 8,
20, 3, 9, 50, 20, 50, 10, 20, 20, 50, 10, 20, 10, 20, 50, 60, 8, 20, 3, 9
]
let insideDataarr = [10, 20, 50, 60, 8, 10, 10, 20, 50, 10, 20, 50, 20, 50, 10, 20, 20, 50, 10, 20, 10, 20, 50,
60, 8, 20, 3, 9, 20, 50, 10, 20, 50, 20, 3, 20, 20, 50, 10, 20, 50, 10, 20, 9
]
/**
* 画两个由缺口的圆
*/
//圆的起始角度和结尾角度
let beginAngle = 270
let EndAngle = 220
//内,外圈半径
let radius = 100
let inradius = 90
//内外圈颜色
let outside = '#02aba3'
let inside = '#d95524'
//是否画出内外圈
let isShowArc = false
if (isShowArc) {
//画外圈圆
ctx.beginPath()
// ctx.arc(350, 300, 100, Math.PI * 1.5, (Math.PI) * 1.2)
ctx.arc(350, 300, 100, [(Math.PI) / 180] * beginAngle, [(Math.PI) / 180] * EndAngle)
ctx.lineWidth = 6
ctx.stroke()
ctx.moveTo(0, 0)
ctx.lineTo(350, 0)
ctx.lineTo(350, 300)
ctx.stroke()
//画内圈圆
ctx.beginPath()
// ctx.arc(350, 300, 80, Math.PI * 1.5, (Math.PI) * 1.2)
ctx.arc(350, 300, 80, [(Math.PI) / 180] * beginAngle, [(Math.PI) / 180] * EndAngle)
ctx.lineWidth = 1
ctx.stroke()
}
/**
* 画圆上的数据展示
*/
//刻度长度占比:获取数据中的最大值然后通过和最大值的比较算出每个数据的长度占比
let outLengthPercentage = []
let inLengthPercentage = []
let sumNumOut = Math.max.apply(null, outDataArr);
let sumNumIn = Math.max.apply(null, insideDataarr);
outDataArr.map((res, index) => {
outLengthPercentage.push((res / sumNumOut).toFixed(2) - 0)
inLengthPercentage.push((insideDataarr[index] / sumNumIn).toFixed(2) - 0)
})
//外,内圈刻度最长为
let outMaXLength = 60
let inMaXLength = 20
//有多少刻度:通过数组的长度判断刻度的数量
let dataNum = outDataArr.length
//由于canvas中角度顺序是 上 270° ,右 0°,下 90°,左 180°
//所以设置圆最上端顶点的角度为
let tops = 270
/**
* 保存所有画出来的点的坐标用以判断鼠标是否在刻度线上
*/
//获得外圈所有的尾线的坐标
let outLastData = []
//外圈x,y坐标的范围
let outLineData = []
//选中的那个刻度的index值
let checkLindexIndex = null
let oldCheckLindexIndex = null
//开始启动
drawLine()
/**
* 函数
*/
//在圆上画刻度的函数
function drawLine() {
console.log('开始画刻度');
//清除
//获得外圈所有的尾线的坐标
outLastData = []
//外圈x,y坐标的范围
outLineData = []
//用以设置内外圈刻度长度的标识
let index = 0
//每个刻度对应的角度
let addbeishu = ((360 - (beginAngle - EndAngle)) / (dataNum - 1)).toFixed(2) - 0
for (let i = 0; i < 310;) {
i = i.toFixed(2) - 0
//度数
let angle = tops + i
angle = angle >= 360 ? (angle - 360).toFixed(2) - 0 : angle
//画刻度的函数
//要传的参数有:内外圈种类 , 圆的横坐标 ,圆的纵坐标 ,圆的度数 ,外圈刻度颜色 ,内圈刻度颜色 ,每个刻度长度 ,内外圈的刻度的基础半径
drawScale('out', 350, 300, angle, outside, inside, outMaXLength * outLengthPercentage[index], radius)
drawScale('in', 350, 300, angle, outside, inside, inMaXLength * inLengthPercentage[index], inradius)
i += addbeishu
//给圆的最后一个加数据 其中 EndAngle 是圆结尾的度数
if (i >= 310) {
drawScale('out', 350, 300, EndAngle, outside, inside, outMaXLength * outLengthPercentage[index],
radius)
drawScale('in', 350, 300, EndAngle, outside, inside, inMaXLength * inLengthPercentage[index],
inradius)
}
index++
}
}
//画刻度
function drawScale(type, x, y, angle, outsideColor, insideColor, length, radius) {
//外圈
if (type == 'out') {
//通过sin和cos来计算刻度 开始点和结尾点 坐标
let oldY = Math.sin((Math.PI / 180) * (angle)).toFixed(2) * radius
let oldX = Math.cos((Math.PI / 180) * (angle)).toFixed(2) * radius
let newY = Math.sin((Math.PI / 180) * (angle)).toFixed(2) * (radius + length)
let newX = Math.cos((Math.PI / 180) * (angle)).toFixed(2) * (radius + length)
ctx.beginPath()
ctx.moveTo(x + oldX, y + oldY)
ctx.lineTo(x + newX, y + newY)
ctx.lineWidth = 3
ctx.strokeStyle = outsideColor
ctx.stroke()
outLastData.push([x + newX, y + newY])
//如果在右上角那存的数据就是 x<刻度结尾x && x>刻度开始x ,y>刻度结尾y && y<刻度开始y
//按照 小的下标为0 ,大的下标为 1
if (angle >= 270 && angle <= 360) {
outLineData.push({
x: [x + oldX - 1, x + newX + 1],
y: [y + newY - 1, y + oldY + 1]
})
}
if (angle >= 0 && angle < 90) {
outLineData.push({
x: [x + oldX - 1, x + newX + 1],
y: [y + oldY - 1, y + newY + 1]
})
}
if (angle >= 90 && angle < 180) {
outLineData.push({
x: [x + newX - 1, x + oldX + 1],
y: [y + oldY - 1, y + newY + 1]
})
}
if (angle >= 180 && angle < 270) {
outLineData.push({
x: [x + newX - 1, x + oldX + 1],
y: [y + newY - 1, y + oldY + 1]
})
}
} else {
//内圈
oldY = Math.sin((Math.PI / 180) * (angle)).toFixed(2) * radius
oldX = Math.cos((Math.PI / 180) * (angle)).toFixed(2) * radius
newY = Math.sin((Math.PI / 180) * (angle)).toFixed(2) * (radius - length)
newX = Math.cos((Math.PI / 180) * (angle)).toFixed(2) * (radius - length)
ctx.beginPath()
ctx.moveTo(x + oldX, y + oldY)
ctx.lineTo(x + newX, y + newY)
ctx.lineWidth = 3
ctx.strokeStyle = insideColor
ctx.stroke()
}
}
//重画页面
let times = null
function drawUp(e) {
// console.log(outLineData);
var rect = canvas.getBoundingClientRect();
//获得在canvas中点击的数据
let checkCoordinate = [e.clientX - rect.x, e.clientY - rect.y]
for (let index = 0; index < outLineData.length; index++) {
let res = outLineData[index]
if (res.x[1] >= checkCoordinate[0] && res.x[0] <= checkCoordinate[0] && res.y[1] >=
checkCoordinate[1] && res.y[0] <= checkCoordinate[1]) {
console.log('开始画延伸线');
checkLindexIndex = index
let x = outLastData[index][0]
let y = outLastData[index][1]
ctx.beginPath()
ctx.moveTo(x, y)
//前一半线在右端,后一半在左端
if (Math.floor(outLineData.length / 2) < index) {
ctx.lineTo(600, y + 10)
} else {
ctx.lineTo(200, y + 10)
}
ctx.stroke()
break;
} else {
checkLindexIndex = null
}
}
if (checkLindexIndex != oldCheckLindexIndex && typeof (checkLindexIndex) == 'number') {
console.log('重画延伸线');
ctx.clearRect(0, 0, 800, 800)
drawLine()
let x = outLastData[checkLindexIndex][0]
let y = outLastData[checkLindexIndex][1]
console.log(x, y);
ctx.beginPath()
ctx.moveTo(x, y)
//由于不是满圆,所以外线段的数组数量来判断详情指示框在左还是右。如果是在左边则
if (Math.floor(outLineData.length * 0.55) > checkLindexIndex) {
ctx.lineTo(600, y + 10)
ctx.lineTo(600, 500)
ctx.lineTo(450, 500)
ctx.lineTo(450, 788)
ctx.lineTo(750, 788)
ctx.lineTo(750, 500)
ctx.lineTo(600, 500)
drawText(500, 600)
} else {
ctx.lineTo(150, y + 10)
ctx.lineTo(150, 500)
ctx.lineTo(10, 500)
ctx.lineTo(10, 788)
ctx.lineTo(300, 788)
ctx.lineTo(300, 500)
ctx.lineTo(150, 500)
drawText(50, 600)
}
ctx.strokeStyle = '#c8c9cc'
ctx.stroke()
checkLindexIndex = oldCheckLindexIndex
} else if (checkLindexIndex == oldCheckLindexIndex && checkLindexIndex) {
} else {
console.log('清除延伸线');
ctx.clearRect(0, 0, 800, 800)
drawLine()
}
}
/**
* 画下面的文字
*/
function drawText(x, y) {
ctx.font = '50px Verdana'
ctx.strokeText('Hello Canvas!', x, y, 200)
// ctx.beginPath()
ctx.moveTo(x, y + 8)
ctx.lineTo(x + 200, y + 8)
ctx.moveTo(x - 50, y + 88)
ctx.lineTo(x + 250, y + 88)
ctx.stroke()
}
//canvas.onclick = drawUp
canvas.onmousemove = drawUp
</script>
</body>
</html>