使用Canvas组件来绘制多日天气气温曲线

该天气曲线基于和风天气Api中天气数据,在进行气温曲线绘制前务必完成天气数据的请求获取相关代码

数据模型和天气请求方法由于篇幅原因暂时省略,大家可以自己尝试编写或者直接设定一组数据来做。

需要引入的库(仅供参考)

import {DailyWeather} from '../bean/DailyWeather/DailyWeather'  //数据模型
import {daily} from '../bean/DailyWeather/daily'    //数据模型
import getweatherUtil from '../Util/getWeatherUtil'    //获取天气数据方法
//仅供参考

数据获取

private locationId: string = '101010100'  // 初始化位置id(用于获取天气数据),后续可在主页面中传入数据进行修改

  //本次曲线绘制所需要的数据模型
  @State FutureWeather: DailyWeather = new DailyWeather()
  @State FutureWeatherData: Array<daily> = []    //以上模型已事先定义好,只想实现气温曲线功能可以省略这两个模型

  @State tempMax : number[] = []  
  @State tempMin : number[] = []
  @State weatherDates : string[] = []
  @State weatherConditions : string[] = []
  //这些数据会在页面初始化后获取数据填入,如果没有写数据获取方法可在这一步直接填写数据。

  //页面初始化
  aboutToAppear(): void {
    this.initDate()
  }

  //初始化方法
  async initDate() {

    //使用准备好的方法获取
    let result1: DailyWeather = await getweatherUtil.getDailyWeather_1(this.locationId)
    this.FutureWeather = result1
    this.FutureWeatherData = result1.daily
    console.log("Date_test"+this.FutureWeatherData[0].fxDate)

    let i:number
    for( i=0; i<this.FutureWeatherData.length ; i++){
      this.tempMax[i] = Number(this.FutureWeatherData[i].tempMax)
      this.tempMin[i] = Number(this.FutureWeatherData[i].tempMin)
      this.weatherDates[i] = this.DateChange(this.FutureWeatherData[i].fxDate)
      this.weatherConditions[i] = this.FutureWeatherData[i].textDay
    }


  }

其他方法

//获取天气图标,该方法返回的是图标的本地URl
weatherUrl(weather: string) {

    if (weather === '晴') {
      return "resources/base/media/tianqi_qing.png"

    }

    else if (weather === '阴') {
      return "resources/base/media/tianqi_yintian.png"

    }

    else if (weather === '多云') {
      return "resources/base/media/tianqi_duoyun.png"

    }

    else if (weather.includes("雨")) {
      return "resources/base/media/tianqi_zhongyu.png"

    }
    else return "error"

  }

//获取日期并将日期转换为XX/XX格式
  formatDate(date: Date): string {
    // 获取年、月、日等部分
    let year = date.getFullYear();
    let month = (date.getMonth() + 1).toString().padStart(2, '0');
    let day = date.getDate().toString().padStart(2, '0');

    // 组合成所需的日期格式
    return `${month}/${day}`;
  }

  DateChange(dates:string):string{

    let dateString: string = dates

    // 使用Date对象解析时间字符串
    let date = new Date(dateString);

    // 创建一个格式化日期时间的函数


    // 调用函数并打印结果
    let formattedTime:string = this.formatDate(date);
    return formattedTime 
  }

定义Canvas画布属性

 //定义边距
  sideMargin: number = 40; 

  settings: RenderingContextSettings = new RenderingContextSettings(true);   
  //settings配置,这个ture为启用alpha通道
  context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);   
  //创建画布对象
 
  canvasHeight: number = 200;   //画布高度
  itemWidth: number = 80;    //最高温最低温之间距离

组件搭建

build(){
     Column(){
       Row(){
         Column(){
           Text("多日预报")
             .textAlign(TextAlign.Start)
             .fontSize(15)
             .fontColor(Color.White)
             .opacity(0.5)
             .width("100%")
             .margin({top:20,left:30})

           Blank()

           //开始曲线的绘制
           Scroll(){
             Canvas(this.context)
               .width(this.tempMax.length * this.itemWidth + this.itemWidth + this.sideMargin * 2)
               .height("85%")
               .onReady(()=>{
                 //找出高温曲线的最大值和低温曲线的最小值
                 const maxTemp: number = Math.max(...this.tempMax)
                 const minTemp: number = Math.max(...this.tempMin)
                 //每改变一度,曲线变化幅度
                 const step: number = this.canvasHeight*0.4/(maxTemp - minTemp)
                 const curveChartMargin: number = this.canvasHeight * 0.09

                 //初始化属性
                 this.context.lineWidth = 2
                 this.context.font = '13vp sans-serif'
                 this.context.fillStyle = 'rgba(255,255,255,0.6)'
                 this.context.strokeStyle = 'rgba(255,255,255,0.5)'
                 this.context.textAlign = 'start'

                 //设置x坐标
                 let xPos: number[] = []
                 let tempMaxPos: number[] = []
                 let tempMinPos: number[] = []

                 for(let i: number = 0;i<this.tempMax.length;i++){	
                   //遍历数组确定高低温温度点的坐标并存储
                   let x: number = this.sideMargin + (i * this.itemWidth)
                   let yHeight: number = this.canvasHeight - (curveChartMargin + (this.tempMax [i] - minTemp) * step)+20
                   let yLow: number = this.canvasHeight - (curveChartMargin + (this.tempMin[i] - minTemp) * step)

                   //存放数据
                   xPos.push(x)
                   tempMaxPos.push(yHeight)
                   tempMinPos.push(yLow)

                   //画出每个结点
                   this.context.fillStyle = "white"
                   let region: Path2D = new Path2D()
                   region.ellipse(x, yHeight, 4, 4, 0, 0, Math.PI * 2)
                   region.ellipse(x, yLow, 4, 4, 0, 0, Math.PI * 2)
                   this.context.fill(region)

                   //绘制日期
                   this.context.fillStyle = "rgba(255,255,255,0.6)"
                   this.context.font = '60px Arial'
                   let dateStr: string = this.weatherDates[i]
                   this.context.fillText(dateStr,x - this.context.measureText(dateStr).width / 2,15)

                   //绘制天气图标
                   let iconUrl: string = this.weatherUrl(this.weatherConditions[i])
                   let imgIcon = new ImageBitmap(iconUrl)
                   this.context.drawImage(imgIcon, x - this.context.measureText(dateStr).width/2+10,30,30,35)

                   //绘制天气状态
                   this.context.fillStyle = "white"
                   this.context.font = '55px Arial'
                   let highConditionStr: string = this.weatherConditions[i]
                   this.context.fillText(highConditionStr, x-this.context.measureText(highConditionStr).width/2,90)

                   //绘制高温低温的文字
                   this.context.fillStyle = "white"
                   this.context.font = '55px Arial'
                   let maxTemperatureStr: string = `${this.tempMax[i]}℃`
                   let minTemperatureStr: string = `${this.tempMin[i]}℃`
                   this.context.fillText(maxTemperatureStr, x-this.context.measureText(maxTemperatureStr).width/2,yHeight-8)
                   this.context.fillText(minTemperatureStr, x-this.context.measureText(minTemperatureStr).width/2,yLow+25)

                 }

                 //绘制高温曲线
                 this.context.fillStyle = "rgba(255,255,255,0.5)"
                 this.context.beginPath()
                 this.context.moveTo(xPos[0],tempMaxPos[0])
                 for(let i: number=1;i<xPos.length;i++){
                   let x0: number = xPos[i-1]
                   let y0: number = tempMaxPos[i-1]
                   //绘制一条三次贝塞尔曲线
                   this.context.bezierCurveTo(x0+(xPos[i]-x0)*0.3,y0,xPos[i]-(xPos[i]-x0)*0.3,tempMaxPos[i],xPos[i],tempMaxPos[i])
                 }
                 this.context.stroke()    //调用stroke方法来实际绘制出路径

                 //绘制低温曲线
                 this.context.beginPath()
                 this.context.moveTo(xPos[0],tempMinPos[0])
                 for(let i: number=1;i<xPos.length;i++){
                   let x0: number = xPos[i-1]
                   let y0: number = tempMinPos[i-1]
                   this.context.bezierCurveTo(x0+(xPos[i]-x0)*0.3,y0,xPos[i]-(xPos[i]-x0)*0.3,tempMinPos[i] ,xPos[i],tempMinPos[i])
                 }
                 this.context.stroke()
               })
           }
           .scrollable(ScrollDirection.Horizontal)
           .scrollBar(BarState.Off)
         }
         .width("95%")
         .height("50%")
         .backgroundColor('rgba(255, 255, 255, 0.1)')
         .borderRadius(20)
         .margin({top:20})
       }
     }
  }

效果如下

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值