用Vue + Mapbox 制作太阳系图

用Vue + Mapbox 制作太阳系图

以下是使用 Vue + Mapbox 制作太阳系图的详细步骤和代码示例:

一、项目准备

  1. 创建 Vue 项目

    vue create solar-system-map
    cd solar-system-map
    npm install mapbox-gl --save
    
  2. 引入 Mapbox 样式
    public/index.html 或 Vue 组件中添加:

    <link href="https://api.mapbox.com/mapbox-gl-js/v2.18.0/mapbox-gl.css" rel="stylesheet" />
    

二、组件开发 (SolarSystem.vue)

<template>
  <div style="height: 600px;" ref="mapContainer"></div>
</template>

<script>
import mapboxgl from 'mapbox-gl';

// 行星数据(距离太阳的相对距离、颜色、半径)
const planetsData = [
  { name: '太阳', distance: 0, radius: 20, color: '#FFD700' },
  { name: '水星', distance: 0.4, radius: 3, color: '#8A8A8A' },
  { name: '金星', distance: 0.7, radius: 5, color: '#F0B27A' },
  { name: '地球', distance: 1.0, radius: 5, color: '#2E86C1' },
  { name: '火星', distance: 1.5, radius: 4, color: '#E74C3C' },
  { name: '木星', distance: 5.2, radius: 12, color: '#F4D03F' },
  { name: '土星', distance: 9.5, radius: 10, color: '#D4AC0D' },
  { name: '天王星', distance: 19.2, radius: 8, color: '#5DADE2' },
  { name: '海王星', distance: 30.1, radius: 8, color: '#2980B9' },
];

export default {
  data() {
    return {
      map: null,
      center: [0, 0], // 太阳中心坐标(经度, 纬度)
      zoom: 0, // 初始缩放级别
    };
  },
  mounted() {
    this.initMap();
    this.addSunAndPlanets();
  },
  beforeUnmount() {
    // 销毁地图实例
    if (this.map) {
      this.map.remove();
      this.map = null;
    }
  },
  methods: {
    initMap() {
      // 初始化 Mapbox 地图(隐藏默认地图样式,使用黑色背景)
      this.map = new mapboxgl.Map({
        container: this.$refs.mapContainer,
        style: {
          version: 8,
          sources: {},
          layers: [{
            id: 'background',
            type: 'background',
            paint: { 'background-color': '#000' },
          }],
        },
        center: this.center,
        zoom: this.zoom,
        interactive: false, // 禁用地图交互
      });
    },

    addSunAndPlanets() {
      // 添加太阳(中心圆)
      this.addCircleLayer('sun', this.center, 20, '#FFD700');

      // 添加行星
      planetsData.forEach((planet, index) => {
        if (planet.name === '太阳') return; // 跳过太阳

        // 计算行星位置(极坐标转经纬度,这里简化为环形分布)
        const angle = (index * 40) * (Math.PI / 180); // 每个行星间隔 40 度
        const distance = planet.distance * 100000; // 相对距离放大(调整视觉效果)
        const lon = this.center[0] + (distance * Math.cos(angle)) / (111319.5 * Math.cos(this.center[1] * Math.PI / 180)); // 经度计算(1°≈111km)
        const lat = this.center[1] + (distance * Math.sin(angle)) / 111319.5; // 纬度计算
        const position = [lon, lat];

        this.addCircleLayer(planet.name, position, planet.radius, planet.color);
      });
    },

    addCircleLayer(id, position, radius, color) {
      // 添加圆圈图层
      this.map.addSource(id, {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: [{
            type: 'Feature',
            geometry: { type: 'Point', coordinates: position },
          }],
        },
      });

      this.map.addLayer({
        id,
        type: 'circle',
        source: id,
        paint: {
          'circle-radius': radius,
          'circle-color': color,
          'circle-stroke-width': 1,
          'circle-stroke-color': '#fff',
        },
      });
    },
  },
};
</script>

三、关键功能说明

  1. 地图初始化

    • 使用自定义样式(纯黑色背景),禁用地图交互(interactive: false),专注于可视化。
    • 中心坐标设为 [0, 0](太阳位置),缩放级别 zoom: 0 确保足够大的显示范围。
  2. 行星数据结构

    • distance 表示相对太阳的距离(以地球为 1 单位,按比例缩放)。
    • radiuscolor 定义视觉样式。
  3. 坐标计算

    • 极坐标转经纬度:将行星按环形分布,角度间隔 40°,距离按比例放大以适配地图显示。
    • 经纬度转换公式:利用地球表面距离公式(1°纬度≈111km,1°经度≈111km×cos(纬度))。
  4. 圆圈图层

    • 每个天体(太阳和行星)使用独立的 GeoJSON 数据源和圆圈图层,方便后续动画更新。

四、优化与扩展

  1. 动画效果(行星旋转)
    添加定时器,定期更新行星的角度和坐标:

    mounted() {
      this.initMap();
      this.addSunAndPlanets();
      this.startAnimation(); // 启动动画
    },
    methods: {
      startAnimation() {
        setInterval(() => {
          planetsData.forEach((planet, index) => {
            if (planet.name === '太阳') return;
            const angle = (index * 40 + Date.now() * 0.1) * (Math.PI / 180); // 角度随时间变化
            // 重新计算坐标并更新数据源
            const newPosition = this.calculatePosition(angle, planet.distance);
            this.updatePlanetPosition(planet.name, newPosition);
          });
        }, 100);
      },
      calculatePosition(angle, distance) {
        // 同上坐标计算逻辑
      },
      updatePlanetPosition(id, position) {
        const source = this.map.getSource(id);
        source.setData({
          type: 'FeatureCollection',
          features: [{
            type: 'Feature',
            geometry: { type: 'Point', coordinates: position },
          }],
        });
      },
    },
    
  2. 交互增强

    • 添加鼠标悬停提示(使用 mapbox-glqueryRenderedFeatures 方法)。
    • 支持缩放和拖动(取消 interactive: false,并调整视图逻辑)。
  3. 3D 效果

    • 使用 Mapbox 的 3D 模型(3d-model 图层)或结合 Three.js 实现更真实的 3D 太阳系。

五、运行项目

npm run serve

打开浏览器访问 http://localhost:8080,即可看到太阳系可视化效果,行星按轨道分布并可添加旋转动画。

通过以上步骤,利用 Mapbox 的自定义图层功能,结合 Vue 的组件化开发,实现了太阳系的交互式可视化。可根据需求进一步优化数据精度和视觉效果。

在 Mapbox 中实现太阳和行星的纹理(如真实表面纹理或图片贴图),需要利用 自定义图标(Symbol Layer)Canvas 绘制(因 Mapbox 的 circle-layer 不支持直接纹理贴图)。以下是具体实现方案:

六、如何增加太阳和行星的纹理

Mapbox 的 symbol-layer 支持加载外部图片作为图标,可直接显示纹理。步骤如下:

1. 准备纹理图片

从公开资源(如 NASA、维基百科)下载天体纹理图(建议正方形图片,尺寸如 64x64px128x128px),命名为 sun.pngearth.png 等,放入项目 public 目录或通过 URL 引用。
示例纹理资源:

2. 修改组件代码(替换 circle-layersymbol-layer
<template>
  <div style="height: 600px;" ref="mapContainer"></div>
</template>

<script>
import mapboxgl from 'mapbox-gl';

const planetsData = [
  { 
    name: '太阳', 
    distance: 0, 
    icon: '/sun.png',  // 纹理图片路径
    size: 40,          // 图标大小(px)
    anchor: 'center'   // 图标锚点
  },
  { 
    name: '地球', 
    distance: 1.0, 
    icon: '/earth.png', 
    size: 10, 
    anchor: 'center' 
  },
  // 其他行星...
];

export default {
  mounted() {
    this.initMap();
    this.addSunAndPlanets();
  },
  methods: {
    initMap() {
      this.map = new mapboxgl.Map({
        container: this.$refs.mapContainer,
        style: 'mapbox://styles/mapbox/dark-v11', // 深色背景样式
        center: [0, 0],
        zoom: 0,
        interactive: false,
      });
    },

    addSunAndPlanets() {
      planetsData.forEach((planet) => {
        // 加载图标(需在添加图层前注册)
        this.map.loadImage(planet.icon, (error, image) => {
          if (error) throw error;
          // 注册自定义图标
          this.map.addImage(planet.name, image, {
            pixelRatio: 2, // 高清显示(可选)
          });

          // 添加符号图层(显示纹理图标)
          this.map.addSource(planet.name, {
            type: 'geojson',
            data: {
              type: 'FeatureCollection',
              features: [{
                type: 'Feature',
                geometry: { 
                  type: 'Point', 
                  coordinates: this.calculatePosition(planet.distance) // 计算坐标
                },
              }],
            },
          });

          this.map.addLayer({
            id: planet.name,
            type: 'symbol',
            source: planet.name,
            layout: {
              'symbol-placement': 'point',
              'icon-image': planet.name, // 引用注册的图标
              'icon-size': planet.size / 64, // 图标大小(64px 为 Mapbox 图标默认基准)
              'icon-anchor': planet.anchor, // 图标中心对齐
              'icon-rotation': 0, // 禁止图标随地图旋转
            },
          });
        });
      });
    },

    // 计算行星坐标(极坐标转经纬度,同前)
    calculatePosition(distance) {
      const angle = Math.random() * 360 * (Math.PI / 180); // 示例角度(可替换为轨道计算)
      const distanceScale = distance * 100000; // 距离缩放
      const lon = 0 + (distanceScale * Math.cos(angle)) / (111319.5 * Math.cos(0));
      const lat = 0 + (distanceScale * Math.sin(angle)) / 111319.5;
      return [lon, lat];
    },
  },
};
</script>
3. 进阶方案:使用 Canvas 绘制纹理(复杂纹理或动态效果)

若需更精细的纹理(如云层动画、环形山等),可通过 Canvas 动态绘制纹理,再作为图标加载:

  • 生成 Canvas 纹理
// 生成太阳纹理(示例:带光斑的圆形)
function createSunCanvas() {
  const canvas = document.createElement('canvas');
  canvas.width = 64;
  canvas.height = 64;
  const ctx = canvas.getContext('2d');
  ctx.beginPath();
  ctx.arc(32, 32, 30, 0, 2 * Math.PI);
  ctx.fillStyle = '#FFD700';
  ctx.shadowColor = 'rgba(255, 215, 0, 0.5)';
  ctx.shadowBlur = 10;
  ctx.fill();
  return canvas;
}
  • 注册 Canvas 生成的图标
// 在 addSunAndPlanets 中替换图片加载
const canvas = createSunCanvas();
this.map.addImage('sun', canvas);
4. 关键配置说明
  1. 图标锚点(icon-anchor

    • 设置为 center 确保图标中心与坐标点对齐,避免偏移。
  2. 图标大小(icon-size

    • Mapbox 图标默认基准为 64pxicon-size: 1 表示原始大小。若图片为 128px,设置 icon-size: 0.5 可缩小为一半。
  3. 跨域问题

    • 本地图片放入 public 目录,通过相对路径(如 /textures/earth.png)引用,避免跨域。
5. 效果优化
  1. 高清显示

    • 使用 pixelRatio: window.devicePixelRatio 适配视网膜屏幕:
      this.map.addImage(planet.name, image, { pixelRatio: window.devicePixelRatio });
      
  2. 纹理混合

    • 在图标图层下方添加半透明的 circle-layer 作为光晕(如太阳的光芒):
      // 添加光晕图层
      this.map.addLayer({
        id: `${planet.name}-glow`,
        type: 'circle',
        source: planet.name,
        paint: {
          'circle-radius': planet.size * 1.2,
          'circle-color': 'rgba(255, 215, 0, 0.3)',
          'circle-blur': 0.5,
        },
      });
      
  3. 动态纹理(如行星自转)

    • 通过定时器更新 Canvas 绘制内容,重新注册图标并刷新图层:
      setInterval(() => {
        const newCanvas = drawRotatedEarth(); // 绘制自转后的地球纹理
        this.map.removeImage('earth');
        this.map.addImage('earth', newCanvas);
      }, 1000);
      
6.资源推荐
  1. 免费天体纹理库

  2. Mapbox 图标最佳实践

通过上述方法,可在 Mapbox 中实现带纹理的太阳系天体显示,结合 Vue 的响应式特性,还能动态更新纹理或添加交互(如点击显示天体信息)。

Vue.jsMapbox 是两个非常流行的工具,用于构建用户界面和提供强大的地功能。Vue 是一个轻量级的前端框架,以其组件化开发和易于上手的特点受到开发者喜爱。Mapbox 则是一个提供了丰富的地服务、地样式和交互功能的平台。 当你将 VueMapbox 结合使用时,可以实现以下几个关键点: 1. 安装依赖:首先,你需要在 Vue 项目中安装 `@vuegis/vue-mapbox-gl` 或者直接使用 Mapbox GL JS(原生库),这是官方推荐的 Vue 组件化解决方案。 2. 创建地组件:创建一个 Vue 组件,例如 `Map.vue`,引入 Mapbox GL JS,并设置初始化地的方法,包括设置中心位置、比例尺、地样式等。 ```html <template> <div ref="map"></div> </template> <script> import { mapboxgl, Map } from '@vuegis/vue-mapbox-gl' export default { components: { MapboxGL: mapboxgl.Map, }, mounted() { this.createMap() }, methods: { createMap() { this.$refs.map .el .style = 'width: 100%; height: 400px;'; // 设置样式 const map = new Map(this.$refs.map, { center: [51.505, -0.09], // 地中心点 zoom: 13, // 初始缩放级别 style: 'mapbox://styles/mapbox/streets-v11', // 使用预设样式 }); }, }, } </script> ``` 3. 动态数据绑定:Vue 的响应式系统使得地可以根据数据变化动态调整,比如根据用户的输入改变地或添加标记。 4. 层和交互:Mapbox 提供了各种层(如矢量层、影像层等)和交互元素(如点击事件、鼠标悬停事件),可以在 Vue 中轻松使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值