ECharts 3D地球(铁路线、飞线、标点、图标、文字标注等)

发布于:2025-03-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

旋转3D地球效果如下
在这里插入图片描述
整体思路:

  1. 加载世界地图坐标信息json数据
  2. 准备生成散点、铁路线、箭头标注、文字标注等一些echarts需要的配置对象
  3. 使用加载的json数据生成地图基础纹理,基本纹理中series包含第二步骤生成的那些东西
  4. 使用globe配置对象绘制3d地球,在globe里面配置生成的纹理
  5. 可开启一个定时器,让地球旋转起来

1.加载世界地图坐标信息json数据

// request.js
//没有基地址 访问根目录下文件
export const GETNOBASE = async (url, params) => {
    try {
        const data = await axios.get(url, {
            params: params,
        });
        return data;
    } catch (error) {
        return error;
    }
}
// earth3d.vue
// 加载地理信息
async loadGeoJson() {
    const res = await GETNOBASE("./map-geojson/worldZh.json");
    return res.data;
} 
// worldZh.json这个文件自己去这个网站下 [数据可视化平台](https://datav.aliyun.com/portal/school/atlas/area_selector)
// worldZh.json这个文件我放在public/map-geojson文件下的
  1. 准备生成散点、铁路线、箭头标注、文字标注等一些echarts需要的配置对象
// 准备绘制数据
// earth3d.vue
 prepareData() {
   this.prepareEffectScatterData();
   this.prepareTrainLines();
   this.prepareArrows();
   this.prepareTrainNames();
} 
// 生成散点数据
    prepareEffectScatterData() {
      cityData.forEach((point) => {
        this.effectScatter.push({
          name: point.name,
          value: point.value,
          symbol: point.hasIcon ? `image://${point.icon}` : "circle", // 使用自定义图片作为symbol
          symbolSize: point.name === "重庆" ? 12 : 8,
          z: point.name === "重庆" ? 5 : 2,
          label: {
            position: point.position,
            fontSize: point.name === "重庆" ? 16 : 14,
            // fontWeight: 'bold',
            color: point.name === "重庆" ? "#e51c1c" : "#fff",
          },
        });
      });
    }
// 生成铁路线数据
    prepareTrainLines() {
      this.trainLines = [
        drawTrainLine(hasTielu, 4, "solid", "rgba(255, 255, 255, 1)", 3),
        drawTrainLine(hasTielu, 4, [10, 10], "#000", 2),
      ];
    }
  // 生成箭头标注
    prepareArrows() {
      this.arrows = [
        createArrow({
          name: "向上的箭头",
          value: [102.3043, 40.2248],
          x: 200,
          y: 250,
          type: "up",
        }),
        createArrow({
          name: "向左的箭头",
          value: [71.511721, 42.302711],
          x: 300,
          y: 150,
          type: "left",
        }),
        createArrow({
          name: "向下的箭头",
          value: [99.36, 17.58],
          x: 150,
          y: 300,
          type: "down",
        }),
      ];
    }
    // 生成文字标注
  prepareTrainNames() {
      this.trainNames = [
        ...trainName.map((item) => createNamePoint(item)), // 印在地球上不带框框的文字
        createNamePointImg({ name: "勒是铁路线", value: [102.11293, 42.14693] }), // 带框框的提示文字
      ];
    }
    
// map.js
// 散点数据
export const cityData = [
  {
    name: '科伦坡',
    value: [79.52, 6.55],
    position: 'left', // 这个是在点的四周位置,top,right,left,bottom,根据实际情况来定
    hasIcon: false,
  },
  {
    name: "钦州",
    value: [107.27, 21.25], // [107.27, 21.35]
    position: 'bottom',
    hasIcon: false,
  },
  {
    name: "重庆",
    value: [106.55116, 29.61203],
    position: 'bottom',
    hasIcon: true,
    icon: cqIcon, // 这个是引入的图片资源
  },
  {
    name: "武汉",
    value: [113.41, 29.58],
    position: 'top',
    hasIcon: false,
  },
  {
    name: "上海",
    value: [121.451830, 31.175201],
    position: 'top',
    hasIcon: false,
  },
  {
    name: "成都",
    value: [104.083736, 30.65318],
    position: 'top',
    hasIcon: false,
  },
  {
    name: "二连浩特",
    value: [111.982513, 43.655688],
    position: 'right',
    hasIcon: false,
  },
  {
    name: "马拉",
    value: [19.40624, 52.12210],
    position: 'bottom',
    hasIcon: false,
  },
  {
    name: "明斯克",
    value: [27.30, 53.51],
    position: 'bottom',
    hasIcon: false,
  },
  {
    name: "吉大港",
    value: [91.48, 22.18],
    position: 'top',
    hasIcon: false,
  },
  {
    name: "台湾",
    value: [120.1, 21.45],
    position: 'right',
    hasIcon: false,
  },
  {
    name: "满洲里",
    value: [117.3837, 49.6035],
    position: 'right',
    hasIcon: false,
  },
  {
    name: "霍尔果斯",
    value: [80.4852, 44.163962],
    position: 'top',
    hasIcon: false,
  },
  {
    name: "莫克兰",
    value: [62.280, 25.2200],
    position: 'right',
    hasIcon: false,
  },
  {
    name: "塔什干",
    value: [69.2444, 41.3396],
    position: 'bottom',
    hasIcon: false,
  },
  {
    name: "吉洪诺沃",
    value: [40.2936, 56.3469],
    position: 'top',
    hasIcon: false,
  },
]
// 铁路线
export const hasTielu = [
  {
    name: "重庆-成都",
    coords: [
      [106.55116, 29.61203],
      [104.083736, 30.65318],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "成都-太原",
    coords: [
      [104.083736, 30.65318],
      [112.510097, 37.936464],
    ],
    lineStyle: { curveness: -0.1 },
  },

  {
    name: "太原-二连浩特",
    coords: [
      [112.510097, 37.936464],
      [111.982513, 43.655688],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "二连浩特-a",
    coords: [
      [111.982513, 43.655688],
      [102.3043, 51.2248],
    ],
    lineStyle: { curveness: -0.2 },
  },
  {
    name: "a-新西伯利亚州",
    coords: [
      [102.3043, 51.2248],
      [79.733157, 55.566475],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "新西伯利亚州-吉洪诺沃",
    coords: [
      [79.733157, 55.566475],
      [40.2936, 56.3469],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "吉洪诺沃-明斯克",
    coords: [
      [40.2936, 56.3469],
      [27.30, 53.51],
    ],
    lineStyle: { curveness: 0 },
  },

  {
    name: "重庆-荆州",
    coords: [
      [106.55116, 29.61203],
      [111.15, 29.26],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "荆州-武汉",
    coords: [
      [111.15, 29.26],
      [113.41, 29.58],
    ],
    lineStyle: { curveness: 0 },
  },
  {
    name: "武汉-上海",
    coords: [
      [113.41, 29.58],
      [121.451830, 31.175201],
    ],
    lineStyle: { curveness: 0.1 },
  },

  {
    name: "成都-b",
    coords: [
      [104.083736, 30.65318],
      [95.51293, 37.44693],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "b-霍尔果斯",
    coords: [
      [95.51293, 37.44693],
      [80.4852, 44.163962],
    ],
    lineStyle: { curveness: -0.1 },
  },
  {
    name: "霍尔果斯-塔什干",
    coords: [
      [80.4852, 44.163962],
      [69.2444, 41.3396],
    ],
    lineStyle: { curveness: 0.1 },
  },
  {
    name: "塔什干-阿克套",
    coords: [
      [69.2444, 41.3396],
      [51.1644, 43.6595],
    ],
    lineStyle: { curveness: 0.1 },
  },
  {
    name: "明斯克-马拉",
    coords: [
      [27.30, 53.51],
      [19.40624, 52.12210],
    ],
    lineStyle: { curveness: 0 },
  },
]
// 地球上无框文字
export const trainName = [
  {
    name: '白色无框文字',
    value: [55.1644, 44.6595],
    position: 'top',
    color: '#fff',
    rotate: 8,
  },
  {
    name: '深蓝无框文字',
    value: [90.774369, 50.14839],
    position: 'top',
    color: '#214D97',
    rotate: -10,
  },
  {
    name: '深蓝无框文字',
    value: [71.5598, 29.228952],
    position: 'top',
    color: '#214D97',
    rotate: 50,
  },
  {
    name: '深蓝无框文字',
    value: [81.538926, 18.785731],
    position: 'top',
    color: '#214D97',
    rotate: 45,
  },
]
// 飞线--线段
export const airLine = [
  {
    value: (Math.random() * 3000).toFixed(2),
    coords: [
      [106.55116, 29.61203], // 重庆
      [91.48, 22.18], // 吉大港
    ]
  },
  {
    value: (Math.random() * 3000).toFixed(2),
    coords: [
      [91.48, 22.18], // 吉大港
      [79.52, 6.55], // 科伦坡
    ]
  },
]
// mapFun.js
// 画铁路线
export const drawTrainLine = (arr, zIndex, lineType, lineColor, lineW) => {
    return {
      name: "铁路线",
      type: "lines",
      coordinateSystem: "geo",
      zlevel: zIndex,
      effect: {
        show: true,
        period: 6, // 动画周期
        trailLength: 0,
        color: "#7FFBFD", // 轨迹颜色
        symbol: `image://${trainIcon}`,  // 使用自定义图片作为symbol
        symbolSize: 10,
      },
      lineStyle: {
        normal: {
          type: lineType, // solid,dotted,dashed
          color: lineColor || "#fff",
          width: lineW || 3,
          opacity: 1,
          curveness: 0.3,
        },
      },
      data: arr.map((line) => ({
        coords: line.coords, // 起点和终点
        lineStyle: {
          normal: {
            curveness: line.lineStyle.curveness
          },
        },
      })),
    }
  }
 // 渲染大箭头标注
const imgMap = { // 全是自定义图片
    up: arrow1,
    left: arrow2,
    down: arrow3,
}
export const createArrow = (obj) => {
    return {
        name: obj.name,
        type: 'scatter',
        coordinateSystem: 'geo',
        symbol: `image://${imgMap[obj.type]}`,  // 使用自定义图片作为symbol
        symbolSize: [obj.x, obj.y],  // 设置图标大小
        label: {
            show: false,
            position: 'inside',  // 文字显示在图标内部
            formatter: '{b}',  // 显示标注点的名字
            color: '#fff',  // 文字颜色
        },
        data: [
            {
                name: obj.name,  // 图标上显示的文字
                value: obj.value,  // 标注的地理坐标(经纬度)
            },
        ],
        zlevel: 2,
    }
}
// 文字标注名称,利用散点图画出来
export const createNamePoint = (point) => {
    return {
        name: point.name,
        type: 'scatter',
        coordinateSystem: "geo",
        data: [{ name: point.name, value: point.value }],
        symbol: "circle",
        symbolSize: 0, // 不显示实际的点,只显示文字
        zlevel: 5,
        label: {
            show: true,
            formatter: '{b}',  // {b} 会显示数据名称
            position: point.position,
            fontSize: 10, // getFontSize()
            fontWeight: 'bold',
            color: point.color || "#fff",
            rotate: point.rotate,
        }
    }
}
// 带框的文字标注点,有自定义的图片
export const createNamePointImg = (obj) => {
    return {
        name: obj.name,
        type: 'scatter',
        coordinateSystem: 'geo',
        symbol: `image://${imageURL}`,  // 使用自定义图片作为symbol
        symbolSize: [120, 50],  // 设置图标大小
        label: {
            show: true,
            position: 'inside',  // 文字显示在图标内部
            formatter: '{b}',  // 显示标注点的名字
            color: '#fff',  // 文字颜色
            fontSize: 10,  // 文字大小
            offset: [5, -10],
        },
        data: [
            {
                name: obj.name,  // 图标上显示的文字
                value: obj.value,  // 标注的地理坐标(经纬度)
            },
        ],
        zlevel: 7,
    }
}

3.使用加载的json数据生成地图基础纹理,基本纹理中series包含第二步骤生成的那些东西

// earth3d.vue
// 生成基础纹理
generateBaseTexture(geoJson) {
	// 注册geo数据
      echarts.registerMap(this.code, geoJson);
      // 创建画布 生成纹理
      const canvas = document.createElement("canvas");
      this.baseTexture = echarts.init(canvas, null, { width: 1920, height: 1080 });

      this.baseTexture.setOption({
        backgroundColor: "rgba(26, 40, 71, 0.85)", //相当于海洋颜色
        geo: {
          type: "map",
          map: this.code,
          left: 0,
          top: 0,
          right: 0,
          bottom: 0,
          boundingCoords: [
            [-180, 90],
            [180, -90],
          ],
          roam: false,
          selectedMode: "single",
          select: {
            itemStyle: {
              areaColor: "#3ADAF4",
            },
            label: {
              show: true,
              color: "#000",
              fontSize: 10,
            },
          },
          emphasis: {
            disabled: true,
            itemStyle: {
              areaColor: "#3ADAF4",
            },
            label: {
              show: true,
              color: "#000",
              fontSize: 10,
            },
          },
          itemStyle: {
            areaColor: "#2C89F5", // #1EA3C8  rgba(44, 153, 245, 1)
            borderColor: "#314E85",
          },
        },
        series: [
          // 散点
          {
            type: "effectScatter",
            coordinateSystem: "geo",
            zlevel: 6,
            rippleEffect: { number: 0, brushType: "stroke" },
            label: { show: true, formatter: "{b}", distance: 5 },
            itemStyle: { normal: { color: "#fdf80c", borderColor: "#fff" } },
            data: this.effectScatter,
          },
          // 飞线
          {
            type: "lines",
            zlevel: 3,
            effect: {
              show: true,
              period: 4,
              trailLength: 0,
              symbol: "arrow",
              symbolSize: 12,
            },
            lineStyle: {
              normal: {
                color: "#fdf80c",
                width: 2,
                type: "solid",
                opacity: 1,
                curveness: -0.1,
              },
            },
            data: airLine,
          },
          ...this.trainLines,
          ...this.trainNames,
          ...this.arrows,
        ],
      });

      // 监听鼠标悬浮和点击事件
      this.baseTexture.on("click", (params) => {
        if (params.name === "重庆") {
          // 点击了重庆,要干点什么
        }
      });
}

4.使用globe配置对象绘制3d地球,在globe里面配置生成的纹理

// earth3d.vue
 // 绘制地球
    drawEarth() {
      const option = {
        globe: {
          baseTexture: this.baseTexture, // 基础纹理
          // globeRadius: getEarthRadius(),
          shading: "color", // color lambert // 'lambert' 通过经典的 lambert 着色表现光照带来的明暗
          light: {
            ambient: { intensity: 0.9 },
            main: { alpha: -45, beta: 45, intensity: 0.6 }, // 主光源
          },
          atmosphere: {
            show: true,
            color: "rgba(33, 97, 179, 0.6)",
            glowPower: 4,
          },
          viewControl: {
            projection: 'perspective',
            alpha: this.currentAlpha,
            beta: this.currentBeta,
            autoRotateSpeed: 0.6,
            autoRotate: true, // 开启自动旋转 true
            autoRotateAfterStill: 5, //鼠标停止操纵后,恢复自转时间
            // distance: 200, //默认视角距离主体距离
            distance: this.currentDistance, // 使用记录的距离
            minDistance: 40,       // 最小视角距离
            maxDistance: 400,      // 最大视角距离
            damping: 0, //鼠标旋转或缩放操作时的迟滞因子
            rotateSensitivity: 0.8, //旋转操作的灵敏度
            zoomSensitivity: 8, //缩放操作的灵敏度
            maxBeta: this.maxBeta,
          },
          layers: [
            {
              show: true,
              type: "blend",
              blendTo: "emission",
              texture: this.createLatitudeLongitudeGrid(),
              // distance: getEarthRadius() + 5, // 网格稍微在地球表面上方
              distance: this.currentDistance + 5,
            },
          ],
          top: "6%",
        },

        series: [],
      };

      this.myChart.clear();
      this.myChart.setOption(option);
    }
// 创建经纬网格
    createLatitudeLongitudeGrid() {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      const size = 1024;
      canvas.width = size;
      canvas.height = size;

      ctx.strokeStyle = "#114A72";
      ctx.lineWidth = 1;
      ctx.globalAlpha = 0.8;

      // 绘制经线
      for (let i = 0; i <= size; i += size / 18) {
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, size);
        ctx.stroke();
      }

      // 绘制纬线
      for (let j = 0; j <= size; j += size / 9) {
        ctx.beginPath();
        ctx.moveTo(0, j);
        ctx.lineTo(size, j);
        ctx.stroke();
      }
      console.log(canvas);

      return canvas;
    }

5.可开启一个定时器,让地球旋转起来

setTimer() {
      this.rotateTimer = setInterval(() => {
        this.initBeta++;
        if (this.initBeta > this.maxBeta) {
          clearInterval(this.rotateTimer);
          this.initBeta = 180;
          this.currentBeta = this.initBeta
          this.drawEarth();
          this.setTimer();
        }
      }, 1000);
    }

完整的代码:

// earth3d.vue
<template>
  <div class="earth-3d" ref="earth3dRef" id="earth3dRef"></div>
</template>

<script>
import * as echarts from "echarts";
import "echarts-gl"; // 引入 ECharts 3D 的模块 必须引入 ECharts GL 才能使用 WebGL 功能
import { GETNOBASE } from "api/request";
import { airLine, cityData, hasTielu, trainName } from "./js/map";
import {
  drawTrainLine,
  createNamePoint,
  createNamePointImg,
  createArrow,
} from "./js/echartsFun";
export default {
  data() {
    return {
      myChart: null,
      baseTexture: null,
      code: "world",
      effectScatter: [],
      trainLines: [], // 铁路线集合
      arrows: [], // 箭头集合
      trainNames: [], // 文字标注集合
      maxBeta: 220, // 设置旋转的最大角度
      rotateTimer: null,
      initBeta: 180,
      currentDistance: 230, // 添加当前视角距离属性
      currentAlpha: 30, // 添加当前视角角度属性
      currentBeta: 180, // 添加当前视角角度属性
    };
  },
  mounted() {
    if (!this.webglSupport()) {
      this.message("您的浏览器不支持 WebGL,请切换或升级浏览器");
      return;
    }
    this.$nextTick(() => {
      this.myChart = echarts.init(this.$refs.earth3dRef);
      this.initializeMap();
    });
  },
  beforeDestroy() {
    if (this.myChart) {
      this.myChart.dispose();
    }
    if (this.rotateTimer) {
      clearInterval(this.rotateTimer);
      this.initBeta = 180;
    }
  },
  methods: {
    // 这段代码会在组件挂载时检查用户的浏览器是否支持 WebGL。如果不支持,会给出提示信息,而不会继续执行图表初始化
    webglSupport() {
      try {
        const canvas = document.createElement("canvas");
        return !!(
          window.WebGLRenderingContext &&
          (canvas.getContext("webgl") || canvas.getContext("experimental-webgl"))
        );
      } catch (e) {
        return false;
      }
    },
    // 初始化地图
    initializeMap() {
      this.loadGeoJson().then((geoJson) => {
        this.prepareData();
        this.generateBaseTexture(geoJson);
        this.drawEarth();
        this.setTimer();
        // 获取地图容器元素
        const mapContainer = this.$refs.earth3dRef;
        // 添加滚轮事件监听
        mapContainer.addEventListener('wheel', () => {
          // 获取当前视图的配置
          const viewControl = this.myChart.getOption().globe[0].viewControl;
          this.currentDistance = viewControl.distance;
        });
      });
    },
    setTimer() {
      this.rotateTimer = setInterval(() => {
        this.initBeta++;
        if (this.initBeta > this.maxBeta) {
          clearInterval(this.rotateTimer);
          this.initBeta = 180;
          this.currentBeta = this.initBeta
          this.drawEarth();
          this.setTimer();
        }
      }, 1000);
    },

    // 加载地理信息
    async loadGeoJson() {
      const res = await GETNOBASE("./map-geojson/worldZh.json");
      return res.data;
    },

    // 准备绘制数据
    prepareData() {
      this.prepareEffectScatterData();
      this.prepareTrainLines();
      this.prepareArrows();
      this.prepareTrainNames();
    },

    // 生成散点数据
    prepareEffectScatterData() {
      cityData.forEach((point) => {
        this.effectScatter.push({
          name: point.name,
          value: point.value,
          symbol: point.hasIcon ? `image://${point.icon}` : "circle", // 使用自定义图片作为symbol
          symbolSize: point.name === "重庆" ? 12 : 8,
          z: point.name === "重庆" ? 5 : 2,
          label: {
            position: point.position,
            fontSize: point.name === "重庆" ? 16 : 14,
            // fontWeight: 'bold',
            color: point.name === "重庆" ? "#e51c1c" : "#fff",
          },
        });
      });
    },

    // 生成铁路线数据
    prepareTrainLines() {
      this.trainLines = [
        drawTrainLine(hasTielu, 4, "solid", "rgba(255, 255, 255, 1)", 3),
        drawTrainLine(hasTielu, 4, [10, 10], "#000", 2),
      ];
    },

    // 生成箭头标注
    prepareArrows() {
      this.arrows = [
        createArrow({
          name: "向上的箭头",
          value: [102.3043, 40.2248],
          x: 20,
          y: 25,
          type: "up",
        }),
        createArrow({
          name: "向左的箭头",
          value: [71.511721, 42.302711],
          x: 30,
          y: 15,
          type: "left",
        }),
        createArrow({
          name: "向下的箭头",
          value: [99.36, 17.58],
          x: 15,
          y: 30,
          type: "down",
        }),
      ];
    },

    // 生成文字标注
    prepareTrainNames() {
      this.trainNames = [
        ...trainName.map((item) => createNamePoint(item)), // 印在地球上不带框框的文字
        createNamePointImg({ name: "勒是铁路线", value: [102.11293, 42.14693] }), // 带框框的提示文字
      ];
    },

    // 生成基础纹理
    generateBaseTexture(geoJson) {
      // 注册geo数据
      echarts.registerMap(this.code, geoJson);
      // 创建画布 生成纹理
      const canvas = document.createElement("canvas");
      this.baseTexture = echarts.init(canvas, null, { width: 1920, height: 1080 });

      this.baseTexture.setOption({
        backgroundColor: "rgba(26, 40, 71, 0.85)", //相当于海洋颜色
        geo: {
          type: "map",
          map: this.code,
          left: 0,
          top: 0,
          right: 0,
          bottom: 0,
          boundingCoords: [
            [-180, 90],
            [180, -90],
          ],
          roam: false,
          selectedMode: "single",
          select: {
            itemStyle: {
              areaColor: "#3ADAF4",
            },
            label: {
              show: true,
              color: "#000",
              fontSize: 10,
            },
          },
          emphasis: {
            disabled: true,
            itemStyle: {
              areaColor: "#3ADAF4",
            },
            label: {
              show: true,
              color: "#000",
              fontSize: 10,
            },
          },
          itemStyle: {
            areaColor: "#2C89F5", // #1EA3C8  rgba(44, 153, 245, 1)
            borderColor: "#314E85",
          },
        },
        series: [
          // 散点
          {
            type: "effectScatter",
            coordinateSystem: "geo",
            zlevel: 6,
            rippleEffect: { number: 0, brushType: "stroke" },
            label: { show: true, formatter: "{b}", distance: 5 },
            itemStyle: { normal: { color: "#fdf80c", borderColor: "#fff" } },
            data: this.effectScatter,
          },
          // 飞线
          {
            type: "lines",
            zlevel: 3,
            effect: {
              show: true,
              period: 4,
              trailLength: 0,
              symbol: "arrow",
              symbolSize: 12,
            },
            lineStyle: {
              normal: {
                color: "#fdf80c",
                width: 2,
                type: "solid",
                opacity: 1,
                curveness: -0.1,
              },
            },
            data: airLine,
          },
          ...this.trainLines,
          ...this.trainNames,
          ...this.arrows,
        ],
      });

      // 监听鼠标悬浮和点击事件
      this.baseTexture.on("click", (params) => {
        if (params.name === "重庆") {
          // 点击了重庆,要干点什么
        }
      });

    },

    // 绘制地球
    drawEarth() {
      const option = {
        globe: {
          baseTexture: this.baseTexture, // 基础纹理
          shading: "color", // color lambert // 'lambert' 通过经典的 lambert 着色表现光照带来的明暗
          light: {
            ambient: { intensity: 0.9 }, 
            main: { alpha: -45, beta: 45, intensity: 0.6 }, // 主光源
          },
          atmosphere: {// 利用大气层实现发光效果
            show: true,
            color: "rgba(33, 97, 179, 0.6)",
            glowPower: 4,
          },
          viewControl: {
            projection: 'perspective',
            alpha: this.currentAlpha,
            beta: this.currentBeta,
            autoRotateSpeed: 0.6,
            autoRotate: true, // 开启自动旋转 true
            autoRotateAfterStill: 5, //鼠标停止操纵后,恢复自转时间
            // distance: 200, //默认视角距离主体距离
            distance: this.currentDistance, // 使用记录的距离
            minDistance: 40,       // 最小视角距离
            maxDistance: 400,      // 最大视角距离
            damping: 0, //鼠标旋转或缩放操作时的迟滞因子
            rotateSensitivity: 0.8, //旋转操作的灵敏度
            zoomSensitivity: 8, //缩放操作的灵敏度
            maxBeta: this.maxBeta,
          },
          layers: [
            {
              show: true,
              type: "blend",
              blendTo: "emission",
              texture: this.createLatitudeLongitudeGrid(),
              // distance: getEarthRadius() + 5, // 网格稍微在地球表面上方
              distance: this.currentDistance + 5,
            },
          ],
          top: "6%",
        },
        series: [],
      };
      this.myChart.clear();
      this.myChart.setOption(option);
    },

    // 创建经纬网格
    createLatitudeLongitudeGrid() {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      const size = 1024;
      canvas.width = size;
      canvas.height = size;

      ctx.strokeStyle = "#114A72";
      ctx.lineWidth = 1;
      ctx.globalAlpha = 0.8;

      // 绘制经线
      for (let i = 0; i <= size; i += size / 18) {
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, size);
        ctx.stroke();
      }

      // 绘制纬线
      for (let j = 0; j <= size; j += size / 9) {
        ctx.beginPath();
        ctx.moveTo(0, j);
        ctx.lineTo(size, j);
        ctx.stroke();
      }
      return canvas;
    },


    // 消息提示
    message(text) {
      this.$Message({
        text: text,
        type: "warning",
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.earth-3d {
  width: 100%;
  height: 100%;
}
</style>

现在你已经拥有 了一个可以旋转的3d地球了