在微信小程序中绘制图表(part2)

摘要:
40)=>下部向下查找类型=类型?If(type=='uper'){num=Math.ceil(num);}else{num=Math.floor(num));==0){If(type=='uper';{num++;a).contat(b.data);}else If(range>
本期大纲

1、确定纵坐标的范围并绘制

2、根据真实数据绘制折线

相关阅读:
在微信小程序中绘制图表(part1)
在微信小程序中绘制图表(part3)

关注我的 github 项目 查看完整代码。

确定纵坐标的范围并绘制

为了避免纵坐标的刻度出现小数的情况,我们把纵坐标分为5个区块,我们取最小单位刻度为例如10(能够被5整除),当然真实情况会比这复杂,待会儿我们再讨论。

所以我们的处理输入输出应该是下面的结果

(5, 34.1)  => (10, 40)
(10, 34)   => (10, 40)
(-5.1, 40) => (-10, 40)
// 确定Y轴取值范围
function findRange (num, type, limit) {
    limit = limit || 10;
    
    // upper向上查找,lower向下查找
    type = type ? type : 'upper';

    // 进行取整操作,避免while时进入死循环
    if (type === 'upper') {
        num = Math.ceil(num);
    } else {
        num = Math.floor(num);
    }
    while (num % limit !== 0) {
        if (type === 'upper') {
            num++;
        } else {
            num--;
        }
    }

    return num;
}

好了,初步的确定范围已经完成了,但是细想一下这个范围还是不是很理想,比如用户传入的数据都是小数级别的,比如 (0.2, 0.8),我们输出的范围是(0, 5)这个范围偏大,图表展现的效果则会是上面有大部分的留白,同样用户输入的数据很大,比如(10000, 18000),我们得到的范围是(10000, 18010),这个范围则没什么意义,所以我们需要根据传入的数据的范围来分别确定我们的最小单位刻度。

规定我们的参数格式是这样的:

opts = {
    ...
    series: [{
            ...
            data: [15, 20, 45, 37, 4, 80]
        }, {
            ...
            data: [70, 40, 65, 100, 34, 18]
        }
    ]
}

让我们继续进行优化

// 合并数据,将series中的每项data整合到一个数组当中
function dataCombine(series) {
    return series.reduce(function(a, b) {
        return (a.data ? a.data : a).concat(b.data);
    }, []);
}

// 根据数据范围确定最小单位刻度
function getLimit (maxData, minData)
    var limit = 0;
    var range = maxData - minData;
    if (range >= 10000) {
        limit = 1000;
    } else if (range >= 1000) {
        limit = 100;
    } else if (range >= 100) {
        limit = 10;
    } else if (range >= 10) {
        limit = 5;
    } else if (range >= 1) {
        limit = 1;
    } else if (range >= 0.1) {
        limit = 0.1;
    } else {
        limit = 0.01;
    }
}

var dataList = dataCombine(opts.series);
// 获取传入数据的最小值
var minData = Math.min.apply(this, dataList);
// 获取传入数据的最大值
var maxData = Math.max.apply(this, dataList);

var limit = getLimit(maxData, minData);

var minRange = findRange(minData, 'lower', limit);
var maxRange = findRange(maxData, 'upper', limit);

现在我们动态的确定除了合适的最小刻度范围,接下来我们接着优化一下上面的findRange方法,主要是增加对小数的支持

function findRange (num, type, limit) {
    limit = limit || 10;
    type = type ? type : 'upper';
    var multiple = 1;
    while (limit < 1) {
        limit *= 10;
        multiple *= 10;
    }
    if (type === 'upper') {
        num = Math.ceil(num * multiple);
    } else {
        num = Math.floor(num * multiple);
    }
    while (num % limit !== 0) {
        if (type === 'upper') {
            num++;
        } else {
            num--;
        }
    }

    return num / multiple;
}

现在我们已经确定好了Y轴的取值范围,关于如何画出Y轴可以参看 part1 中X轴的绘制方法,此处不再累赘。

Y轴效果图:

opts = {
    ...
    series: [{
            ...
            data: [15, 20, 45, 37, 4, 80]
        }, {
            ...
            data: [70, 40, 65, 100, 34, 18]
        }
    ]
}

clipboard.png

opts = {
    ...
    series: [{
            ...
            data: [0.15, 0.2, 0.45, 0.37, 0.4, 0.8]
        }, {
            ...
            data: [0.30, 0.37, 0.65, 0.78, 0.69, 0.94]
        }
    ]
}

clipboard.png

效果还不错,我们接着往下

根据真实数据绘制折线

问题的关键在于确定每个数据点的(x, y)坐标,x坐标比较好确定,我们根据画布的宽度以及opts.categories即可确定。

规定我们的配置为:

config = {
    xAxisHeight: 30, // X轴高度
    yAxisWdith: 30   // Y轴宽度
}
var data = [15, 20, 45, 37, 4, 80];
var xPoints = [];
var validWidth = opts.width - config.yAxisWidth;
var eachSpace = validWidth / opts.categories.length;
var start = config.yAxisWidth;

data.forEach(function (item, index) {
    xPoints.push(start + (index + 0.5) * eachSpace);
});

y坐标稍微会复杂一点,需要根据Y轴的范围已经本身的数值进行计算得出。

clipboard.png

所以我们计算出的y应该为

y = validHeight * (data - min) / (max - min);
// 由于canvas画布是左上角为原点坐标,故我们变化一下
// 得到最终的y绘制点
y = valideHeight - y;

代码如下:

var data = [15, 20, 45, 37, 4, 80];
var yPoints = [];
var validHeight = opts.height - config.xAxisHeight;
data.forEach(function(item) {
    var y = validHeight * (item - min) / (max - min);
    y = validHeight - y;

    yPoints.push(y);
}

现在我们已经确定了数据点在画布上的绘制坐标,关于如何绘制折现请查看 part1 中相关内容,此处不再累赘。

最终效果图如下:

clipboard.png

预告:下一部分我们一起讨论绘制过程中的一些技巧、动画效果和如何工程化我们的项目。

相关阅读

在微信小程序中绘制图表(part1)
在微信小程序中绘制图表(part3)

本文转载于猿2048:在微信小程序中绘制图表(part2)

免责声明:文章转载自《在微信小程序中绘制图表(part2)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇git 安装 和 基本操作架构之微服务设计(Nginx + Upsync)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

服务器推送的实现—基于EventSource

一、服务器推送理解   首先要知道为什么使用服务器推送,回答这个问题其实就是相当于回答,服务器推送的优点,可以从两个方面来思考: 1.1 服务器推送的目的   及时的将客户端感兴趣的数据推送给它。 1.2 不是用服务器推送怎么来实现需求   不使用服务端推送,那就只能由客户端定期对服务器发送请求,来获取是否有需要的数据。这样做有几个缺点: 不能及时的获取...

wordCount的执行流程

  我们对于wordCount的这个流程,在清晰不过了,不过我们在使用spark以及hadoop本身的mapReduce的时候,我们是否理解其中的原理呢,今天我们就来介绍一下wordCount的执行原理,   1.首先我们都会这样子执行(wordCount执行在hadoop中)   val rdd = sc.textFile("hdfs://weekday...

Cesium 拾取 API 完全总结

先讲怎么用,再讲大致原理。 1 拾取坐标 > 仅拾取椭球体表面坐标 使用 Camera.prototype.pickEllipsoid 方法,接受一个必选的屏幕坐标,返回一个三维世界坐标 Cartesian3。 > 拾取带地形高度的地表坐标 使用 Globe.prototype.pick 方法。需要事先使用 Camera.prototype.g...

ArcGIS Server的切图原理深入

GoogleMap,Virtual Earth,YahooMap等,目前所有的WebGIS都使用了缓存机制以提高地图访问速度。原理都是将地图设定为多个比例尺,对于每个比例尺提前将地 图分成若干小图片,存在服务器上,客户端访问时直接获取需要的小图片拼接成地图,而不是由服务器动态创建出一幅图片来送到客户端,极大程度的提高了反问速 度。好比外面卖菠萝,和自己买一...

vue 数据(data)赋值问题

总结一下我遇到的一个纠结很久的问题。 在项目中需要用到后台的数据对前端渲染,使用到了vue整合的axios,使用vue中的钩子函数在页面组件挂载完成之后向后台发送一个get请求然后将返回后的数据赋值data()中定义的属性: 执行后前端报错: 原因: 在请求执行成功后执行回调函数中的内容,回调函数处于其它函数的内部this不会与任何对象绑定,为und...

二、Metrics指标类型

Prometheus 的客户端库中提供了四种核心的指标类型。但这些类型只是在客户端库(客户端可以根据不同的数据类型调用不同的 API 接口)和在线协议中,实际在 Prometheus server 中并不对指标类型进行区分,而是简单地把这些指标统一视为无类型的时间序列 2.1、Counter (计数器) ​ Counter 类型代表一种样本数据单调递增的指...