窥天机,握风云:七日天象尽在掌握

157 阅读1分钟

你可曾思索,为何天气预报员总能在风雷雨雪间笃定自若,仿若洞悉天机?如今,轮到你了!借助 ECharts,绘制一幅玄妙的七日天象图,分分钟登上掘金,化身掌控风云的“气象秘师”。

且看下图!

不吹牛逼了, 其实就是一个echart图表(狗头保命)

426e60fc1b2008a7385570a901aebc7.png

功能介绍

  1. 每日日期。
  2. 风力等级。
  3. 白天的天气图标。
  4. 每日的最高气温和最低气温。

开搞!用代码说话

以下是项目的核心代码逻辑分解:

<template>
  <div class="three-echart-height">
    <Echart :options="waterOptions" />
  </div>
</template>

具体echart组件自行封装就行

使用的是和风天气免费api获取数据

methods: {
  async getWeather() {
    const key = 'YOUR_API_KEY';
    const location = '根据需要填写';
    
    try {
      const res = await axios.get('https://geoapi.qweather.com/v2/city/lookup?', { params: { key, location } });
      if (res.data.code === '200') {
        const locationId = res.data.location[0].id;
        const weatherData = await this.fetchWeatherData(locationId, key);
        this.processDailyWeather(weatherData.daily);
      }
    } catch (error) {
      console.error('Error', error);
    }
  }
}

更新数据

processDailyWeather(dailyData) {
  this.dateList = dailyData.map(item => item.fxDate.slice(5));
  this.tempMaxs = dailyData.map(item => item.tempMax);
  this.tempMins = dailyData.map(item => item.tempMin);
  this.iconDays = dailyData.map(item => item.iconDay);

  this.waterOptions.xAxis[0].data = this.dateList;
  this.waterOptions.series[0].data = this.tempMaxs;
  this.waterOptions.series[1].data = this.tempMins;
}

添加天气图标, 需要提前下载好和风天气的icon图标,我这里是ui设计师重新设计的新图标

updateIconLabels() {
  this.iconDays.forEach(val => {
    this.waterOptions.xAxis[1].axisLabel.rich[val] = {
      backgroundColor: {
        image: require(`@/assets/qwIcons/${val}-fill.svg`)
      },
      height: 30,
      width: 30
    };
  });
}

以下是options配置项

export const waterOptions = {
  grid: {
    show: true,
    backgroundColor: 'transparent',
    opacity: 0.3,
    borderWidth: '0',
    top: '130',
    bottom: '50'
  },
  tooltip: {
    trigger: 'axis'
  },
  legend: {
    show: false
  },
  xAxis: [
    // 日期
    {
      type: 'category',
      boundaryGap: false,
      position: 'top',
      offset: 90,
      zlevel: 100,
      axisLine: {
        show: false
      },
      axisTick: {
        show: false
      },
      axisLabel: {
        formatter: [
          '{a|{value}}'
        ].join('\n'),
        rich: {
          a: {
            color: 'white',
            fontSize: 14,
          }
        }
      },
      nameTextStyle: {

      },
      data: ["8/25","8/26","8/27","8/28","8/29","8/30","8/31"]
    },
    // 天气图标
    {
      type: 'category',
      boundaryGap: false,
      position: 'top',
      offset: 30,
      zlevel: 100,
      axisLine: {
        show: false
      },
      axisTick: {
        show: false 
      },
      axisLabel: {
        formatter: function(value, index) {
          return `{${value}|}`;
        },
        rich: {
          0: {
            backgroundColor: {
              image: 'https://xxx.png'
            },
            height: 40,
            width: 40
          },
          b: {
            color: 'white',
            fontSize: 12,
            lineHeight: 30,
            height: 20
          }
        }
      },
      nameTextStyle: {
        fontWeight: 'bold',
        fontSize: 19
      },
      data: ["小雨","小雨","阴","小雨","多云","小雨","小雨"]
    },
    // 风级
    {
      type: 'category',
      boundaryGap: false,
      position: 'bottom',
      offset: 18,
      zlevel: 100,
      axisLine: {
        show: false
      },
      axisTick: {
        show: false
      },
      axisLabel: {
        interval: 0,
        formatter: function (value) {
          return `{b|}` + `{value|${value}级}`;
        },
        rich: {
          b: {
            backgroundColor: {
              image: require('@/assets/images/windLevel.png')
            },
            height: 12,
            width: 12
          },
          value: {
            color: '#fff',
            fontSize: 12,
          },
        }
      },
      data: ["<3",">3","1","2","3","4","1"]
    }
  ],
  yAxis: {
    type: 'value',
    show: false,
    axisLabel: {
      formatter: '{value} °C',
      color: 'white',
    },
    axisLine: {
      show: true
    },
  },
  series: [
    {
      name: '最高气温',
      type: 'line',
      data: ["16.3","16.2","20.6","14.2","17.6","15.7","14.3"],
      symbol: 'emptyCircle',
      symbolSize: 6,
      showSymbol: true,
      smooth: true,
      itemStyle: {
        normal: {
          color: '#F38200'
        }
      },
      label: {
        show: true,
        position: 'top',
        color: 'white',
        fontSize: 14,
        formatter: '{c} °C'
      },
      lineStyle: {
        width: 1,
        // color: 'white'
      },
      areaStyle: {
        opacity: 1,
        color: 'transparent'
      }
    },
    {
      name: '最低气温',
      type: 'line',
      data: ["0","-2","-5","12.5","12.4","13.2","13"],
      symbol: 'emptyCircle',
      symbolSize: 6,
      showSymbol: true,
      smooth: true,
      itemStyle: {
        normal: {
          color: '#178FFF'
        }
      },
      label: {
        show: true,
        position: 'bottom',
        color: 'white',
        fontSize: 14,
        formatter: '{c} °C'
      },
      lineStyle: {
        width: 1,
      },
      areaStyle: {
        opacity: 1,
        color: 'transparent'
      }
    },
  ]
}

完整代码如下:

<template>
  <div class="three-echart-height">
      <Echart :options="waterOptions"></Echart>
  </div>
</template>
 
<script>
import Echart from '@/components/commonEchart/index.vue'
import { waterOptions } from './echartOptions.js'
import axios from 'axios'

export default {
  components: {
    Echart
  },
  data() {
    return {
      waterOptions,
      dateList: [], // 日期
      windScaleDays: [], // 风力等级
      tempMaxs: [], // 最高温
      tempMins: [], // 最低温
      iconDays: [], // 白天天气图标
      nowWeather: '' // 当前天气
    };
  },
  created() {
    this.getWeather();
  },
  methods: {
    async getWeather() {
      const key = 'YOUR_API_KEY';
      const params = {
        key,
        location: '根据需要填写',
      };

      try {
        // 获取城市 location 值
        const res = await axios.get('https://geoapi.qweather.com/v2/city/lookup?', { params });
        if (res.data.code === '200') {
          const locationId = res.data.location[0].id;
          const weatherData = await this.fetchWeatherData(locationId, key);

          if (weatherData.daily) {
            this.processDailyWeather(weatherData.daily);
          }
          if (weatherData.now) {
            this.nowWeather = weatherData.now;
          }
        }
      } catch (error) {
        console.error('Error', error);
      }
    },

    async fetchWeatherData(locationId, key) {
      const params = { key, location: locationId };
      try {
        const [dailyResponse, nowResponse] = await Promise.all([
          axios.get('https://devapi.qweather.com/v7/weather/7d?', { params }),
          axios.get('https://devapi.qweather.com/v7/weather/now?', { params })
        ]);

        return {
          daily: dailyResponse.data.code === '200' ? dailyResponse.data.daily : null,
          now: nowResponse.data.code === '200' ? nowResponse.data.now : null
        };
      } catch (error) {
        console.error('Error fetching individual weather data:', error);
        return {};
      }
    },

    processDailyWeather(dailyData) {
      this.dateList = dailyData.map(item => item.fxDate.slice(5));
      this.windScaleDays = dailyData.map(item => item.windScaleDay);
      this.tempMaxs = dailyData.map(item => item.tempMax);
      this.tempMins = dailyData.map(item => item.tempMin);
      this.iconDays = dailyData.map(item => item.iconDay);

      // 更新图表数据
      this.waterOptions.xAxis[0].data = this.dateList;
      this.waterOptions.xAxis[2].data = this.windScaleDays;
      this.waterOptions.series[0].data = this.tempMaxs;
      this.waterOptions.series[1].data = this.tempMins;
      this.waterOptions.xAxis[1].data = this.iconDays;

      this.updateIconLabels();
    },

    updateIconLabels() {
      this.iconDays.forEach(val => {
        this.waterOptions.xAxis[1].axisLabel.rich[val] = {
          backgroundColor: {
            image: require(`@/assets/qwIcons/${val}-fill.svg`)
          },
          height: 30,
          width: 30
        };
      });
    }
  }
};
</script>

欢迎各位留言(轻点骂)