升级补丁
手把手教你在微信小程序中使用canvas绘制天气折线图(附代码)-小程序开发
来源:祺点源码论坛     阅读:3
网站管理员
发布于 2024-09-11 12:27
查看主页

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

折线

效果图:

自定义组件 line-chart

<canvas type="2d" id="line" class="line-class" style="width:{{width}}px;height:{{height}}px" />登录后复制Component({ externalClasses: [&#39;line-class&#39;], properties: { width: String, height: String, data: Array, }, observers: { width() { // 这里监听 width 变化重绘 canvas // 动态传入 width 好像只能这样了.. const query = this.createSelectorQuery(); query .select(&#39;#line&#39;) .fields({ node: true, size: true }) .exec(res => { const canvas = res[0].node; const ctx = canvas.getContext(&#39;2d&#39;); 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 = &#39;center&#39;; ctx.font = &#39;13px Microsoft YaHei&#39;; ctx.lineWidth = 2; ctx.strokeStyle = &#39;#ABDCFF&#39;; // 画折线图的线 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, &#39;rgba(255,255,255,0.9)&#39;); lingrad.addColorStop(1, &#39;rgba(171,220,255,0)&#39;); 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 = &#39;#0396FF&#39;; ctx.fill(); }, },});登录后复制

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

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

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

// 获取 scroll-view 的总宽度 wx.createSelectorQuery() .select(&#39;.hourly&#39;) .boundingClientRect(rect => { this.setData({ scrollWidth: rect.right - rect.left, }); }) .exec();登录后复制<view class="title">小时概述</view><scroll-view scroll-x scroll-y class="scroll" show-scrollbar="{{false}}" enhanced="{{true}}"> <view class="hourly"> <view wx:for="{{time}}" wx:key="index">{{item}}</view> </view> <line-chart line-class="line" width="{{scrollWidth}}" height="100" data="{{temp}}" /></scroll-view>登录后复制

这里写 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; }}登录后复制Canvas贝塞尔曲线绘制工具 (karlew.com)http://wx.karlew.com/canvas/bezier/

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

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

控制点的计算参考: 贝塞尔曲线控制点确定的方法 - 百度文库

https://wenku.baidu.com/view/c790f8d46bec0975f565e211.html

浓缩一下就是

这里的 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();},登录后复制

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

以上就是手把手教你在微信小程序中使用canvas绘制天气折线图(附代码)的详细内容!

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 升级补丁
相关推荐
微信怎么投诉噪音扰民 微信投诉噪音扰民教程-手机软件
探索通马桶工具的创新发展(助力卫生)-安卓手机
2024年09月23日Bonk币价格_Bonk币今日最新价格-web3.0
金铲铲之战法转铁男阵容玩法详解-游戏攻略
《潜行者2》全新截图 夜色虽美但行动要注意-主机专区

首页

消息

购物车

我的