• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • dedecms
  • ecshop
  • z-blog
  • UcHome
  • UCenter
  • drupal
  • WordPress
  • 帝国cms
  • phpcms
  • 动易cms
  • phpwind
  • discuz
  • 科汛cms
  • 风讯cms
  • 建站教程
  • 运营技巧
您的位置:首页 > CMS教程 >建站教程 > 手把手教你在微信小程序中使用canvas绘制天气折线图(附代码)

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

作者:站长图库 字体:[增加 减小] 来源:互联网 时间:2022-04-29

站长图库向大家介绍了微信小程序开发,小程序canvas绘制,canvas天气折线图等相关知识,希望对您有所帮助

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


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


折线

效果图:


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

自定义组件 line-chart

<canvas type="2d" id="line" class="line-class" style="width:{{width}}px;height:{{height}}px" />
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, 'rgba(255,255,255,0.9)');      lingrad.addColorStop(1, 'rgba(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();
<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,要不会出现绝对定位偏移的问题,也不知道为什么


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


.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 需要定位一下

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

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


三阶贝塞尔曲线

效果图


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

emmm,好像并不怎么圆滑

计算控制点

首先写一个点类

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

Canvas贝塞尔曲线绘制工具 (karlew.com)

http://wx.karlew.com/canvas/bezier/

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

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

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

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

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

浓缩一下就是

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

这里的 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

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

至于最后一个点,不需要做任何事,因为 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();},


分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 手把手教你在微信小程序中使用canvas绘制天气折线图(附代码)
  • 浅析微信小程序中自定义组件的方法

相关文章

  • 2022-04-29怎么用php实现支付宝支付
  • 2022-04-29WordPress主题RiPro美化-给特定分类添加VIP权限才可访问效果
  • 2022-04-29css如何实现适配iphone全面屏
  • 2022-04-29微信小程序中echarts的用法和可能遇见的坑,快来收藏避雷!!
  • 2022-04-29详解PHP如何高效导出Excel(CSV)
  • 2022-04-29实战分享:利用nodejs​爬取并下载一万多张图片
  • 2022-04-29一分钟介绍WordPress文章怎么添加排序选项
  • 2022-04-29分享一个Golang Http 验证码示例
  • 2022-04-29PS绘制苹果APP应用金属相机图标
  • 2022-04-29微信小程序自定义菜单导航实现楼梯效果

文章分类

  • dedecms
  • ecshop
  • z-blog
  • UcHome
  • UCenter
  • drupal
  • WordPress
  • 帝国cms
  • phpcms
  • 动易cms
  • phpwind
  • discuz
  • 科汛cms
  • 风讯cms
  • 建站教程
  • 运营技巧

最近更新的内容

    • 列举论坛最常见的SEO优化问题及解答
    • 说说PHP太空船运算符的使用场景
    • 什么是依赖注入?在Angular中怎么实现?
    • 详解如何​利用WordPress自带短代码添加视频
    • 织梦cms内容页调用评论数量的方法
    • 不用修改dedecms模板文章内容页直接点图片进入下
    • 一起看看JavaScript如何获取页面上被选中的文字
    • PHP在图片中用 imagettftext() 添加水印(图文详解)
    • 你知道Thinkphp5.1让验证码在Electron-vue中怎么用?
    • Node项目中用images+imageinfo库给图片批量添加水印

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有