【canvas】根据给定数据画折线图

首先我们需要写好画坐标的方法

/**
 * Created by evan_qb on 2017/12/17.
 */
//画坐标轴的两条线
function drawCoordinate(ctx,vertexTop,vertexRight,origin,arrow,padding) {
    ctx.beginPath();
    ctx.moveTo(vertexTop.x, vertexTop.y);
    ctx.lineTo(origin.x, origin.y);
    ctx.lineTo(vertexRight.x, vertexRight.y);
    ctx.stroke();
    //画向上箭头
    ctx.beginPath();
    ctx.moveTo(vertexTop.x, vertexTop.y);
    ctx.lineTo(vertexTop.x - arrow.width / 2, vertexTop.y + arrow.height);
    ctx.lineTo(vertexTop.x, vertexTop.y + arrow.height / 2);
    ctx.lineTo(vertexTop.x + arrow.width / 2, vertexTop.y + arrow.height);
    ctx.fill();
    //画右边的箭头
    ctx.beginPath();
    ctx.moveTo(vertexRight.x, vertexRight.y);
    ctx.lineTo(vertexRight.x - arrow.height, vertexRight.y - arrow.width);
    ctx.lineTo(vertexRight.x - arrow.height / 2, vertexRight.y);
    ctx.lineTo(vertexRight.x - arrow.height, vertexRight.y + arrow.width);
    ctx.fill();
}


然后我们引入该js文件,并准备数据画出坐标后以及折线

html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>武大郎卖饼</title>
    <style>
        canvas{
            border: 1px dashed gray;
        }
    </style>
</head>
<body>
    <canvas id="cvs" width="500" height="500"></canvas>
</body>
<!-- 引入文件: 画坐标-->
<script src="js/draw.js"></script>

js代码如下:

<script>
    var cvs = document.getElementById('cvs');
    var ctx = cvs.getContext('2d');
    // 坐标轴距离画布上右下左的边距
    var padding = {
        top:20,
        right:20,
        bottom:20,
        left:20
    }
    // 坐标轴中箭头的宽和高
    var arrow = {
        width:12,
        height:20
    }
    // 求坐标轴上顶点的坐标
    var vertexTop = {
        x:padding.left,
        y:padding.top
    }
    // 求坐标轴原点的坐标
    var origin = {
        x:padding.left,
        y:cvs.height - padding.bottom
    }
    // 求坐标轴右顶点的坐标
    var vertexRight = {
        x:cvs.width - padding.left,
        y:cvs.height - padding.bottom
    }
    //设置线宽
    ctx.lineWidth = 2;
    //画坐标
    drawCoordinate(ctx,vertexTop,vertexRight,origin,arrow,padding);
    //计算武大郎每天卖饼的情况
    var data = [20,50,100,123,455,378,226,27,7,3];
    // 求坐标轴默认可显示数据的最大值
    var coordMaxX = cvs.width - padding.left - padding.right -arrow.height;
    var coordMaxY = cvs.height - padding.bottom - padding.top - arrow.height;

    //求数据缩放的比例
    var ratioX = coordMaxX / data.length;
    //Math.max.apply选取数组中的最大值
    var ratioY = coordMaxY / Math.max.apply(null,data);
    //根据比例,对元数据进行缩放
    /*
     * 数组.map(function( val, index, arr ) { return 1; } )
     * 返回值:把回调reurn的值共同组成一个数组
     * 对数组的元素进行等比例缩放
     */
    var ratioData = data.map(function(val){
        return val*ratioY;
    });

    //画点
    ctx.fillStyle = "green";
    //val为值,index为下标
    //等比例缩放后的数据
    ratioData.forEach(function(val,index){
        ctx.fillRect(origin.x + (index*ratioX) - 3,origin.y - val - 3,4,4);
    });

    //根据点连线
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.strokeStyle = "yellow";
    //等比例缩放后的数据
    ratioData.forEach(function (val,index) {
        ctx.lineTo(origin.x + (index * ratioX),origin.y - val);
    });
    //描边
    ctx.stroke();
</script>



结果如下:


优化版:

我们可以使用对象来对以上的代码进行优化

我们可以将画图的方法抽出出一个Line对象

/**
 * Created by evan_qb on 2017/12/17.
 */
/*
 * constructor { line } 折线图构造函数
 * param { ctx: Context } 绘图环境
 * param { data: Array } 绘制折线图所需的数据
 * param { padding: Object } 设置坐标轴到画布的边距
 * param { arrow: Object } 设置箭头的宽高
 */
function Line(ctx,data,padding,arrow){
    this.ctx = ctx;
    this.data = data;
    this.padding = padding || { top: 10, right: 10, bottom: 10, left: 10 };
    this.arrow = arrow || { width: 10, height: 20 };
    //上顶点的坐标
    this.vertexTop = {
        x:this.padding.left,
        y:this.padding.top
    }
    //原点的坐标
    this.origin = {
        x:this.padding.left,
        y:this.ctx.canvas.height - this.padding.bottom
    }
    //右顶点的坐标
    this.vertexRight = {
        x:this.ctx.canvas.width - this.padding.right,
        y:this.ctx.canvas.height - this.padding.bottom
    }
    //计算坐标轴的最大刻度
    this.coordWidth = this.ctx.canvas.width - this.padding.left - this.padding.right - this.arrow.height;
    this.coordHeight = this.ctx.canvas.height - this.padding.top - this.padding.bottom - this.arrow.height;
}
//给原型扩充方法
Line.prototype = {
    constructor:Line,

    draw:function(){
        this.drawCoord();
        this.drawArrow();
        this.drawLine();
    },
    //绘制坐标轴
    drawCoord:function(){
        this.ctx.beginPath();
        this.ctx.moveTo(this.vertexTop.x,this.vertexTop.y);
        this.ctx.lineTo(this.origin.x,this.origin.y);
        this.ctx.lineTo(this.vertexRight.x,this.vertexRight.y);
        this.ctx.stroke();
    },
    //绘制箭头
    drawArrow:function(){
        //绘制向上的箭头
        this.ctx.beginPath();
        this.ctx.moveTo(this.vertexTop.x,this.vertexTop.y);
        this.ctx.lineTo(this.vertexTop.x - this.arrow.width / 2,this.vertexTop.y + this.arrow.height);
        this.ctx.lineTo(this.vertexTop.x , this.vertexTop.y + this.arrow.height / 2);
        this.ctx.lineTo(this.vertexTop.x + this.arrow.width / 2,this.vertexTop.y + this.arrow.height);
        this.ctx.fill();
        //绘制向右的箭头
        this.ctx.beginPath();
        this.ctx.moveTo(this.vertexRight.x,this.vertexRight.y);
        this.ctx.lineTo(this.vertexRight.x - this.arrow.height,this.vertexRight.y - this.arrow.width / 2);
        this.ctx.lineTo(this.vertexRight.x - this.arrow.height / 2,this.vertexRight.y);
        this.ctx.lineTo(this.vertexRight.x - this.arrow.height,this.vertexRight.y + this.arrow.width / 2);
        this.ctx.fill();
    },
    //根据数据绘制折线图
    drawLine:function(){
        //先清除路径
        this.ctx.beginPath();
        var self = this;
        //计算x轴和y轴的缩放比值
        var ratioX = this.coordWidth / this.data.length;
        var ratioY = this.coordHeight / Math.max.apply(null,self.data);
        //设置点的颜色
        this.ctx.fillStyle = "green";
        //根据原点计算点的坐标
        //x = self.origin.x + x
        //y = self.origin.y - y
        //遍历数据,并依次描点 数组:val:值 index:下标
        this.data.forEach(function(val,index){
            self.ctx.fillRect(self.origin.x + index*ratioX - 2,self.origin.y - val*ratioY - 2,4,4);
        });
        //设置线的颜色
        this.ctx.strokeStyle = "yellow";
        //遍历数据,连线
        this.data.forEach(function(val,index){
            self.ctx.lineTo(self.origin.x + index*ratioX,self.origin.y - val*ratioY);
        });
        this.ctx.stroke();
    }
}

然后将创建这个对象,调用对应的方法,将数据传入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对象版折线图</title>
    <style>
        canvas{
            border: 1px solid red;
        }
    </style>
</head>
<body>
    <canvas id="cvs" width="500" height="500"></canvas>
</body>
<!--引入划线的文件-->
<script src="line.js"></script>
<script>
    var cvs = document.getElementById("cvs");
    var ctx = cvs.getContext("2d");
    //创建对象
    var line = new Line(ctx,[ 10, 40, 70, 730, 100, 40, 1500, 2000, 300 ]);
    //调用绘图方法
    line.draw();
</script>
</html>

运行结果:




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值