微信小程序canvas 绘制图表

468 阅读2分钟

之前做了一个问卷小程序,做了一个将数据可视化保存为图片,如下: 在这里插入图片描述 当时仅仅实现绘制饼图,现在有个想法把柱状图、折线图等补齐。说干就干。 先放源码

d3.js 可视化库

之前有粗略学习过d3.js, 觉得D3 使用scale 类型的函数处理数据十分方便,于是想把scale放在小程序中引用,殊不知文件太大了,不方便操作。于是便粗略的自己实现了几个。这些方法将会再绘制图表中处理数据使用。

linearScale

// 映射 值域

class MyScaleLinear {
    constructor() {
        this.domainVal = []
        this.rangeVal = []
    }
    domain(arr) {
        try {
            if (!Array.isArray(arr)) throw 'domain 参数不是一个数组!';
            if (arr.length !== 2)    throw 'domain 数组长度不为2';
            this.domainVal = [...arr];
        }
        catch (e) {
            console.error(e)
        }
        return this;
    }
    range(arr) {
        try {
            if (!Array.isArray(arr)) throw 'domain 参数不是一个数组!';
            if (arr.length !== 2)    throw 'domain 数组长度不为2';
            this.rangeVal = [...arr];
        }
        catch (e) {
            console.error(e)
        }
        return this.findRange.bind(this);
    }

    findRange(val){
        let  result = (val - this.domainVal[0]) / (this.domainVal[1] - this.domainVal[0]) *  (this.rangeVal[1] - this.rangeVal[0]) +  this.rangeVal[0];
        return result
    }

}

export default MyScaleLinear

OrdinalScale

// 数组map
class  MyScaleOrdinal {
    constructor() {
        this.domainVal = []
        this.rangeVal = []
    }
    domain(arr) {
        try {
            if (!Array.isArray(arr)) throw 'domain 参数不是一个数组。'
            this.domainVal = [...arr]
        } catch (e) {
            console.error(e)
        }
        return this
    }
    range(arr) {
        try {
            if (!Array.isArray(arr)) throw 'range 参数不是一个数组。'
            this.rangeVal = [...arr]
        } catch (e) {
            console.error(e)
        }
        return this.findRange.bind(this);
    }
    findRange(val){
        let index = this.domainVal.findIndex((item) => item === val);
        let rlen = this.rangeVal.length
        if (rlen > index) return this.rangeVal[index]
        return this.rangeVal[index % rlen];
    }
	
	

}

export default MyScaleOrdinal;

ScalePie

// 饼图角度
class  MyScalePie{
    constructor() {
        this.arcData = []
        this.hasValueFunc =  false
        this.valueFunc = null
    }
    value(fn) {
        this.hasValueFunc = true;
        this.valueFunc = fn
        return this
    }
    getArcs(arr) {
        try {
            if (!Array.isArray(arr)) throw  'getArcs 参数为非数组'
            this.arcData = [...arr]
            this.arcData.sort((a, b) =>{
                if (this.hasValueFunc) return this.valueFunc(b) - this.valueFunc(a)
                return b - a
            })
            let totals  = this.arcData.reduce((total, num) => {
                if ( this.hasValueFunc) return total + this.valueFunc(num)
                return total + num
            },0)
            let result = []
            let resObj = {}
            let resValue = 0
            let lastAngle = 0
            let currentAngle = 0
            this.arcData.forEach((num)=>{
                resValue = num
                if ( this.hasValueFunc)  resValue = this.valueFunc(num)
                currentAngle = lastAngle + resValue / totals * Math.PI * 2
                resObj = {
                    data: num,
                    value: resValue,
                    startAngle: lastAngle,
                    endAngle: currentAngle
                }
                result.push(resObj)
                lastAngle = currentAngle
            })
            return  result
        } catch (e) {
            console.error(e)
        }
        return []
    }

}

export default MyScalePie

效果

经常使用的是 Echart 可视化,对它的数据配置以及新版的颜色认可。便粗略仿照它的样子去实现。效果如下:

柱状图:

在这里插入图片描述

在这里插入图片描述 饼图: 在这里插入图片描述 折线图 在这里插入图片描述 雷达图 在这里插入图片描述 仪表盘 在这里插入图片描述