Appearance
3D 曲面图
3D 曲面图用于展示二维网格数据的高度分布,通过连续曲面表现数据趋势,适用于地形数据、数学函数可视化、科学计算等场景。
快速开始
最简单的 3D 曲面图只需提供二维数组数据:
vue
<template>
<RaySurface3D :option="chartOption" />
</template>
<script setup>
import { ref } from 'vue';
import { RaySurface3D } from 'raychart';
// 生成简单的波浪曲面数据
function generateWaveSurface() {
const data = [];
for (let z = 0; z < 40; z++) {
const row = [];
for (let x = 0; x < 40; x++) {
const xVal = (x / 39) * Math.PI * 2;
const zVal = (z / 39) * Math.PI * 2;
row.push(Math.sin(xVal) * Math.cos(zVal) * 4 + 5);
}
data.push(row);
}
return data;
}
const chartOption = ref({
grid3D: { show: true },
xAxis3D: { show: true },
yAxis3D: { show: true },
zAxis3D: { show: true },
series: [
{
type: 'surface3D',
name: '波浪曲面',
data: generateWaveSurface(),
},
],
});
</script>数据格式说明
data 支持三种格式:
- 二维数组
number[][]:第一维(行)对应 Z 轴方向,第二维(列)对应 X 轴方向,数值对应 Y 轴(高度) - 函数
(x, z) => y:按需采样生成曲面 - 对象数组
[{x, z, value}, ...]
核心配置
数据配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
series.type | string | - | 必须为 'surface3D' |
series.data | number[][] | - | 二维网格数据(必填) |
series.name | string | - | 系列名称(用于图例和提示) |
样式配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
series.itemStyle | object | - | 曲面材质配置 |
series.itemStyle.opacity | number | 0.9 | 透明度(0-1) |
series.itemStyle.roughness | number | 0.4 | 粗糙度(0-1,影响反光) |
series.itemStyle.metalness | number | 0.1 | 金属度(0-1) |
series.itemStyle.clearcoat | number | 0.3 | 清漆层强度(0-1) |
series.colorGradient | Array<{stop, color}> | 内置 9 色渐变(蓝→红) | 曲面颜色映射 |
线框配置(可选)
通过 series.wireframe 配置线框网格:
javascript
wireframe: {
show: true, // 显示线框(默认 true)
color: '#ffffff', // 线框颜色(默认 '#ffffff')
opacity: 0.15 // 线框透明度(默认 0.15)
}线框可以帮助用户更好地理解曲面的网格结构和曲率变化。
曲面细分配置(进阶)
通过调整数据网格密度控制曲面细分:
javascript
// 更密集的网格(50x50)可以提供更平滑的曲面
const data = generateSurface(mathFunction, 50, 50);
// 稀疏网格(20x20)适用于简单曲面或性能优化
const data = generateSurface(mathFunction, 20, 20);网格密度越高,曲面越平滑,但渲染性能会降低。建议在 20-60 之间选择合适的值。
坐标系配置
通过 grid3D、xAxis3D、yAxis3D、zAxis3D 配置坐标系:
javascript
grid3D: {
show: true, // 显示网格盒子
boxWidth: 20, // 盒子宽度(X 轴)
boxHeight: 10 // 盒子高度(Y 轴)
}盒子高度建议根据数据范围调整,以获得最佳视觉效果。
常见场景
数学函数曲面
展示数学函数 y = sin(x) * cos(z) 的三维形态:
vue
<template>
<RaySurface3D :option="chartOption" />
</template>
<script setup>
import { ref } from 'vue';
import { RaySurface3D } from 'raychart';
// 生成数学函数曲面
function generateMathSurface(fn, xSteps, zSteps) {
const data = [];
for (let z = 0; z < zSteps; z++) {
const row = [];
for (let x = 0; x < xSteps; x++) {
const xVal = (x / (xSteps - 1)) * Math.PI * 2;
const zVal = (z / (zSteps - 1)) * Math.PI * 2;
row.push(fn(xVal, zVal));
}
data.push(row);
}
return data;
}
const chartOption = ref({
lights: [
{ id: '1', type: 'Ambient', color: '#ffffff', intensity: 0.5 },
{
id: '2',
type: 'Directional',
color: '#fffdf4',
intensity: 1.0,
position: { x: 10, y: 15, z: 10 },
castShadow: true,
},
],
grid3D: { show: true, boxWidth: 20, boxHeight: 10 },
xAxis3D: { show: true },
yAxis3D: { show: true },
zAxis3D: { show: true },
series: [
{
type: 'surface3D',
name: '波浪曲面',
data: generateMathSurface((x, z) => Math.sin(x) * Math.cos(z) * 4 + 5, 40, 40),
wireframe: { show: true, opacity: 0.15 },
itemStyle: { opacity: 0.9, roughness: 0.4, metalness: 0.1 },
},
],
});
</script>马鞍面(双曲抛物面)
展示经典的马鞍面数学模型 y = x² - z²:
vue
<script setup>
const chartOption = ref({
lights: [
{ id: '1', type: 'Ambient', color: '#ffffff', intensity: 0.6 },
{
id: '2',
type: 'Directional',
color: '#fffdf4',
intensity: 1.0,
position: { x: 10, y: 15, z: 10 },
castShadow: true,
},
],
grid3D: { show: true, boxWidth: 20, boxHeight: 10 },
xAxis3D: { show: true },
yAxis3D: { show: true },
zAxis3D: { show: true },
series: [
{
type: 'surface3D',
name: '马鞍面',
data: generateMathSurface((x, z) => {
const nx = x / Math.PI - 1; // 归一化到 [-1, 1]
const nz = z / Math.PI - 1;
return (nx * nx - nz * nz) * 4 + 5;
}, 40, 40),
wireframe: { show: true, opacity: 0.15 },
itemStyle: {
opacity: 0.9,
roughness: 0.4,
metalness: 0.1,
},
},
],
});
</script>涟漪曲面(径向波)
模拟水面涟漪效果 y = sin(r) / r:
vue
<script setup>
const chartOption = ref({
lights: [
{ id: '1', type: 'Ambient', color: '#ffffff', intensity: 0.5 },
{ id: '2', type: 'Directional', color: '#4facfe', intensity: 0.8, position: { x: -10, y: 15, z: 10 } },
{ id: '3', type: 'Point', color: '#f093fb', intensity: 0.5, position: { x: 0, y: 20, z: 0 } },
],
grid3D: { show: true, boxWidth: 20, boxHeight: 10 },
xAxis3D: { show: true },
yAxis3D: { show: true },
zAxis3D: { show: true },
series: [
{
type: 'surface3D',
name: '涟漪',
data: generateMathSurface((x, z) => {
const nx = x - Math.PI; // 中心化
const nz = z - Math.PI;
const r = Math.sqrt(nx * nx + nz * nz); // 到中心的距离
return (Math.sin(r) / (r + 0.001)) * 5 + 5; // 避免除零
}, 50, 50),
wireframe: { show: true, opacity: 0.1 },
itemStyle: {
opacity: 0.88,
roughness: 0.3,
metalness: 0.15,
},
},
],
});
</script>地形高度图
使用真实的地形数据展示海拔分布:
vue
<script setup>
// 模拟地形数据(实际项目中可从后端或 GeoTIFF 加载)
function generateTerrainData() {
const data = [];
for (let z = 0; z < 30; z++) {
const row = [];
for (let x = 0; x < 30; x++) {
// 模拟山峰:使用多个高斯分布叠加
const peak1 = Math.exp(-((x - 10) ** 2 + (z - 10) ** 2) / 20) * 8;
const peak2 = Math.exp(-((x - 20) ** 2 + (z - 15) ** 2) / 30) * 6;
const noise = Math.sin(x * 0.3) * Math.cos(z * 0.3) * 0.5;
row.push(peak1 + peak2 + noise + 2); // 基础海拔 2
}
data.push(row);
}
return data;
}
const chartOption = ref({
lights: [
{ id: '1', type: 'Ambient', color: '#ffffff', intensity: 0.7 },
{
id: '2',
type: 'Directional',
color: '#fffdf4',
intensity: 1.2,
position: { x: 15, y: 20, z: 15 },
castShadow: true,
},
],
grid3D: { show: true, boxWidth: 20, boxHeight: 12 },
xAxis3D: { name: '经度' },
yAxis3D: { name: '海拔(m)' },
zAxis3D: { name: '纬度' },
series: [
{
type: 'surface3D',
name: '地形',
data: generateTerrainData(),
wireframe: { show: false }, // 地形图通常不显示线框
itemStyle: {
opacity: 1.0,
roughness: 0.8, // 粗糙表面模拟土壤质感
metalness: 0.0,
},
},
],
});
</script>组件属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
option | RayChartOption | - | 图表配置对象(必填) |
width | string | number | '100%' | 图表宽度 |
height | string | number | '600px' | 图表高度 |
相关文档
基础学习
进阶主题
API 参考
其他图表类型
- 3D 散点图 XYZ - 三维空间数据分布
- 3D 热力图 - 三维矩阵数据可视化
- 3D 面积图 - 多系列数据趋势对比
- 查看所有图表 - 浏览完整图表库
