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

旋转3D地球效果如下

整体思路:

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

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

javascript 复制代码
// 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需要的配置对象
javascript 复制代码
// 准备绘制数据
// 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包含第二步骤生成的那些东西

javascript 复制代码
// 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里面配置生成的纹理

javascript 复制代码
// 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.可开启一个定时器,让地球旋转起来

javascript 复制代码
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);
    }

完整的代码:

javascript 复制代码
// 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地球了

相关推荐
随风九天42 分钟前
使用 Nginx 进行前端灰度发布的策略与实践
运维·前端·nginx·前端灰度发布
黄Java1 小时前
SVG中linearGradient的id冲突的显隐问题深度解析
前端·svg
小咕聊编程1 小时前
【含文档+PPT+源码】基于SpringBoot和Vue的编程学习系统
vue.js·spring boot·学习
蜗牛快跑1232 小时前
通过尤大“围绕Vite的前端统一框架”分享,看未来前端发展趋势
前端
skywalk81632 小时前
Mac下安装Zed以及Zed对MCP(模型上下文协议)的支持
服务器·前端·macos
陈龙龙的陈龙龙2 小时前
macOS 安装 Homebrew、nvm 及安装切换 node 版本
前端·macos·bash
asphyxia2 小时前
老龄化项目问题解决
前端
xiefl2 小时前
如何本地调试vue core源码
vue.js
SaebaRyo2 小时前
作为一个前端er如何了解LLM(大语言模型)
前端·llm
悬炫2 小时前
深入解析浏览器渲染原理与性能优化策略
前端·javascript