【帶着canvas去流浪】(1)繪製柱狀圖

示例代碼託管在:http://www.github.com/dashnowords/blogs前端

博客園地址:《大史住在大前端》原創博文目錄git

華爲雲社區地址:【你要的前端打怪升級指南】github

一. 任務說明

使用原生canvasAPI繪製柱狀圖。(柱狀圖截圖來自於百度Echarts官方示例庫【查看示例連接】canvas

二. 重點提示

柱狀圖或許是最容易實現的圖表類型了,矩形的部分直接使用fillRect()來繪製便可,爲了將座標軸標籤文字繪製在小分割線中間,須要用measureText()來測量文本的寬度,而後進行相應的偏移,不然直接繪製的話文字的左邊界會和直線相對齊。其餘部分都是一些基本API的使用,但願各位小夥伴經過作練習來熟悉這些API的用法。瀏覽器

三. 示例代碼

提示:代碼中將個別圖表參數直接寫在了函數裏(也就是所謂的「魔鬼數字」),這種作法是不提倡的,由於它違反了開發的基本原則之一「開放封閉原則」。若是你使用過Echarts圖表庫就會發現,圖表中幾乎全部要素均可以經過參數來定製,此處只須要關注canvasAPI的實現方法便可。echarts

/**
 * 獲取canvas繪圖上下文
 * @type {[type]}
 */
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

//繪圖配置
let options = {
        chartZone:[50,50,1000,700],//標識繪圖區域
        yAxisLabel:['0','100','200','300','400'],//標示Y軸座標
        yMax:400,//Y軸最大值
        xAxisLabel:['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],//X軸座標
        data:[10,50,200,330,390,320,220],//柱狀圖數據
        barStyle:{
            width:70,//柱狀圖寬度
            color:'#1abc9c'//柱狀圖顏色
        }
    }

/*Echarts使用時,會調用實例方法echartsInstance.setOptions(options)來啓動繪圖*/
drawBarChart(options);

/**
 * 繪製柱狀圖
 */
function drawBarChart(options) {
    drawAxis(options); //繪製座標軸
    drawYLabels(options); //繪製y軸座標
    drawXLabels(options); //繪製x軸座標
    //drawData(options);//繪製柱狀圖
    drawDataGradient(options);//繪製漸變色柱狀圖
}

/**
 * 繪製座標軸
 */
function drawAxis(options) {
    let chartZone = options.chartZone;
    context.strokeWidth = 4;
    context.strokeStyle = '#353535';
    context.moveTo(chartZone[0],chartZone[1]);
    context.lineTo(chartZone[0],chartZone[3]); //y軸總高從50到700
    context.lineTo(chartZone[2],chartZone[3]); //x軸總長從50到1000
    context.stroke();
}

/**
 * 繪製y軸座標
 */
function drawYLabels(options) {
    let labels = options.yAxisLabel;
    let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
    let gap = yLength / (labels.length - 1);
    
    labels.forEach(function (label, index) {
        //繪製座標文字
        let offset = context.measureText(label).width + 20;
        context.strokeStyle = '#eaeaea';
        context.font = '16px';
        context.fillText(label, options.chartZone[0] - offset ,options.chartZone[3] - index * gap);
        //繪製小間隔
        context.beginPath();
        context.strokeStyle = '#353535';
        context.moveTo(options.chartZone[0] - 10, options.chartZone[3] - index * gap);
        context.lineTo(options.chartZone[0], options.chartZone[3] - index * gap);
        context.stroke();
        //繪製輔助線
        context.beginPath();
        context.strokeStyle = '#eaeaea';
        context.strokeWidth = 2;
        context.moveTo(options.chartZone[0], options.chartZone[3] - index * gap);
        context.lineTo(options.chartZone[2], options.chartZone[3] - index * gap);
        context.stroke();
    });
}

/**
 * 繪製x軸座標
 */
function drawXLabels(options) {
    let labels = options.xAxisLabel;
    let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
    let gap = xLength / labels.length;
    
    labels.forEach(function (label, index) {
        //繪製座標文字
        let offset = context.measureText(label).width;
        context.strokeStyle = '#eaeaea';
        context.font = '18px';
        context.fillText(label, options.chartZone[0] + (index + 1) * gap - offset ,options.chartZone[3] + 20);
        //繪製小間隔
        context.beginPath();
        context.strokeStyle = '#353535';
        context.moveTo(options.chartZone[0] + (index + 1) * gap - offset / 2 ,options.chartZone[3]);
        context.lineTo(options.chartZone[0] + (index + 1) * gap - offset / 2,options.chartZone[3]+5);
        context.stroke();
        //存儲偏移量
        options.offsetXLabel = offset / 2;
    });
}

/**
 * 繪製數據
 */
function drawData(options) {
    let data = options.data;
    let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
    let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
    let gap = xLength / options.xAxisLabel.length;

    //繪製矩形
    data.forEach(function (item, index) {
        context.fillStyle = options.barStyle.color || '#1abc9c'; //02BAD4
        let x0 = options.chartZone[0] + (index + 1) * gap - options.barStyle.width / 2 - options.offsetXLabel;
        let height = item / options.yMax * (options.chartZone[3] - options.chartZone[1])*0.98;
        let y0 =  options.chartZone[3] - height;
        let width = options.barStyle.width;
        context.fillRect(x0,y0,width,height);
    });
}

/**
 * 繪製線性漸變色柱狀圖
 */
function drawDataGradient(options) {
    let data = options.data;
    let xLength = (options.chartZone[2] - options.chartZone[0])*0.96;
    let yLength = (options.chartZone[3] - options.chartZone[1])*0.98;
    let gap = xLength / options.xAxisLabel.length;
    //建立漸變色
    let fillStyleGradient = context.createLinearGradient(50,50,50,700);
        fillStyleGradient.addColorStop(0, options.barStyle.color);
        fillStyleGradient.addColorStop(1, 'rgba(1,176,241,0.6)');

    //繪製矩形
    data.forEach(function (item, index) {
        context.fillStyle = fillStyleGradient;
        let x0 = options.chartZone[0] + (index + 1) * gap - options.barStyle.width / 2 - options.offsetXLabel;
        let height = item / options.yMax * (options.chartZone[3] - options.chartZone[1])*0.98;
        let y0 =  options.chartZone[3] - height;
        let width = options.barStyle.width;
        context.fillRect(x0,y0,width,height);
    });
}

瀏覽器中可查看效果:函數

四. 思考題

若是但願在座標軸末端加一個箭頭,須要怎麼作呢?3d

/*x軸箭頭示例*/
//1.options中增長箭頭顏色和大小的設置
let options = {
    //...
    axisArrow:{
            size:2,
            color:'#DA5961'
        }
}
//箭頭繪製函數

/**
 * x軸繪製箭頭
 */
function drawArrow(options) {
    let factor = options.axisArrow.size;//獲取箭頭大小因子
    context.save();//保存當前設置的繪圖上下文
    context.translate(options.chartZone[2], options.chartZone[3]);//移動座標系原點至x軸末端
    context.beginPath();//開始繪製箭頭
    context.moveTo(0,0);//移動至新原點
    context.lineTo(2 * factor,-3 * factor);
    context.lineTo(10 * factor,0);
    context.lineTo(2 * factor, 3 * factor);
    context.lineTo(0,0);
    context.globalAlpha = 0.7; //設置填充色透明度
    context.fillStyle = options.axisArrow.color;//獲取箭頭顏色
    context.fill();//填充箭頭路徑
    context.restore();//恢復繪圖上下文樣式設置
}

箭頭效果:rest

y軸的箭頭請自行完成便可。

相關文章
相關標籤/搜索