JS+Canvas整了一个折线图

317 阅读2分钟

Gitee地址:折线图源码:

初衷:2021-9-6;练练手,发散一下自己的思维;

结果:达到了练手目的;有时间还可以发散一下其他图表,哈哈;

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #charts {
            width: 500px;
            height: 500px;
            background-color: teal;
            margin: 100px auto;
        }
    </style>
</head>

<body>
    <div id="charts"></div>
    <script type="text/javascript" src="./charts.js"></script>
    <script type="text/javascript" src="./index.js"></script>
</body>

</html>
(function () {
    function Charts(el) {
        this.el = typeof el === 'string' ? document.querySelector(el) : el;
    }
    Charts.config = {
        type: 'line',
        drakMode: false,
        font: '25px serif',
        lineColor: 'orange',
        lineWidth: 1,
        XAxisColor: 'orange',
        YAxisColor: 'orange',
        gridColor: 'gray',
        drawStartPoints: {
            x: 30,
            y: 30
        },
        drawCenter: 60,
        textOffsetPoint: 15
    }
    Charts.prototype = {
        init: function () {
            var oCanvas = document.createElement('canvas'),
                thisElWidth = this.el.clientWidth,
                thisElHeight = this.el.clientHeight,
                oController = document.createElement('div'),
                oYLine = document.createElement('div'),
                oXLine = document.createElement('div');
            oCanvas.width = thisElWidth;
            oCanvas.height = thisElHeight;
            oController.width = thisElWidth;
            oController.height = thisElHeight;
            oController.className = 'chart_controller';
            this.oCanvas = oCanvas;
            this.oController = oController;
            this.oYLine = oYLine;
            this.oXLine = oXLine;
            this.thisElWidth = thisElWidth;
            this.thisElHeight = thisElHeight;
            this.render();
            this.oController.appendChild(this.oCanvas);
            this.el.appendChild(this.oController);
            this.bindEvent();
        },
        bindEvent: function () {
        },
        render: function () {
            var ctx = this.oCanvas.getContext('2d');
            switch (Charts.config.type) {
                case 'line':
                    this.drawLine(ctx);
                    break;
                case 'pie':
                    this.drawPie(ctx);
                    break;
                default:
                    this.drawLine(ctx);
                    break;
            }
        },
        drawLine: function (ctx) {
            var parmes = this.getDrawParmes();
            //绘制XY轴
            ctx.lineWidth = 7;
            ctx.strokeStyle = 'orange';
            ctx.lineCap = 'square';
            //X主轴
            ctx.beginPath();
            ctx.moveTo(parmes.XY_startPoints.x, parmes.XY_startPoints.y);
            ctx.lineTo(parmes.X_endPoints.x, parmes.X_endPoints.y);
            ctx.stroke();
            //Y主轴 
            ctx.beginPath();
            ctx.moveTo(parmes.XY_startPoints.x, parmes.XY_startPoints.y);
            ctx.lineTo(parmes.Y_endPoints.x, parmes.Y_endPoints.y);
            ctx.stroke();
            //绘制X轴上面的步长标记点
            this._drawStepAndText('X', ctx, parmes);
            this._drawStepAndText('Y', ctx, parmes);
            this._drawDataLine(ctx, parmes);
        },
        /**
         * 绘制数据折线
         */
        _drawDataLine: function (ctx, parmes) {
            var xys = this._dataToTrueXY(parmes),
                item,
                _this = this,
                textOffset = 10;
            ctx.beginPath();
            ctx.moveTo(xys[0].x, xys[0].y);
            for (var i = 0; i < xys.length; i++) {
                item = xys[i];
                (function (i, item) {
                    setTimeout(() => {
                        ctx.lineTo(item.x, item.y);
                        ctx.fillText(_this.data.data[i].value, item.x + textOffset, item.y - textOffset);
                        ctx.stroke();
                    }, i * 200);
                })(i, item)
            }
        },
        /**
         * 数据转换成真实的XY值
         */
        _dataToTrueXY(parmes) {
            var res = [],
                item,
                data = this.data.data,
                XAxisindex,//拿到数据中和X轴数据中对应的下标
                xstep = parmes.x_step,
                ystep = parmes.y_step,
                ytag = parmes.y_step_tag;
            for (var i = 0; i < data.length; i++) {
                item = data[i];
                XAxisindex = this.data.xAxis.indexOf(item.xAxis) + 1;
                var temp = {
                    x: XAxisindex * xstep,
                    y: (ystep * data.length) - ystep * (item.value / ytag) + ystep
                }
                res.push(temp);
            }
            return res;
        },
        /**
         * 折线图绘制步长和文本
         * @param {*} tag 
         * @param {*} ctx 
         * @param {*} len 
         * @param {*} parmes 
         */
        _drawStepAndText: function (tag, ctx, parmes) {
            var YAxisMax = parmes.xAxisLength * parmes.y_step;
            ctx.beginPath();
            ctx.lineCap = 'square';
            ctx.fillStyle = 'white';
            for (var i = 1; i < parmes.xAxisLength + 1; i++) {
                if (tag.toUpperCase() == 'X') {
                    ctx.beginPath();
                    ctx.lineWidth = Charts.config.lineWidth;
                    var tempX = i * parmes.x_step;
                    ctx.moveTo(tempX, parmes.X_endPoints.y);
                    ctx.strokeStyle = Charts.config.gridColor;
                    ctx.lineTo(tempX, Charts.config.drawStartPoints.y);
                    ctx.fillText(this.data.xAxis[i - 1], tempX - Charts.config.textOffsetPoint, (parmes.X_endPoints.y + parmes.stepWidth + Charts.config.textOffsetPoint));
                    ctx.stroke();
                    ctx.strokeStyle = Charts.config.XAxisColor;
                } else if (tag.toUpperCase() == 'Y') {
                    ctx.beginPath();
                    ctx.lineWidth = Charts.config.lineWidth;
                    var tempY = (i + 1) * parmes.y_step;
                    ctx.strokeStyle = Charts.config.gridColor;
                    ctx.moveTo(parmes.xAxisLength * parmes.y_step, tempY);
                    ctx.lineTo(parmes.Y_endPoints.x, tempY);
                    ctx.fillText(Math.round(parmes.y_step_tag * parmes.yAxisLength - parmes.y_step_tag * i), 0, tempY);
                    ctx.stroke();
                    ctx.strokeStyle = Charts.config.YAxisColor;
                }
            }
            ctx.fillStyle = '';
        },
        getMaxYaxis: function (data) {
            if (!data) {
                throw TypeError('data not is array');
            }
            var max = 0,
                item;
            for (var index in data) {
                item = data[index];
                max = max > item.value ? max : item.value;
            }
            return max;
        },
        /**
         * 绘制参数
         * 包括起始点、结束点、X轴步长、Y轴步长
         * 
         * @returns object
         */
        getDrawParmes: function () {
            var fn = function (num, len) {
                return num / len;
            }
            var xAxisLength = this.data.xAxis.length,//X轴数据长度
                yAxisLength = this.data.data.length,
                max_yAxis = this.getMaxYaxis(this.data.data) > this.thisElHeight ? this.getMaxYaxis(this.data.data) : this.thisElHeight,//从数据中找到最大的Y轴上数据点
                XY_startPoints = { x: Charts.config.drawStartPoints.x, y: this.thisElHeight - Charts.config.drawStartPoints.y },//XY轴共同的绘制起始点
                X_endPoints = { x: this.thisElWidth - Charts.config.drawCenter, y: this.thisElHeight - Charts.config.drawCenter / 2 },//X轴绘制结束点
                Y_endPoints = { x: Charts.config.drawStartPoints.x, y: Charts.config.drawStartPoints.y },//Y轴绘制结束点
                x_step = Math.floor((this.thisElWidth - Charts.config.drawCenter) / xAxisLength),//X轴,绘制月份+小竖线分割标志
                y_step = Math.floor((this.thisElHeight - Charts.config.drawCenter) / yAxisLength),//Y轴步长
                y_step_no_floor = (this.thisElHeight - Charts.config.drawCenter) / yAxisLength,//Y轴步长
                y_step_tag = fn(max_yAxis, yAxisLength),//Y轴每个步长文本点
                stepWidth = 10;
            return {
                xAxisLength,
                yAxisLength,
                max_yAxis: max_yAxis,
                XY_startPoints: XY_startPoints,
                X_endPoints: X_endPoints,
                Y_endPoints: Y_endPoints,
                x_step: x_step,
                y_step: y_step,
                y_step_no_floor,
                y_step_tag,
                stepWidth
            }
        },
        drawPie: function (ctx) {
            
        },
        setOption: function (data) {
            this.data = data;
            this.init();
        }
    }
    window.Charts = Charts;
})()

var line = {
    xAxis: [
        '2021-1',
        '2021-2',
        '2021-3',
        '2021-4',
        '2021-5',
        '2021-6',
        '2021-7',
        '2021-8',
        '2021-9',
        '2021-10',
        '2021-11',
        '2021-12',
    ],
    data: [
        {
            id: 1,
            value: 300,
            text: '我爱中国1',
            xAxis: '2021-1'
        },
        {
            id: 2,
            value: 72,
            text: '我爱中国2',
            xAxis: '2021-2'
        },
        {
            id: 3,
            value: 350,
            text: '我爱中国3',
            xAxis: '2021-3'
        },
        {
            id: 4,
            value: 360,
            text: '我爱中国4',
            xAxis: '2021-4'
        },
        {
            id: 5,
            value: 180,
            text: '我爱中国5',
            xAxis: '2021-5'
        },
        {
            id: 6,
            value: 250,
            text: '我爱中国6',
            xAxis: '2021-6'
        },
        {
            id: 7,
            value: 252,
            text: '我爱中国7',
            xAxis: '2021-7'
        },
        {
            id: 8,
            value: 36,
            text: '我爱中国8',
            xAxis: '2021-8'
        },
        {
            id: 9,
            value: 324,
            text: '我爱中国9',
            xAxis: '2021-9'
        },
        {
            id: 10,
            value: 36,
            text: '我爱中国10',
            xAxis: '2021-10'
        },
        {
            id: 11,
            value: 396,
            text: '我爱中国11',
            xAxis: '2021-11'
        },
        {
            id: 12,
            value: 432,
            text: '我爱中国12',
            xAxis: '2021-12'
        }
    ]
}
// Charts.config.type = 'pie';
var oChart = document.getElementById('charts');
var myChart = new Charts('#charts');
myChart.setOption(line);