900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 手把手教你在微信小程序中使用canvas绘制天气折线图(code)

手把手教你在微信小程序中使用canvas绘制天气折线图(code)

时间:2023-09-28 03:40:54

相关推荐

手把手教你在微信小程序中使用canvas绘制天气折线图(code)

微信小程序|小程序开发

微信小程序,canvas,天气折线图

微信小程序-小程序开发

微信小程序中如何绘制天气折线图?下面本篇文章就来给大家介绍一下在微信小程序中使用canvas绘制天气折线图的方法,以及使用三阶贝塞尔曲线拟合温度点,使之变得圆滑,曲线底部有背景色,希望对大家有所帮助!

微博客源码下载,vscode 设置代码类型,ubuntu,安装tomcat下载,sqlite 最大记录数,不属于web前端框架的是,初学者网络爬虫视频大全,xampp php环境,seo排名工具站长,网站源代码上传,酷炫全屏网页,discuz模板手册lzw

禾匠商城小程序源码,双ssd装ubuntu,tomcat跳转到空白页,vps运行爬虫,php 开元内容管理框架,seo权重站lzw

折线

电影网站源码加教程,ubuntu怎么删除ftp,数据爬虫的背景,php 集群用户,seo运营规则lzw

效果图:

自定义组件 line-chart

Component({ externalClasses: [line-class], properties: { width: String, height: String, data: Array, }, observers: { width() {// 这里监听 width 变化重绘 canvas// 动态传入 width 好像只能这样了..const query = this.createSelectorQuery();query .select(#line) .fields({ node: true, size: true }) .exec(res => {const canvas = res[0].node;const ctx = canvas.getContext(2d);const width = res[0].width; // 画布宽度const height = res[0].height; // 画布高度console.log(`宽度: ${width}, 高度: ${height}`);const dpr = wx.getSystemInfoSync().pixelRatio;canvas.width = width * dpr;canvas.height = height * dpr;ctx.scale(dpr, dpr);// 开始绘图this.drawLine(ctx, width, height, this.data.data); }); }, }, methods: { drawLine(ctx, width, height, data) {const Max = Math.max(...data);const Min = Math.min(...data);// 把 canvas 的宽度, 高度按一定规则平分const startX = width / (data.length * 2), // 起始点的横坐标 X baseY = height * 0.9, // 基线纵坐标 Y diffX = width / data.length, diffY = (height * 0.7) / (Max - Min); // 高度预留 0.2 写温度ctx.beginPath();ctx.textAlign = center;ctx.font = 13px Microsoft YaHei;ctx.lineWidth = 2;ctx.strokeStyle = #ABDCFF;// 画折线图的线data.forEach((item, index) => { const x = startX + diffX * index,y = baseY - (item - Min) * diffY; ctx.fillText(`${item}°`, x, y - 10); ctx.lineTo(x, y);});ctx.stroke();// 画折线图背景ctx.lineTo(startX + (data.length - 1) * diffX, baseY); // 基线终点ctx.lineTo(startX, baseY); // 基线起点const lingrad = ctx.createLinearGradient(0, 0, 0, height * 0.7);lingrad.addColorStop(0, gba(255,255,255,0.9));lingrad.addColorStop(1, gba(171,220,255,0));ctx.fillStyle = lingrad;ctx.fill();// 画折线图上的小圆点ctx.beginPath();data.forEach((item, index) => { const x = startX + diffX * index,y = baseY - (item - Min) * diffY; ctx.moveTo(x, y); ctx.arc(x, y, 3, 0, 2 * Math.PI);});ctx.fillStyle = #0396FF;ctx.fill(); }, },});

data 就是温度数组,如 [1, 2, …]

因为不知道温度数值有多少个,因此这里的 width 动态传入

有个小问题,就是宽度过大的话真机不会显示…

// 获取 scroll-view 的总宽度 wx.createSelectorQuery().select(.hourly).boundingClientRect(rect => { this.setData({scrollWidth: rect.right - rect.left, });}).exec();

小时概述{{item}}

这里写 scroll-x 和 scroll-y,要不会出现绝对定位偏移的问题,也不知道为什么

.scroll { position: relative; height: 150px; width: 100%;}.hourly { display: flex; height: 150px; position: absolute; top: 0;}.hourly > view { min-width: 3.5em; text-align: center;}.line { // 折线图绝对定位到底部 position: absolute; bottom: 0;}

这里使用绝对定位其实是想模拟墨迹天气这种折线图和每一天在一个块内的效果,所以 hourly 要和 scroll-view 等高,canvas 需要定位一下

主要是不知道墨迹天气怎么实现的,只能暂时这样

三阶贝塞尔曲线

效果图

emmm,好像并不怎么圆滑

计算控制点

首先写一个点类

class Point { constructor(x, y) { this.x = x; this.y = y; }}

通过上面这个网站可以知道三阶贝塞尔曲线各个参数的意义

也就是使用 bezierCurveTo 的时候最后一个点是下一个点,前两个是控制点

浓缩一下就是

这里的 a 和 b 可以是任意正数

因此定义一个计算某点的控制点 A 和 B 的方法

/** * 计算当前点的贝塞尔曲线控制点 * @param {Point} previousPoint: 前一个点 * @param {Point} currentPoint: 当前点 * @param {Point} nextPoint1: 下一个点 * @param {Point} nextPoint2: 下下个点 * @param {Number} scale: 系数 */calcBezierControlPoints( previousPoint, currentPoint, nextPoint1, nextPoint2, scale = 0.25) { let x = currentPoint.x + scale * (nextPoint1.x - previousPoint.x); let y = currentPoint.y + scale * (nextPoint1.y - previousPoint.y); const controlPointA = new Point(x, y); // 控制点 A x = nextPoint1.x - scale * (nextPoint2.x - currentPoint.x); y = nextPoint1.y - scale * (nextPoint2.y - currentPoint.y); const controlPointB = new Point(x, y); // 控制点 B return { controlPointA, controlPointB };}

这里 scale 就是 a 和 b,不过将它们的取值相等

但是第一个点没有 previousPoint,倒数第二个点没有 nextPoint2

因此当点是第一个的时候,使用 currentPoint 代替 previousPoint

当倒数第二个点的时候,使用 nextPoint1 代替 nextPoint2

至于最后一个点,不需要做任何事,因为 bezierCurveTo 第三个参数就是下一个点,只需要提供坐标就能连起来,不需要计算控制点

因此绘制三阶贝塞尔曲线的方法:

/** * 绘制贝塞尔曲线 * ctx.bezierCurveTo(控制点1, 控制点2, 当前点); */drawBezierLine(ctx, data, options) { const { startX, diffX, baseY, diffY, Min } = options; ctx.beginPath(); // 先移动到第一个点 ctx.moveTo(startX, baseY - (data[0] - Min) * diffY); data.forEach((e, i) => { let curPoint, prePoint, nextPoint1, nextPoint2, x, y; // 当前点 x = startX + diffX * i; y = baseY - (e - Min) * diffY; curPoint = new Point(x, y); // 前一个点 x = startX + diffX * (i - 1); y = baseY - (data[i - 1] - Min) * diffY; prePoint = new Point(x, y); // 下一个点 x = startX + diffX * (i + 1); y = baseY - (data[i + 1] - Min) * diffY; nextPoint1 = new Point(x, y); // 下下个点 x = startX + diffX * (i + 2); y = baseY - (data[i + 2] - Min) * diffY; nextPoint2 = new Point(x, y); if (i === 0) {// 如果是第一个点, 则前一个点用当前点代替prePoint = curPoint; } else if (i === data.length - 2) {// 如果是倒数第二个点, 则下下个点用下一个点代替nextPoint2 = nextPoint1; } else if (i === data.length - 1) {// 最后一个点直接退出return; } const { controlPointA, controlPointB } = this.calcBezierControlPoints(prePoint,curPoint,nextPoint1,nextPoint2 ); ctx.bezierCurveTo(controlPointA.x,controlPointA.y,controlPointB.x,controlPointB.y,nextPoint1.x,nextPoint1.y ); }); ctx.stroke();},

【相关学习推荐:小程序开发教学】

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。