使用H5的canvas绘制饼状图

9 篇文章 0 订阅
3 篇文章 0 订阅

前言:在我们写网站,我们往往都想要向用户展示一些数据,比如访问数,文章的点击数等等。如果我们只是用数字来向用户展示的,用户很可能会感觉到枯燥,因为数据的显示实在使太单一的。所以今天我来教大家来用H5的画布api来绘制饼状图,让你的网站更加高大上。

直接进入正题,先展示成果和源码。

最终结果:
饼状图
代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <canvas width="600" height="600"></canvas>
</body>
<script>
    let data = [
        {
            size: 2,
            title: '0-5岁'
        },
        {
            size: 3,
            title: '5-10岁'
        },
        {
            size: 4,
            title: '10-15岁'
        },
        {
            size: 1,
            title: '15-20岁'
        },
        {
            size: 6,
            title: '20-25岁'
        },
        {
            size: 4,
            title: '25-30岁'
        },
        {
            size: 1,
            title: '20-35岁'
        }
    ]
    class Chart {
        constructor(data) {
            this.myCanvas = document.querySelector('canvas')
            this.ctx = this.myCanvas.getContext('2d')
            // 绘制饼状图所需的数据
            this.data = data
            // 取画布的中心作为饼状图的圆心
            this.x0 = this.myCanvas.width / 2
            this.y0 = this.myCanvas.height / 2
            // 用来存储每一个数据对应的弧度
            this.angles = []
            // 半径
            this.radius = 150
            // 延申线的长度
            this.outline = 15
            // 初始化功能
            this.init()
        }
        init() {
            // 获取对应的角度
            this.getAngles()
            // 画饼状图
            this.drawCircle()
        }
        // 获取每个数据对应的角度
        getAngles() {
            let sum = 0
            this.data.forEach(item => {
                sum += item.size
            })
            this.data.forEach((item) => {
                this.angles.push((item.size / sum) * Math.PI * 2)
            })
        }
        drawCircle() {
            let startAngle = 0
            let endAngle = 0
            // 循环绘制扇形,最后组成圆
            for (let i = 0; i < this.data.length; i++) {
                endAngle = startAngle + this.angles[i]
                this.ctx.beginPath()
                // 每次都要把起点移回圆心,不然就会就弄不成扇形
                this.ctx.moveTo(this.x0, this.y0)
                this.ctx.fillStyle = this.getRandomColor()
                this.ctx.arc(this.x0, this.y0, this.radius, startAngle, endAngle)
                this.ctx.closePath()
                this.ctx.fill()
                // 绘制延迟线
                this.drawDetails(startAngle, this.angles[i], this.ctx.fillStyle, i)
                startAngle = endAngle
            }
        }
        // 获取随机颜色
        getRandomColor() {
            let r = Math.floor(Math.random() * 255)
            let g = Math.floor(Math.random() * 255)
            let b = Math.floor(Math.random() * 255)
            return `rgb(${r},${g},${b})`
        }
        drawDetails(startAngle, angle, color, i) {
            // 绘制延申线
            let edge = this.radius + this.outline
            let edgeX = Math.cos(startAngle + angle / 2) * edge
            let edgeY = Math.sin(startAngle + angle / 2) * edge
            this.ctx.moveTo(this.x0, this.y0)
            this.ctx.lineTo(edgeX + this.x0, edgeY + this.y0)
            this.ctx.strokeStyle = color
            this.ctx.stroke()
            // 绘制文字下划线
            this.drawTextDecoration(edgeX + this.x0, edgeY + this.y0, i, color)
            // 画布左上方的描述
            this.drawDataDecoration(this.data[i].title, color, i)
        }
        drawTextDecoration(x, y, i, color) {
            this.ctx.font = '20px 宋体'
            this.ctx.beginPath()
            this.ctx.moveTo(x, y)
            this.ctx.strokeStyle = color
            let textWidth = this.ctx.measureText(this.data[i].title).width
            if (x > this.x0) { // x坐标大于原点,下划线向右延申不会与圆重叠
                this.ctx.lineTo(x + textWidth, y)
                // 添加文字,y-3使文字和下划线不用紧贴
                this.ctx.fillText(this.data[i].title, x, y - 3)
            } else { // x坐标小于原点,下划线向左延申不会与圆重叠
                this.ctx.lineTo(x - textWidth, y)
                // 添加文字
                this.ctx.fillText(this.data[i].title, x - textWidth, y - 3)
            }
            this.ctx.stroke()
        }
        // 在画布的左上方添加描述
        drawDataDecoration(title, color, i) {
            this.ctx.font = '15px 宋体'
            this.ctx.fillStyle = color
            this.ctx.beginPath()
            this.ctx.fillRect(10, (18 * i) + 10, 30, 10)
            this.ctx.fillStyle = 'black'
            this.ctx.fillText(title, 50, (18 * i) + 20)
        }
    }
    const chart = new Chart(data)
</script>

</html>

难点:

1.绘制圆形:

计算每个数据占总数的百分比,拿去乘以整个圆的弧度2π,就可以得到每个数据对应的弧度了。利用这个弧度去绘制扇形,这个扇形组合起来就是一个圆了。如果不想设置颜色,可以像我一样写一个获取随机颜色的函数。

2.绘制延长线

在这里插入图片描述
由于将延长线的颜色和对应的区域的颜色是一样的,我们就只看到饼状图区域外的线。
先说一下延长线的特点:会平分对应扇形的角,经过中点。
.在这里插入图片描述
我们要已知延长线的长度,角度。我们就可以根据三角函数来求延长线的坐标。
我们假设原点是x0和y0,半径的radius,对应扇形的角度是30°。那么我们可以做一个辅助线。如下图:
在这里插入图片描述
斜边就是代码中的半径+outline
a边的长度:斜边×cos(当前扇形的一半)
b边的长度:斜边×sin(当前扇形的一半)
因为垂直嘛,所以:
x坐标就是:圆心的x坐标+a
y坐标就是:圆心的y坐标+b

有些人可能会疑问如果角度大于180度的话,会不会异常,假如扇形的角度是大于180°的,获取到的三角函数值是负数的,就会自动的往那个方向延申了。

3.绘制下滑线

根据对应的延长线的终点坐标,和计算文字所需要的宽度来画线。如果延长线的终点x坐标是在右边的话,就x+文字所需要的宽度,y不变。在左边就x-文字所需要的宽度,y不变
其他的都是基本操作了。

结语

最近学的h5的画布功能,发现画布能实现的功能还是挺多的,实现的功能的可以增添自己网站的丰富度。让网站感觉没那么单调。加油!!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值