原生CANVAS语法实现的封装折线图和饼图

Posted 我是真的不会前端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生CANVAS语法实现的封装折线图和饼图相关的知识,希望对你有一定的参考价值。

介绍

首先canvas是html5的一种新标签,代表一个画布,可以在上面进行绘画、动画等操作。画布默认大小是300*150。
canvas标签本生就是一个只是画布,可以导出为透明背景的png图片。要实现上面有文字、线条等呈现,需要使用js实现。总之,画布上一切的呈现,都需要使用js来实现。
画布大小不能使用样式控制,用样式调整的是一个可视区域,其实真实的大小,还是一样的,只是在画布上画内容的话,会等比例放大。调整画布大小,需要在标签上直接添加width和height属性。
canvas标签也是可以放文字的,只是当canvas标签不被浏览器支持的时候,会显示,例如ie8。

基本使用

  1. 获取canvas元素

    var canvas = document.querySelector('canvas')
    
  2. 获取这个元素的工具箱 - 上下文

    工具箱中包含了很多工具:直线工具、弧形工具、文本工具、矩形工具… 我们需要依赖这些工具进行绘画

    语法:

    var ctx = canvas.getContext('2d')
    
  3. 画图形

    默认情况下,canvas选中的工具是直线工具,要画一条直线的话,需要先将画笔放到一个指定像素点

    ctx.moveTo(x轴坐标,y轴坐标)
    

    然后以线的形式,将画笔移动到终点:

    ctx.lineTo(200,100)
    

    此时应该已经有线了,但是页面中并没有出现,因为我们前面两个步骤描述的是画笔走的路径,这个路径需要进行填充:

    ctx.fill
    

    也可以进行描边:

    ctx.strock()
    

    默认是黑色,线的宽度是1px
    此时在画布中出现了一条线,但是发现线的粗细不是1px,颜色也不是黑色。这是因为,canvas绘制线条,会将线条的中心点跟坐标点的位置进行对齐,这样的话,不管是线的开始对准坐标点还是线的结束对准中心点都不合适,所以浏览器干脆将线强行拉伸到 坐标点-1~坐标点+1,也就是本来1px的线,被强制拉伸到了2px,这样的话,颜色也就浅了。

我们可以将线的宽度设置为2px做验证,因为2px就不用被拉伸了:

ctx.lineWith = 2 // 值是数字
ctx.lineTo(200,100)
//结果就是2px宽的线,纯黑色了。基于这个问题,我们以后在绘图的时候,尽量将像素设置为双数。

1 :简单封装的一个折线图

html和css

<html>
<head>
<meta charset="UTF-8">
<title>document</title>
</head>
<style>
canvas
    border:1px solid #000;

</style>
<body>
<canvas class="linechart" width=600 height=300></canvas>
</body>

javascript封装的源码

// 定义一个类
class LineChart
    constructor(classname,data)
        // 画布标签获取到
        this.canvas = document.querySelector('.'+classname)
        // 工具箱
        this.ctx = this.canvas.getContext('2d')
        // 定义画布大小
        this.canvasWidth = this.ctx.canvas.width
        this.canvasHeight = this.ctx.canvas.height
        // 定义坐标轴到画布边缘的间隙
        this.space = 20
        // 定义原点
        this.originX = 0
        this.originY = 0
        // 定义箭头三角形的高 和 点 的长度
        this.length = 10
        // 将数据绑定成类的属性
        this.data = data
        // 定义画布中的数据
        this.canvasData = null;
    
    // 定义一个初始化方法
    init()
        // 获取原点
        this.getOrigin()
        // 创建坐标轴 - x、y
        this.createAxisX()
        this.createAxisY()
        // 转换数据
        this.convert(this.data)
        // 描每个点
        // this.setPoint(data)
        // 描所有点
        this.setPoints(this.canvasData)
        // 连线
        this.connectLine(this.canvasData)
    
    connectLine(data)
        for(let i=0;i<data.length-1;i++)
            this.ctx.beginPath()
            // 放画笔
            this.ctx.moveTo(data[i].x,data[i].y)
            // 画线
            this.ctx.lineTo(data[i+1].x,data[i+1].y)
            this.ctx.closePath()
            this.ctx.stroke()
        
    
    // 描所有点的方法
    setPoints(data)
        data.forEach(point=>
            this.setPoint(point)
        )
    
    // 描一个点的方法
    setPoint(data)
        this.ctx.beginPath()
        // 放画笔位置
        this.ctx.moveTo(data.x-this.length/2,data.y-this.length/2)
        // 画线
        this.ctx.lineTo(data.x+this.length/2,data.y-this.length/2)
        this.ctx.lineTo(data.x+this.length/2,data.y+this.length/2)
        this.ctx.lineTo(data.x-this.length/2,data.y+this.length/2)
        this.ctx.lineTo(data.x-this.length/2,data.y-this.length/2)
        this.ctx.closePath()
        this.ctx.stroke()
        this.ctx.fill()
    
    // 转换数据的方法
    convert()
        // 将坐标轴的数据转成画布的数据
        // 遍历数据
        this.canvasData = this.data.map(point=>
            return 
                x:point.x+this.space,
                y:this.canvasHeight - this.space - point.y
            
        )
    
    // 创建y轴
    createAxisY()
        this.ctx.beginPath()
        // 将画笔放到原点
        this.ctx.moveTo(this.originX,this.originY)
        // 画线
        this.ctx.lineTo(this.originX,this.space)
        // 画箭头
        this.ctx.lineTo(this.originX-this.length/2,this.space + this.length)
        this.ctx.lineTo(this.originX+this.length/2,this.space + this.length)
        this.ctx.lineTo(this.originX,this.space)
        this.ctx.closePath()
        this.ctx.stroke()
        this.ctx.fill()
    
    // 获取原点的方法
    getOrigin()
        this.originX = this.space;
        this.originY = this.canvasHeight - this.space
    
    // 定义创建x坐标轴的方法
    createAxisX()
        this.ctx.beginPath()
        // 将画笔放到原点
        this.ctx.moveTo(this.originX,this.originY)
        // 画线
        this.ctx.lineTo(this.canvasWidth - this.space,this.originY)
        // 画箭头
        this.ctx.lineTo(this.canvasWidth-this.space-this.length,this.originY-this.length/2)
        this.ctx.lineTo(this.canvasWidth-this.space-this.length,this.originY+this.length/2)
        this.ctx.lineTo(this.canvasWidth - this.space,this.originY)
        this.ctx.closePath()
        this.ctx.stroke()
        this.ctx.fill()
    


调用方法

var lc = new LineChart('linechart',[
    
        x:0,           //写入每个坐标点的位置
        y:0
    ,
    
        x:100,
        y:100
    ,
    
        x:200,
        y:120
    ,
    
        x:300,
        y:220
    ,
    
        x:400,
        y:150
    ,
    
])
lc.init()

效果

是不是简陋了点,现在封装一个好看点的饼图

Demo2:封装一个圆饼图

html和css

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>document</title>
</head>
<body>
<style>
    canvas
        border:1px solid #000
    
</style>
<canvas width=900 height=500 class="pie"></canvas>
</body>

javascript封装的源码

class Pie
    constructor(classname,data)
        // 将数据绑定在属性上
        this.data = data
        // 获取canvas节点
        this.canvas = document.querySelector('.'+classname)
        // 获取工具箱
        this.ctx = this.canvas.getContext('2d');
        // 定义圆心
        this.x = this.ctx.canvas.width / 2 + 200;
        this.y = this.ctx.canvas.height / 2;
        // 定义半径
        this.r = 150
        // 定义延伸线的长度
        this.R = this.r + 40
        // 定义数据总量
        this.total = this.data.reduce((a,b)=>
            if(typeof a === 'number')
                return a + b.num
            else
                return this.data[0].num + b.num
            
            
        )
    
    // 初始化方法
    init()
        // 数据处理方法
        this.convertData()
        // 画饼的方法
        this.drawPie()
    
    // 数据处理方法
    convertData()
        this.data.forEach(v=>
            v.arc = v.num/this.total*2 * Math.PI
        )
    
    // 画饼的方法
    drawPie()
        // 初始弧度
        var startArc = 0
        this.data.forEach((v,index)=>
            this.ctx.beginPath()
            this.ctx.moveTo(this.x,this.y)
            var endArc = startArc + v.arc
            this.ctx.arc(this.x,this.y,this.r,startArc,endArc)
            startArc = endArc
            var color = this.getColor()
            this.ctx.fillStyle = color
            this.ctx.fill()
            // 画延伸线
            this.outLine(v.arc,endArc,color,v.title)
            // 画说明方块
            this.descSquare(index)
        )
    
    // 画说明方块的方法
    descSquare(index)
        var squareWidth = 50;
        var squareHeight = 30;
        var squareLeft = 20;
        var squareTop = 20
        // 画小方块
        this.ctx.fillRect(squareLeft,squareTop*(index+1)+squareHeight*index,squareWidth,squareHeight)
    
        // 计算文字的开始坐标
        var textX = squareLeft + squareWidth + squareLeft
        var textY = squareTop*(index+1)+squareHeight*index+squareHeight/2
    
        this.ctx.textBaseline = 'middle';
        this.ctx.textAlign = 'left';
        var percent = (this.data[index].num / this.total).toFixed(2) * 100 + '%'
        this.ctx.fillText(this.data[index].title+'   ' + percent,textX,textY)
    
    // 画延伸线的方法
    outLine(currentArc,endArc,color,title)
        // 获取到需要画线的这个弧度
        var arc = endArc - currentArc/2
        // 计算这条线的终点x和y
        var endX = Math.cos(arc) * this.R + this.x
        var endY = Math.sin(arc) * this.R + this.y
        this.ctx.moveTo(this.x,this.y)
        this.ctx.lineTo(endX,endY)
        this.ctx.strokeStyle = color
        // 获取文字长度
        this.ctx.font = '16px 楷体'
        var textWidth = this.ctx.measureText(title).width
        if(endX<this.x)
            var textX = endX - textWidth
            this.ctx.textAlign = 'left';
        else
            var textX = endX + textWidth
            this.ctx.textAlign = 'right';
        
        this.ctx.textBaseline = 'bottom';
        this.ctx.lineTo(textX,endY)
        // 写字
        this.ctx.strokeText(title,textX,endY-3)
        this.ctx.stroke()
    
    // 获取随机颜色的方法
    getColor()
        return `rgb($Math.floor(Math.random()*256),$Math.floor(Math.random()*256),$Math.floor(Math.random()*256))`
    


调用方法

var data = [
    
        title:"15 ~ 20 岁",
        num:20
    ,
    
        title我是刚学c#的,老师安排了任务,用c#winformchart画柱状图,折线图和饼图,恳请帮帮忙,

d3 基础折现图和饼图

前端如何实现立体饼图,柱状图,像下面这种?

c#有没有免费的统计工具?柱状图,饼图等等的

Android之自定义控件实现天气温度折线图和饼状图

Atitit.js图表控件总结