☰
Echarts风速玫瑰图实战:极坐标配置与Vue3封装指南
2026/10/5 9:59:19 网站建设 项目流程

先说个真实经历:去年做气象数据可视化大屏,产品指着设计稿跟我说“这里要一个Echarts的风速玫瑰图,跟别人家不一样,要有花瓣感”。我当时第一反应是“这不就是饼图换个角度?”,结果真上手才发现,玫瑰图在Echarts里根本不是饼图,而是极坐标系下的柱状图。它用角度表达风向,用半径长度表达风速或频率,靠这个把“哪个方向风多、哪个方向风大”这种二维信息压进一张图里,信息密度比普通柱状图高一个量级。

这篇文章就把我做风力风速玫瑰图的全过程拆开讲:从气象数据怎么聚合成16个方向,到Echarts极坐标配置的每一个关键参数,再到Vue3大屏项目里的组件封装和自适应适配,最后把我踩过的几个坑一并列出来。适合刚接触Echarts数据可视化、或者已经在做气象/能源/环境类大屏但还没搞懂玫瑰图配置的朋友。

1. 玫瑰图到底在表达什么:风速风向的另一种打开方式

1.1 为什么不用折线图或柱状图

气象数据最原始的样子,是一长串按时间排列的风向和风速记录。用折线图画出来,风速是一堆锯齿,风向是-180到180之间疯狂跳变的折线,别说领导看不清,我自己盯着看两分钟都头晕。

问题的根源在于:风向是角度量,不是线性量。你没法在普通直角坐标系里自然地表达“北”和“北偏东22.5度”之间的连续性,更没法直观看出“这个站点冬天的风主要集中在西北到北这个扇区里”。而玫瑰图天生就是干这个的——它的坐标系本身就是极坐标,角度轴天然就是为风向设计的。

1.2 玫瑰图的三种常见变体

不同业务场景下,玫瑰图画的东西不一样,先搞清楚再动手:

  • 风向频率玫瑰图:统计每个方向的风出现次数占总次数的百分比,柱子越长代表这个方向“风越来越多”。城市规划、污染扩散分析常用这种。
  • 平均风速玫瑰图:每个方向上的风速取平均值,柱子越长代表这个方向“风越大”。风电场选址、建筑风环境分析用得多。
  • 堆叠风速玫瑰图:每个方向按照风速等级(比如0-3m/s、3-6m/s、6-9m/s)分别统计占比,一个扇区里分几段颜色。这是最完整的一种,信息量最大,但配置也最复杂。

我这次做的是第二种——平均风速玫瑰图,因为大屏上要突出“主导风向”和“强风方向”两个信息,单系列最干净,图例和配色也不会挤。

1.3 和饼图、雷达图的本质区别

很多第一次接触玫瑰图的人会问:为什么不用饼图?饼图每个扇区的角度占比表示数值占比,它只有“占比”这一个维度;玫瑰图在极坐标下,扇区的角度是固定的类别位置(16个方位),半径才是数值。用一个不严谨但好记的说法:饼图是靠“切角大小”骗眼睛,玫瑰图是靠“半径长短”说话。

雷达图倒是和玫瑰图有点像,但雷达图是线性的闭合多边形,强调各维度的对比;玫瑰图是圆形的扇形填充,强调方向分布和归一化,在气象场景下默认就是用玫瑰图而不是雷达图。

2. 数据准备:把气象风数据变成16个扇区的数字

2.1 原始数据长什么样

不论数据来自气象站、测风塔还是ERA5再分析资料,最终到手的通常是这样一张表:

时间风向(度)风速(m/s)
2024-01-01 00:003405.2
2024-01-01 01:003556.1
2024-01-01 02:00153.8
2024-01-01 03:00872.1

这里有个气象学的基本约定必须注意:风向是指风的来向。340度表示风从西北偏北方向吹过来,不是吹向西北。做玫瑰图时,这个角度直接对应极坐标的方位角,不需要做180度的反向修正。

2.2 为什么要聚合成16个方位

理论上可以把0到359度每个整数度都做成一个柱子,360个扇区看起来像一朵密度极高的“菊花”,数值上更精细,但视觉上完全是噪音,label也根本放不下。行业惯例是分成8方位或16方位。8方位就是N、NE、E、SE、S、SW、W、NW,每格45度;16方位是在8方位中间再插一条,每格22.5度,精度足够,又不会太密。我做16方位,因为风向的跃迁往往在22.5度这个尺度上还能看出规律,45度会把一些过渡方向抹掉。

16方位名称如下:

索引方位角度范围
0N348.75 ~ 11.25
1NNE11.25 ~ 33.75
2NE33.75 ~ 56.25
3ENE56.25 ~ 78.75
4E78.75 ~ 101.25
5ESE101.25 ~ 123.75
6SE123.75 ~ 146.25
7SSE146.25 ~ 168.75
8S168.75 ~ 191.25
9SSW191.25 ~ 213.75
10SW213.75 ~ 236.25
11WSW236.25 ~ 258.75
12W258.75 ~ 281.25
13WNW281.25 ~ 303.75
14NW303.75 ~ 326.25
15NNW326.25 ~ 348.75

2.3 聚合计算代码

写一个纯前端的聚合函数,把原始时序数据变成玫瑰图需要的数组。这个函数我直接用JavaScript写,方便在静态Demo里演示;真实项目里可以把这个逻辑放到后端,但算法完全一样:

function aggregateWindRose(data, bins = 16) { // data: [{ direction: number, speed: number }] const step = 360 / bins; const sums = new Array(bins).fill(0); const counts = new Array(bins).fill(0); data.forEach((item) => { // 把风向角度映射到最近的方位索引 // + step / 2 是为了让边界角度归入正确的扇区 const idx = Math.floor(((item.direction % 360) + step / 2) / step) % bins; sums[idx] += item.speed; counts[idx] += 1; }); // 返回每个方位的平均风速;没有数据的方向返回0 return sums.map((sum, i) => (counts[i] === 0 ? 0 : +(sum / counts[i]).toFixed(2))); }

这个函数有个容易被忽略的细节:风向角度的取模。数据里可能出现360度,如果直接除以22.5会得到16,数组越界。所以必须先direction % 360。边界角度(比如11.25度这条线)归到哪边都说得通,加半个step再取整会更稳定。

2.4 一份可以直接用的示例数据

后面所有配置我都以这份数据为例,方便你直接复制跑出效果:

// 顺序必须和方位数组一一对应:从N开始,顺时针 const roseData = [4.5, 3.8, 2.6, 2.1, 2.4, 3.0, 3.6, 4.2, 5.0, 4.8, 3.9, 3.1, 2.8, 3.3, 4.0, 4.7]; const windDirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];

这组数据模拟的是某个站点冬季典型风况:北风、南风偏强,东西风偏弱。你会看到后面玫瑰图呈现南北方向花瓣长、东西方向花瓣短的形态,这就是实际气象数据常见的“主导风向”现象。

3. Echarts极坐标系配置:玫瑰图的核心骨架与series选型

3.1 基础配置:polar + angleAxis + radiusAxis

Echarts做玫瑰图的推荐方式,不是pie漏斗改造,而是建立在极坐标系上的柱状图。核心是三件套:polar定义极坐标位置和大小,angleAxis定义角度轴(对应风向),radiusAxis定义半径轴(对应风速数值),最后用type: 'bar'的series挂到coordinateSystem: 'polar'上。

一个最简但是能跑的option长这样:

const option = { polar: { center: ['50%', '50%'], radius: '70%' }, angleAxis: { type: 'category', data: windDirs, startAngle: 90, clockwise: true, axisLabel: { fontSize: 12, color: '#7a8a9e' }, axisLine: { lineStyle: { color: 'rgba(255,255,255,0.2)' } }, axisTick: { show: false }, splitLine: { show: false } }, radiusAxis: { min: 0, max: 6, axisLabel: { formatter: '{value} m/s', color: '#7a8a9e' }, splitLine: { lineStyle: { color: 'rgba(255,255,255,0.15)', type: 'dashed' } } }, series: [ { type: 'bar', coordinateSystem: 'polar', data: roseData, itemStyle: { color: '#2f7cf6', opacity: 0.8 } } ] };

这里有三个参数新手基本都会踩坑,我逐一讲清楚:

startAngle: 90。这个参数决定了第一个类目在极坐标上的起始方位。Echarts默认startAngle是90,正好指向12点钟方向。再配合clockwise: true(默认就是true),数据会从12点方向开始顺时针排列。这正好符合气象玫瑰图的习惯——北在上、方位顺时针排布。如果你的第一个数据是N,那么这样配出来就是标准的“北风朝上”的玫瑰图。

radiusAxis的max要手动指定。如果让Echarts自动计算max,数据最大值是5的时候,max可能算成5.5或者6.5这种带小数的刻度,视觉上不干净。做可视化大屏时我习惯手动给一个略大于实际最大值的整数。最大值6对于这组数据的5.0是合适的。

极坐标下的bar没有柱宽的概念,它的“宽度”是角度。这一点和直角坐标系柱状图完全不同,不需要设置barWidth,扇区的角度大小由angleAxis的类目数量自动均分。

3.2 为什么是bar而不是pie

我在1.3节说过玫瑰图和饼图的区别,这里补充一个更本质的视角:pie系列在极坐标下渲染时,每个扇形共享半径,只有角度维度承载数值;而bar系列在极坐标下,每个类目占据的角度是均匀的,真正承载数值的是从圆心到外圈的半径距离。Echarts内部对极坐标bar的渲染逻辑,就是沿每个角度方向画一个矩形(在极坐标下变成扇环矩形),半径越长柱子越高,这和饼图的几何语义完全不同。

如果你非要用pie的roseType来画半径玫瑰图,你会发现它表达的是“占比”,而你需要的是“绝对值”,这两者在视觉上看着像,数据和业务含义却差之千里。所以别图省事,老老实实用bar挂polar。

3.3 堆叠多系列:风速等级频率玫瑰图的配置思路

如果你的业务需要把风向和风速等级联合起来看,可以在series里加多个bar系列,每个系列代表一个风速区间,然后把stack字段设为同一个值,让它们沿着半径方向堆叠。配置示意:

series: [ { name: '0-3m/s', type: 'bar', coordinateSystem: 'polar', stack: 'wind', data: [1.2, 1.0, 0.8, /* ... */] }, { name: '3-6m/s', type: 'bar', coordinateSystem: 'polar', stack: 'wind', data: [2.5, 2.1, 1.5, /* ... */] }, { name: '6-9m/s', type: 'bar', coordinateSystem: 'polar', stack: 'wind', data: [0.8, 0.7, 0.3, /* ... */] } ] // legend 就需要显示出来了 legend: { data: ['0-3m/s', '3-6m/s', '6-9m/s'], textStyle: { color: '#ccc' } }

堆叠图里每个方向的柱子总长度表示该方向的总频率或总时长,各颜色段表示不同风速等级的占比。这时候legend才真正发挥作用,否则单系列玫瑰图根本不需要legend。堆叠系列要注意:每个系列的数据必须同顺序、同长度,而且要保证每个方向至少有一个系列数据,避免空扇区把整个色块断开。

4. 让玫瑰图能上台面:配色、label、tooltip与图例的细节处理

4.1 大屏深色主题下的配色逻辑

大屏可视化背景几乎都是深色,玫瑰图在这种环境下的配色,既要有辨识度又不能刺眼。我个人的经验是:先定主色,再用透明度分层。

主色可以选择蓝色系#2f7cf6,因为它和深蓝背景有自然的层次关系。在一个系列的情况下,如果所有扇区都用同一个颜色,视觉上会显得“糊”,所以我会给每个扇区按数值大小分配不同透明度,数值大的透明度低(更实),数值小的透明度高(更虚)。这样不需要图例就能让眼睛快速定位到“哪个方向风最大”。

用一个循环生成每个扇区的颜色:

const maxVal = Math.max(...roseData); const baseColor = [47, 124, 246]; // RGB of #2f7cf6 const colors = roseData.map((v) => { const alpha = 0.35 + 0.55 * (v / maxVal); return `rgba(${baseColor[0]}, ${baseColor[1]}, ${baseColor[2]}, ${alpha.toFixed(2)})`; });

这个方案有一个很明显的优点:不需要二次legend说明颜色深浅,因为颜色深浅和数值大小的对应关系是直觉性的,大屏上看一眼就懂。

4.2 每个扇区的渐变效果怎么做

很多人想给扇区做“从内到外渐变”的立体感。这里要提醒一个坑:Echarts极坐标bar的itemStyle.color支持LinearGradient,但这个渐变的x、y坐标是基于直角坐标系的,放在极坐标下不会自动变成径向渐变。直接写LinearGradient(0, 0, 1, 0, ...)出来的效果是所有扇区共享同一个水平方向渐变,看起来非常奇怪。

我试过几种方案,最终稳定可用的做法有两种:

第一种,放弃真正的径向渐变,改用同色系浓度渐变——也就是每个扇区用一个纯色,但在扇区外圈加一圈亮色border,模拟“边缘高光”:

itemStyle: { borderColor: 'rgba(255, 255, 255, 0.6)', borderWidth: 1, borderType: 'solid' }

第二种,用直角坐标系的LinearGradient做整体从左到右的色彩过渡,再叠加一个半透明的深色蒙层。这种适合整张大屏的色调统一,但单个扇区的立体感不强。

如果你非要径向渐变,实现方式是在series的data里给每一项单独配置渐变对象,并且把渐变的坐标对齐到极坐标中心。不过实测这个方案在部分Echarts版本里渲染会有锯齿,性能也差,所以我建议大屏项目直接用“透明度分层+border高光”的方案就够了,视觉上干净、渲染快,还不会出现渐变方向错乱的尴尬。

4.3 label和tooltip:把数字信息嵌进图里

玫瑰图的扇区多了以后,label放不放、放哪里,直接决定这张图的信息完整度。

单系列平均风速玫瑰图,我建议每个扇区都显示数值,position: 'outside',字体小一点,颜色浅一点。配置如下:

label: { show: true, position: 'outside', formatter: (params) => params.value > 0 ? params.value.toFixed(1) : '', fontSize: 10, color: '#a8b6c7' }

有个细节值得强调:风速为0的方向,label不要显示“0.0”,那一整排0会把图弄得全是数字点,应该直接显示空字符串。这在formatter里用一个三元判断就能解决。

tooltip是交互时补充信息的入口,默认只显示数值和方向索引,不够友好。大屏上领导hover过去,希望看到的是人能读懂的文案,所以formatter里自己拼字符串:

tooltip: { trigger: 'item', formatter: (params) => { const dirNames = ['北', '北东北', '东北', '东东北', '东', '东东南', '东南', '南东南', '南', '南西南', '西南', '西西南', '西', '西西北', '西北', '北西北']; const dir = dirNames[params.dataIndex]; return `${dir}风<br/>平均风速:${params.value} m/s`; } }

这里要注意,触发类型推荐trigger: 'item'而不是'axis'。在极坐标下,axis触发很容易因为鼠标扫过圆心区域而出现奇怪的十字线,item触发干净利落。

4.4 动画节奏对大屏展示的影响

大屏首次加载时,玫瑰图从圆心“绽放”出来的动画很有仪式感,但很多人忽略了一个问题:如果大屏有定时器轮询数据,每次setOption都会重播动画,整个图就会一直闪。

我把动画配置分成两段:

animation: true, animationDuration: 800, animationEasing: 'cubicOut', animationDurationUpdate: 300, animationEasingUpdate: 'quarticOut'

animationDuration控制首帧动画,animationDurationUpdate控制数据更新时的过渡。更新动画时间短、缓动效果更平滑,数据刷新时不会有“炸开”的突兀感。如果你的大屏是那种秒级刷新,建议直接把animationDurationUpdate降到0,否则视觉上会一直有重新生长动画。

5. Vue3项目里的落地:组件封装、数据更新与容器自适应

5.1 按需引入Echarts而不是全量引入

Vue3 + Vite的大屏项目,最忌讳的就是import * as echarts from 'echarts',打包体积直接多出1MB以上。玫瑰图用到的模块其实就那几个,按需引入的写法我已经固定下来了:

import * as echarts from 'echarts/core'; import { BarChart } from 'echarts/charts'; import { PolarComponent, AngleAxisComponent, RadiusAxisComponent, TooltipComponent, GridComponent } from 'echarts/components'; import { CanvasRenderer } from 'echarts/renderers'; echarts.use([ BarChart, PolarComponent, AngleAxisComponent, RadiusAxisComponent, TooltipComponent, GridComponent, CanvasRenderer ]);

注意几个点:极坐标的PolarComponent是必须的,但很多人会漏掉AngleAxisComponent和RadiusAxisComponent,导致运行时图表一片空白且控制台报“Component angleAxis is used but not imported”。另外,这里用CanvasRenderer就够了,SVGRenderer在这种大量扇区的场景下渲染性能和交互流畅度反而不如Canvas。

5.2 封装一个可复用的WindRose组件

我习惯把所有echarts图都封装成通用组件,但玫瑰图有一些自己的初始化细节,单独封装成WindRoseChart.vue更清晰。核心代码如下:

<template> <div ref="chartEl" class="wind-rose-chart" :style="{ width: '100%', height: height + 'px' }"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount, watch } from 'vue'; import * as echarts from 'echarts/core'; import { BarChart } from 'echarts/charts'; import { PolarComponent, AngleAxisComponent, RadiusAxisComponent, TooltipComponent } from 'echarts/components'; import { CanvasRenderer } from 'echarts/renderers'; echarts.use([ BarChart, PolarComponent, AngleAxisComponent, RadiusAxisComponent, TooltipComponent, CanvasRenderer ]); const props = defineProps({ height: { type: Number, default: 320 }, roseData: { type: Array, required: true }, windDirs: { type: Array, default: () => ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'] }, maxSpeed: { type: Number, default: 6 } }); const chartEl = ref(null); let chart = null; function calcColors() { const maxVal = Math.max(...props.roseData, 1); const baseColor = [47, 124, 246]; return props.roseData.map((v) => { const alpha = 0.35 + 0.55 * (v / maxVal); return `rgba(${baseColor[0]}, ${baseColor[1]}, ${baseColor[2]}, ${alpha.toFixed(2)})`; }); } function buildOption() { return { backgroundColor: 'transparent', polar: { center: ['50%', '52%'], radius: '70%' }, angleAxis: { type: 'category', data: props.windDirs, startAngle: 90, clockwise: true, axisLabel: { fontSize: 11, color: '#8aa0b5' }, axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: false } }, radiusAxis: { min: 0, max: props.maxSpeed, axisLabel: { color: '#8aa0b5', formatter: (value) => (value === 0 ? '' : value + ' m/s') }, splitLine: { lineStyle: { color: 'rgba(255,255,255,0.12)', type: 'dashed' } } }, series: [ { type: 'bar', coordinateSystem: 'polar', data: props.roseData, itemStyle: { color: (params) => calcColors()[params.dataIndex], borderColor: 'rgba(255,255,255,0.45)', borderWidth: 1, borderRadius: 0 }, label: { show: true, position: 'outside', fontSize: 10, color: '#a8b6c7', formatter: (params) => (params.value > 0 ? params.value.toFixed(1) : '') } } ], tooltip: { trigger: 'item', formatter: (params) => { const dirNames = ['北', '北东北', '东北', '东东北', '东', '东东南', '东南', '南东南', '南', '南西南', '西南', '西西南', '西', '西西北', '西北', '北西北']; const dir = dirNames[params.dataIndex]; return `<b>${dir}风</b><br/>平均风速:${params.value} m/s`; } } }; } function render() { if (!chart) return; chart.setOption(buildOption()); } function handleResize() { chart && chart.resize(); } onMounted(() => { chart = echarts.init(chartEl.value); render(); window.addEventListener('resize', handleResize); }); onBeforeUnmount(() => { window.removeEventListener('resize', handleResize); if (chart) { chart.dispose(); chart = null; } }); watch(() => props.roseData, render, { deep: true }); </script>

这个组件里有几个细节是实战里总结出来的:

radiusAxis的axisLabel里,我让0不显示刻度文字,因为极坐标中心区域出现“0 m/s”会显得很挤,而刻度线本身还在,不影响读数;polar.center的y方向偏下一点,因为顶部要给方位label留出空间,底部要给外围label留出空间;resize监听必须挂在window上,同时组件销毁时一定要dispose,否则大屏长时间运行内存会持续上涨。

5.3 容器高度和初始化时机的坑

Vue3里最常遇到的问题是:mounted时容器还是display: none,或者高度为0,这时候echarts.init会得到一个宽度或高度为0的实例,图表直接白屏。

大屏项目里通常是页面加载后先请求数据、再渲染图表,偶尔会有tab页签或弹窗场景。遇到这种情况,不要硬在mounted里初始化,正确的姿势是等容器真正显示后再init,或者用nextTick配合container的clientWidth判断:

async function initWhenReady() { await nextTick(); if (!chartEl.value || chartEl.value.clientWidth === 0) { setTimeout(initWhenReady, 200); return; } chart = echarts.init(chartEl.value); render(); }

另外,容器必须有显式的高度,光有宽度没有高度,echarts会静默失败,控制台一个错都不报,排查起来非常浪费时间。我在模板里直接:style="{ height: height + 'px' }",就不依赖父级样式了。

5.4 大屏整体缩放方案下的适配

很多大屏用transform: scale()做整体等比缩放,这会导致echarts的resize计算有误差。经验是:如果大屏外层用了scale缩放,组件内部的resize就不要监听window了,而是监听外层容器的ResizeObserver,并且在scale变换后手动调用一次chart.resize()强制重算尺寸。

实践中最简单的做法是给大屏根节点一个固定设计稿尺寸,比如1920x1080,然后用transform缩放适配屏幕。在这种情况下,echarts容器拿到的尺寸始终是1920宽下的值,不需要跟着屏幕变化,反而更稳定。

6. 实测中容易踩的坑:起始角对齐、零值扇区与标签重叠

6.1 起始角不对导致“北”不在上方

这个问题我印象太深了。第一次写完,图表出来,“北”这个label跑到了右上方斜45度的位置,怎么看怎么别扭。原因就是startAngle的默认行为。

Echarts极坐标里,startAngle是角度轴的起始角,默认90度,意思是第一个类目从12点钟方向开始。如果你把startAngle改成0,第一个类目会跑到3点钟方向。气象玫瑰图约定俗成是“北在上”,所以必须让第一个方位N从12点方向起,配置就是startAngle: 90。

还有一个备份问题:如果数据顺序不是从N开始,而是从E开始,那你要么调整数据顺序,要么把startAngle换成对应的角度。我的建议是统一让后端按N开头的固定顺序返回数据,前端不做任何offset调整,这样后续维护的人不会有“为什么这个图转了一圈”的困惑。

6.2 某个方向完全没有数据:零值扇区处理

实测中经常出现某个方位一个月都没出现过风,比如山谷站点东西风几乎为0。这种扇区如果在数据里填0,玫瑰图会塌陷一个缺口,视觉上不完整。

处理方式有两种。如果业务上0就是没有风,保留缺口其实是诚实的表现,但要在label里过滤掉0值,且radiusAxis.max要手动设,避免自动刻度被一堆0拉成从0.5开始。如果业务上想突出“风主要集中在某些方向”,可以把0保留但把扇区周围颜色调淡,用透明度降低存在感。

另外要注意,Math.max(...roseData, 1)这种写法在给颜色计算alpha时能防0,如果所有数据都是0,max至少是1,不会出现除以0产生NaN导致渲染异常。

6.3 窄扇区label重叠和溢出

大屏分辨率不一样时,玫瑰图外围的label经常互相叠。尤其是相邻两个方向的风速值都很小、数字位数接近时,两个label几乎贴在了一起。

解决办法有几个层次。最直接的是把fontSize调小到10px;再做一层防御,用labelLayout的hideOverlap属性:

labelLayout: { hideOverlap: true }

这个配置会把重叠的label按顺序隐藏,鼠标hover时tooltip能补全信息。实测效果不错,大屏上偶尔缺一两个数字不影响整体阅读。如果还嫌不够,把label改成只在数值大于某个阈值的扇区上显示,弱化小数值的视觉噪音。

6.4 tooltip单位口径不一致

这个不算技术坑,是业务坑,但我见过太多次了。气象站原始数据用的是m/s,但风电领域习惯用“级”(蒲福风级),做环保的喜欢用km/h。如果在tooltip和radiusAxis的label里不统一单位,看数据的人很容易误读。

我建议在组件层强制收口:入口参数统一接收m/s,展示层通过formatter再决定要不要换算。比如要显示级数,就在formatter里写windScale(params.value)函数转换,而不是让后端各传各的单位。这也是组件封装的一个价值:数据口径在交付边界上就锁死,后面接不同业务方都不慌。

6.5 极坐标下splitLine和axisLabel的层级感

最后一个容易被忽略的视觉细节:默认情况下,radiusAxis的splitLine是实线,angleAxis的splitLine默认显示。玫瑰图的外圈如果实线和虚线混在一起,会显得很“脏”。

我的做法是:angleAxis的splitLine关闭,因为16个方向的副词分割线会让圆心区域变成一个密密麻麻的蜘蛛网;radiusAxis的splitLine打开但改虚线、降透明度。这样视觉焦点自然落在扇区上,方向通过外围的label感知就足够了。

写在最后

风力风速玫瑰图做下来,技术上真的不难,难的是把气象领域的“方向、风速、频率”这些语义用Echarts的极坐标机制准确翻译出来。只要明白“角度轴表达方向、半径轴表达数值、series用bar挂polar”这个核心逻辑,再配合起始角、数据顺序、label策略这几个细节,一张能上大屏、能交付业务方的玫瑰图就八九不离十了。

如果你们项目里也有类似的气象可视化需求,我建议从单系列平均风速玫瑰图起步,先把16方位聚合和极坐标配置跑通,再往堆叠风速等级方向扩展。留言里可以聊聊你碰到的是哪种场景,是风电场选址、污染扩散,还是纯粹大屏好看,不同用途在配色和数据粒度上差别还挺大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询