第11章 二次开发指南
11.1 二次开发概述
map-360-demo 是一个结构清晰、扩展性良好的演示项目。本章介绍如何在此基础上进行二次开发,包括新增场景、扩展标记类型、自定义样式、接入真实数据等。
11.2 新增场景
11.2.1 场景数据结构
场景定义在 src/data/scenes.ts:
// scenes.ts:11-30
export const SCENES: Scene[] = [
{
id: 'mercantour',
name: '法国默康图尔国家公园',
description: '...',
panorama: BASE_URL + 'sphere.jpg',
coordinates: [6.78677, 44.58241],
bearing: '120deg',
defaultZoom: 14,
},
{
id: 'key-biscayne',
name: '美国比斯坎湾灯塔',
description: '...',
panorama: BASE_URL + 'tour/key-biscayne-1.jpg',
coordinates: [-80.1556, 25.6742],
bearing: '0deg',
defaultZoom: 15,
},
]
11.2.2 新增场景步骤
- 准备全景图:将全景图片放到
public/或使用 CDN 地址 - 添加场景对象:在
SCENES数组中添加新场景 - 配置参数:
id:唯一标识name:场景名称panorama:全景图地址coordinates:拍摄点经纬度[lng, lat]bearing:方位角(全景正北方向对应的地理方位角)defaultZoom:地图默认缩放级别
11.2.3 添加默认标记
在 DEFAULT_MARKERS 数组中添加新场景的默认标记:
// scenes.ts:32-105
{
id: 'm8',
sceneId: 'mercantour',
name: '新标记',
description: '标记描述',
position: { yaw: 0.5, pitch: 0.1 }, // 全景球形坐标
coordinates: [6.78677, 44.58241], // GPS 经纬度
createdAt: Date.now(),
type: 'info',
}
11.3 扩展标记类型
11.3.1 修改类型定义
在 src/types/index.ts 中扩展 MarkerType:
type MarkerType = 'info' | 'link' | 'video' // 新增 video 类型
11.3.2 更新转换逻辑
在 toPsvMarkerConfig 中处理新类型:
// scenes.ts:117-140
export function toPsvMarkerConfig(m: MarkerData): PsvMarkerConfig {
const isLink = m.type === 'link'
const isVideo = m.type === 'video'
// 根据类型选择图标和样式
return {
// ...
image: isLink ? LINK_ICON : isVideo ? VIDEO_ICON : BASE_URL + 'pictos/pin-blue.png',
className: isLink ? 'psv-marker--link' : isVideo ? 'psv-marker--video' : '',
// ...
}
}
11.3.3 更新点击分发逻辑
在 App.vue 的 onMarkerClick 中处理新类型:
function onMarkerClick(id: string) {
const marker = currentMarkers.value.find(m => m.id === id)
if (!marker) return
if (marker.type === 'link' && marker.targetSceneId) {
// 切换场景
} else if (marker.type === 'video') {
// 播放视频
} else {
psvRef.value?.gotoMarker(id) // 旋转视角
}
}
11.4 自定义样式
11.4.1 修改 PSV 样式
在 src/styles/psv.css 中覆盖 PSV 默认样式:
/* 修改标记 tooltip 样式 */
.psv-marker-content {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
11.4.2 新增标记样式
为不同类型的标记添加样式:
.psv-marker--video {
/* 视频标记样式 */
}
11.5 接入真实数据
11.5.1 替换静态场景
将 SCENES 改为从 API 获取:
// 在 useAppState.ts 中
async function loadScenes() {
const res = await fetch('/api/scenes')
scenes.value = await res.json()
}
11.5.2 替换 localStorage 持久化
将 localStorage 持久化改为后端存储:
// 在 useAppState.ts 中
watch(markers, async (val) => {
await fetch('/api/markers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(val),
})
}, { deep: true })
11.6 常见扩展场景
11.6.1 添加更多插件
Photo Sphere Viewer 提供丰富的插件,如:
- GalleryPlugin:全景图画廊
- VirtualTourPlugin:虚拟漫游
- GyroscopePlugin:陀螺仪控制
- AutorotatePlugin:自动旋转
import { GalleryPlugin } from '@photo-sphere-viewer/gallery-plugin'
viewer = new Viewer({
// ...
plugins: [
GalleryPlugin.withConfig({ /* 配置 */ }),
// 其他插件
],
})
11.6.2 添加自定义 UI
在 App.vue 中添加新的 UI 组件,通过 composable 管理状态。
11.6.3 接入真实地图服务
将 PlanPlugin 的默认 OSM 底图替换为其他地图服务(如天地图、高德地图),需要配置 configureLeaflet 或自定义瓦片源。
11.7 开发注意事项
11.7.1 不要使用 configureLeaflet
重要:不要使用 configureLeaflet 选项。它的语义是”完全接管 Leaflet 配置”,传入后默认 OSM 底图不会添加,地图会变成灰色空面板。点击监听改为创建后通过 getLeaflet() 挂载。
11.7.2 场景切换复用 viewer
场景切换时复用 viewer 而非销毁重建,保留 WebGL 上下文和瓦片缓存,切换更快且带过渡动画。
11.7.3 预览 pin 避免重叠
编辑模式下若位置与原有标记完全相同则不显示预览 pin,避免与蓝 pin 重叠。
11.7.4 坐标换算注意 cos 因子
经度/纬度每度地面距离不同(相差 cos(纬度) 因子),必须先在”米”空间按方位角分解再换算回度数,否则标记落在地图上的实际方向会偏转(中纬度可达 10°)。
11.7.5 HTML 转义防注入
标记名称/描述在渲染到 PSV tooltip 前经过 HTML 转义,防止 XSS 注入。
11.8 项目扩展建议
- 引入 Pinia:如果状态管理变得复杂,可引入 Pinia 替代模块级单例
- 接入后端:将场景和标记数据迁移到后端,支持多用户协作
- 添加更多场景:扩展场景库,支持更多地理位置
- 自定义标记图标:支持用户上传自定义图标
- 移动端适配:优化移动端触控体验
11.9 总结
map-360-demo 是一个结构清晰、扩展性良好的全景地图联动演示项目。通过本章的学习,你应该能够:
- 理解项目的整体架构与数据流
- 掌握 Photo Sphere Viewer 的插件体系
- 理解全景与地图联动的实现原理
- 掌握坐标换算的核心算法
- 能够在此基础上进行二次开发
至此,map-360-demo 的完整教程结束。祝你开发愉快!