react+echarts利用多X轴实现7天天气预报

166 阅读6分钟

效果如下: image.png 代码如下:

import option from './option.js';
import * as echarts from "echarts";
import { useEffect,useRef } from "react";

const App = () => {
    const chartRef = useRef(null);
    useEffect(() => {
        init()
    }, [])
    // 数据来自和风API
    let data = [
        {
            "fxDate": "2024-12-29",
            "sunrise": "07:36",
            "sunset": "16:58",
            "moonrise": "06:07",
            "moonset": "15:03",
            "moonPhase": "残月",
            "moonPhaseIcon": "807",
            "tempMax": "5",
            "tempMin": "-5",
            "iconDay": "101",
            "textDay": "多云",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "270",
            "windDirDay": "西风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "315",
            "windDirNight": "西北风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "26",
            "precip": "0.0",
            "pressure": "1014",
            "vis": "25",
            "cloud": "0",
            "uvIndex": "2"
        },
        {
            "fxDate": "2024-12-30",
            "sunrise": "07:36",
            "sunset": "16:59",
            "moonrise": "07:07",
            "moonset": "15:55",
            "moonPhase": "残月",
            "moonPhaseIcon": "807",
            "tempMax": "7",
            "tempMin": "-5",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "315",
            "windDirDay": "西北风",
            "windScaleDay": "1-3",
            "windSpeedDay": "16",
            "wind360Night": "315",
            "windDirNight": "西北风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "39",
            "precip": "0.0",
            "pressure": "1021",
            "vis": "25",
            "cloud": "0",
            "uvIndex": "2"
        },
        {
            "fxDate": "2024-12-31",
            "sunrise": "07:37",
            "sunset": "16:59",
            "moonrise": "08:01",
            "moonset": "16:57",
            "moonPhase": "新月",
            "moonPhaseIcon": "800",
            "tempMax": "5",
            "tempMin": "-4",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "225",
            "windDirDay": "西南风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "0",
            "windDirNight": "北风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "36",
            "precip": "0.0",
            "pressure": "1019",
            "vis": "25",
            "cloud": "0",
            "uvIndex": "2"
        },
        {
            "fxDate": "2025-01-01",
            "sunrise": "07:37",
            "sunset": "17:00",
            "moonrise": "08:49",
            "moonset": "18:06",
            "moonPhase": "蛾眉月",
            "moonPhaseIcon": "801",
            "tempMax": "5",
            "tempMin": "-5",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "45",
            "windDirDay": "东北风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "0",
            "windDirNight": "北风",
            "windScaleNight": "1-3",
            "windSpeedNight": "16",
            "humidity": "39",
            "precip": "0.0",
            "pressure": "1024",
            "vis": "25",
            "cloud": "25",
            "uvIndex": "2"
        },
        {
            "fxDate": "2025-01-02",
            "sunrise": "07:37",
            "sunset": "17:01",
            "moonrise": "09:28",
            "moonset": "19:18",
            "moonPhase": "蛾眉月",
            "moonPhaseIcon": "801",
            "tempMax": "3",
            "tempMin": "-8",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "315",
            "windDirDay": "西北风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "270",
            "windDirNight": "西风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "21",
            "precip": "0.0",
            "pressure": "1029",
            "vis": "25",
            "cloud": "0",
            "uvIndex": "2"
        },
        {
            "fxDate": "2025-01-03",
            "sunrise": "07:37",
            "sunset": "17:02",
            "moonrise": "10:00",
            "moonset": "20:33",
            "moonPhase": "蛾眉月",
            "moonPhaseIcon": "801",
            "tempMax": "4",
            "tempMin": "-7",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "151",
            "textNight": "多云",
            "wind360Day": "225",
            "windDirDay": "西南风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "0",
            "windDirNight": "北风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "20",
            "precip": "0.0",
            "pressure": "1026",
            "vis": "25",
            "cloud": "2",
            "uvIndex": "2"
        },
        {
            "fxDate": "2025-01-04",
            "sunrise": "07:37",
            "sunset": "17:03",
            "moonrise": "10:29",
            "moonset": "21:47",
            "moonPhase": "蛾眉月",
            "moonPhaseIcon": "801",
            "tempMax": "3",
            "tempMin": "-6",
            "iconDay": "100",
            "textDay": "晴",
            "iconNight": "150",
            "textNight": "晴",
            "wind360Day": "225",
            "windDirDay": "西南风",
            "windScaleDay": "1-3",
            "windSpeedDay": "3",
            "wind360Night": "270",
            "windDirNight": "西风",
            "windScaleNight": "1-3",
            "windSpeedNight": "3",
            "humidity": "23",
            "precip": "0.0",
            "pressure": "1013",
            "vis": "25",
            "cloud": "0",
            "uvIndex": "2"
        }
    ]
    const init = () => {
        if (chartRef.current) {
            const machart = echarts.init(chartRef.current);
            machart.setOption(option(data));
        }

    }
    return (
        <div id="temp7day" ref={chartRef}></div>
    );
}

export default App;

option配置项

    // 处理日期
    const dates = data.map(item =>
        item.fxDate.slice(5)
    );

    // 处理天气图标和名称
    const imgMap= {
        '100':'https://d.scggqx.com/forecast/img/晴.png',
        '101':'https://d.scggqx.com/forecast/img/多云.png',
        '150':'https://d.scggqx.com/forecast/img/晴.png',
        '151':'https://d.scggqx.com/forecast/img/多云.png',
    }
    const handleDayWeather = data.map(item => {
        return {
            backgroundColor: {
                image: imgMap[item.iconDay]
            },
            height: 26,
            width: 26
        }
    })
    const handleNightWeather = data.map(item => {
        return {
            backgroundColor: {
                image: imgMap[item.iconNight]
            },
            height: 26,
            width: 26
        }
    })

    // 处理温度数据
    const maxTemps = data.map(item => item.tempMax);
    const minTemps = data.map(item => item.tempMin);

    // 处理天气
    const weatherText = data.map(item => {
        if (item.textDay === item.textNight) {
            return item.textDay
        } else {
            return `${item.textDay}转${item.textNight}`
        }
    })
    return {
        grid: {
            show: true,
            backgroundColor: 'transparent',
            opacity: 0.3,
            borderWidth: '0',
            top: '30%',
            bottom: '30%',
            left: '7%',
            right: '7%'
        },
        tooltip: {
            trigger: 'axis'
        },
        legend: {
            show: false
        },
        xAxis: [
            // 日期0
            {
                name: '日期',
                nameTextStyle: {
                    fontSize: 0,
                },
                type: 'category', // 同样是类目轴
                boundaryGap: false, // 不留空白
                position: 'top', // 位置在顶部
                offset: 40, // 与图表上边缘的距离
                zlevel: 100, // z轴层级
                axisLine: {
                    show: false // 不显示坐标轴线
                },
                axisTick: {
                    show: false // 不显示刻度线
                },
                axisLabel: {
                    interval: 0, // 每个标签都显示
                    formatter: [
                        '{a|{value}}' // 使用 rich text 格式化标签
                    ].join('\n'), // 换行符
                    rich: {
                        a: {
                            color: '#7EB7D8', // 标签颜色
                            fontSize: 14 // 字体大小
                        }
                    }
                },
                // nameTextStyle: {
                //     fontWeight: 'bold', // 字体加粗
                //     fontSize: 19 // 字体大小
                // },
                data: dates
            },
            // 白天图标
            {
                name: '白天图标',
                nameTextStyle: {
                    fontSize: 0,
                },
                type: 'category',
                boundaryGap: false,//不留空白,数据点从第一个点开始
                position: 'top',//将坐标轴放置在图表的顶部。
                offset: -20,//设置坐标轴与图表上边缘的距离。
                zlevel: 100,//设置层级,使该坐标轴在其他元素之上显示。
                axisLine: {
                    show: false//不显示坐标轴线。
                },
                axisTick: {
                    show: false//不显示刻度线。
                },
                axisLabel: {
                    interval: 0,
                    formatter: function (value, index) {
                        // return `{b|${value}}`;

                        return '{' + index + '| }\n{b|' + value + '}'
                    },
                    rich: {
                        ...handleDayWeather,
                        b: {
                            fontSize: 12,
                            lineHeight: 30,
                            height: 20
                        }
                    }
                },
                data: ['', '', '', '', '', '', '']  // 占位数据
            },
            // 夜间图标
            {
                name: '夜间图标',
                nameTextStyle: {
                    fontSize: 0,
                },
                type: 'category',
                boundaryGap: false,
                position: 'bottom',
                offset: 10,
                zlevel: 100,
                axisLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLabel: {
                    interval: 0,
                    formatter: function (value, index) {
                        return '{' + index + '| }\n{b|' + value + '}'
                    },
                    //预留rich对象
                    rich: {
                        ...handleNightWeather,
                        b: {
                            fontSize: 12,
                            lineHeight: 30,
                            height: 20
                        }
                    }

                },
                data: ['', '', '', '', '', '', '']  // 占位数据
            },

            // 天气
            {
                name: '天气',
                nameTextStyle: {
                    fontSize: 0,
                },
                type: 'category',
                boundaryGap: false,
                position: 'bottom',
                offset: 45,
                zlevel: 100,
                axisLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLabel: {
                    interval: 0,
                    formatter: [
                        '{a|{value}}'
                    ].join('\n'),
                    rich: {
                        a: {
                            color: '#7EB7D8',
                            fontSize: 14
                        }
                    }
                },
                data: weatherText

            },
        ],
        yAxis: {
            type: 'value',
            show: false,//控制是否显示纵坐标轴
            axisLabel: {//设置纵坐标轴的标签样式。
                formatter: '{value} °C',
                color: 'white'//设置纵坐标轴标签的颜色为白色
            }
        },
        series: [
            {
                name: '最高气温',
                type: 'line', // 系列类型,这里是折线图
                data: maxTemps, // 数据点
                symbol: 'emptyCircle', // 数据点的形状
                symbolSize: 0, // 数据点的大小
                showSymbol: true, // 显示数据点
                smooth: true, // 折线是否平滑
                itemStyle: { // 数据点样式
                    normal: {
                        color: '#F7CC80' // 数据点的颜色
                    }
                },
                label: { // 数据标签
                    show: true, // 是否显示
                    position: 'top', // 标签位置
                    formatter: '{c} °C', // 标签内容,这里显示温度
                    color: '#E3F5FF'
                },
                lineStyle: { // 折线样式
                    width: 1, // 线宽
                    // color: 'white' // 线颜色(这里注释掉了)
                },
                areaStyle: { // 区域样式(通常用于填充区域)
                    opacity: 1, // 透明度
                    color: 'transparent' // 填充颜色(这里是透明的)
                }
            },
            {
                name: '最低气温',
                type: 'line',
                data: minTemps,
                symbol: 'emptyCircle', // 数据点的形状
                symbolSize: 0, // 数据点的大小
                showSymbol: true, // 显示数据点
                smooth: true, // 折线是否平滑
                itemStyle: { // 数据点样式
                    normal: {
                        color: '#089EFE' // 数据点的颜色
                    }
                },
                label: {
                    // 数据标签
                    show: true, // 是否显示
                    position: 'bottom', // 标签位置
                    formatter: '{c} °C', // 标签内容
                    color: '#E3F5FF',
                },
                lineStyle: { // 折线样式
                    width: 1, // 线宽
                    // color: 'white' // 线颜色
                },
                areaStyle: { // 区域样式
                    opacity: 1, // 透明度
                    color: 'transparent' // 填充颜色(这里是透明的)
                }
            },

        ]
    }
}
export default option
难点
难点1

X轴上无法之间显示图片,所以利用富文本显示图片,签格式为 '{index| }\n{b|value}',其中 {index| } 用于显示天气图标,{b|value} 用于显示天气名称。 1.

axisLabel: {
        interval: 0,
        formatter: function (value, index) {
            return '{' + index + '| }\n{b|' + value + '}'
        },
        rich: {
            ...handleDayWeather,
            b: {
                fontSize: 12,
                lineHeight: 30,
                height: 20
            }
        }
    },
    data: ['', '', '', '', '', '', '']  // 占位数据

我这里用的是在线图片,如果是本地图片,需要import引入后使用变量,由此引出第二个难点 image.png

难点二

和风API天气情况对应的图标有四百多种,如果一个一个用import引入太费劲了,别问我怎么知道,说多了都是泪。

这里可以使用一个快捷方法require.context,篇幅有限,这块放下一章节 [ 动态引入模块-require.content ] 讲解。