今天继续更新在微信小程序上用canvas画价格走势图。上一篇讲了图上主要的部分,也就是折线图的绘制。假如我在我的绘图主函数中只调用了绘制折线图的函数,效果是这样的:
可以说除了作为坐标背景的<image>和折线图本身,这个图显得空落落的。现在我们来在这个图上绘制拐点。
一、绘制出每个拐点的小圆
我们都可以很明显地看到折线图上的拐点,现在我们在拐点处绘制小圆,让拐点变得更明显。
首先,要回答一个问题——怎么才能知道拐点的坐标?显然,拐点坐标并不是一开始就能得到的,而是绘制折线图的过程中计算出来的,所以我在绘制折线图里的函数中收集拐点,折线图画完后,拐点信息也收集完了,最后把拐点信息返回给主函数。所以,我们现在主函数调用完绘制折线图的函数,就能从返回值中获取拐点数据了。可以像我这样:
/**
* 这个是绘图的主函数
* @param {*} arr 每个值组成的数组
* @param {*} xLeft 折线图最左边与画布边界的距离,单位是px,默认为0
* @param {*} xRight 折线图最右边与画布边界的距离,单位是px,默认为0
* @param {*} yBottom 折线图最下边与画布边界的距离,单位是px,默认为0
* @param {*} yTop 折线图最上边与画布边界的距离,单位是px,默认为0
* @param {*} specialIndex 特殊点的下标,默认为-1(默认没有特殊点)
*/
function draw(arr, xLeft = 0, xRight = 0, yBottom = 0, yTop = 0, specialIndex = -1) {
// 获取上下文对象
const context = wx.createCanvasContext('canvas');
// 获取画布的宽高
const width = _this.data.canvasWidth;
const height = _this.data.canvasHeight;
// 记录图形的边界
const borderInfo = { xLeft, xRight, yBottom, yTop }
// 1.画折线图,传入数据,主色调,画布宽高,图形边界和特殊点位置
// 获得返回的拐点数组
const pointArray = drawFoldLine(context, arr, mainColor, width, height, borderInfo, specialIndex)
// 主函数的其他代码...
}
接下来就比较简单了,遍历拐点数组,把每个拐点的x和y坐标拿到,就以此为圆心,调用context.arc()来画圆(参数为圆心x,y,半径大小,圆弧的起点和终点,是否逆时针绘制),填充白色(为了挡住拐点处的折线),描上橙色的边。我再来说一个点,特殊点的位置画出来的圆半径更大、线条更粗,而其他的圆半径和线条正常,这个区别要怎么处理呢?我会在调用arc方法之前,先判断当前是否是特殊点,如果是,则让半径乘以1.8再来画圆,否则不修改半径,直接画圆。
二、只有小圆带有阴影
仅仅画好了每个拐点处的小圆本身,还不够,我看到UI设计图上,每个小圆是有阴影的。现在我们来为小圆画上阴影。
现在又要回答一个问题,canvas要怎么绘制阴影呢?我查阅文档之后给出了我的回答——使用context.setShadow方法,前两个参数表示阴影在x和y方向上的平移,第三个参数表示阴影的模糊半径,第四个参数是阴影的颜色。OK,那么我在调用context.arc画圆之前就调用context.setShadow吧,so easy!效果如下图所示,小圆上有向右向下的半透明红色阴影。
但是,还有一个不容忽视的问题!我在后来绘制价格文字的时候傻眼了,因为文字也带上了阴影!这该怎么办呢?我找遍了文档,也没有找到取消阴影的API,也就是说,context.setShadow调用后,后续画出来的东西都会带有这样的阴影!可我们的需求并不是这样的呀。文字上并没有带有红色的阴影。这该怎么办呢?
某部动画片里有一句话叫“用魔法才能打败魔法”,我觉得可以用在这里——“用阴影才能打败阴影”,换句话说,只有设置成那样的阴影,才能不在后续的绘图中显示这样的阴影。我还是没说明白吗?——如果你把“那样”的阴影设置成“不偏移,不模糊”的阴影,那么阴影是不是就与我们绘制出来的图像完全重合,无法被看见了呢?这就是我去掉阴影的办法。(此处非常感谢我的一位同事,是他想到了这个办法。)
所以,综上所述,我们应该在绘制小圆之前,调用context.setShadow,设置出这样的红色阴影,然后调用context.arc画出小圆,之后再次调用context.setShadow把阴影设为“不偏移,不模糊”,再去绘制其他东西,就可以满足需求了。
最后,来贴一下我绘制拐点的函数的代码:
/**
* 绘制拐点处的小圆
* @param {*} context 上下文对象
* @param {*} color 使用的颜色
* @param {*} center 圆心的信息
* @param {*} r 半径
* @param {*} isSpecial 是否特殊点
*/
function drawInflectionPoint(context, color, center, r, isSpecial) {
context.beginPath();
// 特殊情况半径更大,线条更粗
if (isSpecial) {
r = r * 1.8;
context.lineWidth = 6 * _this.data.toPx;
} else {
context.lineWidth = 4 * _this.data.toPx;
}
// 设置阴影,再画圆
context.setShadow(1, 2, 3, color);
context.arc(center.x, center.y, r * _this.data.toPx, 0, 2 * Math.PI);
context.fillStyle = "#ffffff";
context.fill();
context.stroke();
// draw传入true,保留上一次的结果
context.draw(true);
context.closePath();
// 去掉阴影,再画其他的
context.setShadow(0, 0, 0, color);
}
非常感谢大家的阅读!下一篇准备写这个图里的价格和拼团人数,重点应该是最低价那里的胶囊图。