第09章:地图集成与辅助功能
前面章节的系统学习,你已经掌握了 Viewer 核心操作、标记系统、图集管理和视频全景。从本章开始,我们把视野扩展到更丰富的辅助功能——地图集成、指南针、自动旋转、设置面板等。这些插件并非必须,但它们能让你的全景应用从”能用”跃升到”好用”,在房地产看房、景区导览、虚拟展厅等场景中尤其关键。
Photo-Sphere-Viewer 提供了两种地图插件:MapPlugin(自定义静态地图)和 PlanPlugin(基于 Leaflet 的真实地理地图),二者定位不同,各有适用场景。本章还将一并介绍 CompassPlugin(指南针)、AutorotatePlugin(自动旋转)、SettingsPlugin / ResolutionPlugin(设置面板与分辨率切换)、VisibleRangePlugin(可见范围限制)以及 OverlaysPlugin(叠加层),并讨论多插件组合的最佳实践。
9.1 MapPlugin 详解(自定义地图)
9.1.1 功能定位
MapPlugin 在 Viewer 界面上叠加一个自定义地图图片——它不是一个 GIS 地图,而是一张静态图片(PNG / JPG / SVG),你可以在上面放置热点标记。它的典型应用场景包括:
- 室内平面图:在 VR 看房中,将楼层平面图作为地图,标记客厅、卧室、厨房等位置
- 景区导览图:使用手绘地图或景区示意图,标记观景台、休息区等点位
- 展馆布局图:为虚拟展厅配置展区分布图,观众可以快速跳转到目标区域
- 工厂/仓库示意图:标记不同工位或货架在全景中的对应位置
MapPlugin 的地图底图是一张自定义图片,因此你可以使用任何设计工具(Photoshop、Figma、甚至手绘图扫描件)来制作。地图支持缩放、拖拽平移,并且地图上的中心指针会随全景视角实时旋转,直观显示当前朝向。
9.1.2 安装与导入
npm install @photo-sphere-viewer/map-plugin
导入代码:
// JS 模块
import { MapPlugin } from '@photo-sphere-viewer/map-plugin';
// CSS(必须)
import '@photo-sphere-viewer/map-plugin/index.css';
CDN 方式则需要在 Import Map 中添加映射,并在 HTML 中引入 CSS:
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/map-plugin@5/index.css" />
<script type="importmap">
{
"imports": {
"@photo-sphere-viewer/map-plugin": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/map-plugin@5/index.module.js"
}
}
</script>
9.1.3 核心配置选项
MapPlugin 的配置项非常丰富,按功能分类如下。
必填配置:
| 选项 | 类型 | 说明 |
|---|---|---|
imageUrl |
string |
地图图片的 URL,必须指定 |
center |
{ x: number, y: number } |
全景图在地图上的坐标位置(单位:像素),必填 |
外观控制:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size |
string |
'200px' |
地图容器尺寸,支持 px、rem、vh 等单位 |
position |
string |
'bottom left' |
地图在 Viewer 中的位置,可选 top / bottom 组合 left / right |
shape |
'round' \| 'square' |
'round' |
地图形状:圆形或方形 |
rotation |
number \| string |
0 |
地图旋转角度,使地图方向与全景图一致,如 '45deg' |
static |
boolean |
false |
若为 true,地图不旋转,只有中心指针旋转以指示朝向 |
缩放与可视性:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
defaultZoom |
number |
100 |
默认缩放级别(百分比) |
minZoom |
number |
20 |
最小缩放级别 |
maxZoom |
number |
200 |
最大缩放级别 |
visibleOnLoad |
boolean |
true |
初始是否显示地图 |
minimizeOnHotspotClick |
boolean |
true |
点击热点后是否自动最小化地图 |
视觉元素:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pinImage |
string |
默认 SVG | 中心指针图片(SVG 或图片 URL) |
pinSize |
number |
35 |
中心指针的大小 |
coneColor |
string |
'#1E78E6' |
视场锥形区域颜色,设为 null 禁用 |
coneSize |
number |
40 |
视场锥形区域大小 |
overlayImage |
string |
默认 SVG | 地图上层叠图片(如指南针刻度),设为 null 禁用 |
热点配置(hotspots):
热点是地图上的可点击标记点。每个热点有两种定位方式:
- 角度 + 距离:
yaw(偏航角度)+distance(距中心点的像素距离) - 绝对坐标:
x+y(地图图片上的像素坐标)
hotspots: [
{
id: 'kitchen',
yaw: '45deg',
distance: 120,
tooltip: '厨房',
},
{
id: 'bedroom',
x: 400,
y: 150,
tooltip: '主卧',
},
]
每个热点还支持 style 属性来覆盖全局的 spotStyle。
9.1.4 方法
| 方法 | 说明 |
|---|---|
setImage(url, center?, rotation?) |
更换地图图片,可同时更新中心点和旋转角度 |
setCenter(center, resetView=true) |
更新全景图在地图上的位置,resetView=false 时不移动地图视角 |
setHotspots(hotspots) |
设置热点数组 |
clearHotspots() |
移除所有热点 |
setZoom(level) |
设置缩放级别(介于 minZoom 和 maxZoom 之间) |
open() / close() |
展开/折叠地图 |
maximize() / minimize() |
最大化/最小化地图(仅在展开状态有效) |
9.1.5 事件
| 事件名 | 参数 | 说明 |
|---|---|---|
select-hotspot |
{ hotspotId: string } |
用户点击地图上的热点 |
view-changed |
view: 'maximized' \| 'normal' \| 'closed' |
地图视图状态变更 |
9.2 MapPlugin 完整示例
下面是一个完整的室内看房场景示例,使用楼层平面图作为地图,标记客厅、主卧、次卧和阳台在全景图中的对应位置。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>室内看房 - MapPlugin 示例</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; }
#viewer { width: 100%; height: 100%; }
</style>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5/index.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/map-plugin@5/index.css" />
</head>
<body>
<div id="viewer"></div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",
"@photo-sphere-viewer/core": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5/index.module.js",
"@photo-sphere-viewer/map-plugin": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/map-plugin@5/index.module.js"
}
}
</script>
<script type="module">
import { Viewer } from '@photo-sphere-viewer/core';
import { MapPlugin } from '@photo-sphere-viewer/map-plugin';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'https://photo-sphere-viewer-data.netlify.app/pano/living-room.jpg',
caption: '三室两厅 · 客厅视角',
navbar: ['zoom', 'fullscreen', 'caption'],
plugins: [
[MapPlugin, {
imageUrl: 'https://photo-sphere-viewer-data.netlify.app/maps/floor-plan.png',
center: { x: 500, y: 300 },
rotation: '0deg',
size: '280px',
position: 'bottom left',
shape: 'square',
defaultZoom: 80,
coneColor: 'rgba(30, 120, 230, 0.25)',
hotspots: [
{
id: 'kitchen',
yaw: '45deg',
distance: 145,
tooltip: '厨房',
},
{
id: 'master-bedroom',
yaw: '180deg',
distance: 110,
tooltip: '主卧',
},
{
id: 'guest-bedroom',
x: 620,
y: 180,
tooltip: '次卧',
},
{
id: 'balcony',
yaw: '270deg',
distance: 160,
tooltip: '阳台',
},
],
visibleOnLoad: true,
}],
],
});
// 获取 MapPlugin 实例
const mapPlugin = viewer.getPlugin(MapPlugin);
// 监听热点点击事件
mapPlugin.addEventListener('select-hotspot', ({ hotspotId }) => {
const targets = {
'kitchen': { yaw: '45deg', pitch: '0deg' },
'master-bedroom': { yaw: '180deg', pitch: '0deg' },
'guest-bedroom': { yaw: '190deg', pitch: '0deg' },
'balcony': { yaw: '270deg', pitch: '-10deg' },
};
const target = targets[hotspotId];
if (target) {
viewer.rotate({
yaw: target.yaw,
pitch: target.pitch,
});
console.log(`切换到: ${hotspotId}`);
}
});
// 页面卸载时销毁
window.addEventListener('beforeunload', () => {
viewer.destroy();
});
</script>
</body>
</html>
这个示例演示了 MapPlugin 最核心的能力:一张自定义平面图叠加在 Viewer 上,点击热点瞬间切换全景视角。在真实项目中,你只需要替换 imageUrl 和 panorama 为实际路径,并根据平面图调整 center 和各热点的坐标即可。
9.3 PlanPlugin 详解(Leaflet 真实地图)
9.3.1 功能定位
PlanPlugin 与 MapPlugin 的最大区别在于:它使用真实的 GIS 地图。PlanPlugin 基于 Leaflet 构建,默认使用 OpenStreetMap 瓦片图层,显示全景图在地球上的真实地理位置。适合以下场景:
- 户外全景:旅游景点、街景、自然风光,需要展示真实经纬度
- 多点位导览:在城市地图上标记多个全景拍摄点,支持快速跳转
- 地理信息系统集成:与已有 Leaflet 地图应用融合
9.3.2 安装与依赖
PlanPlugin 依赖 Leaflet,需要同时加载 Leaflet 的 JS 和 CSS。PSV 的 PlanPlugin 包本身不包含 Leaflet。
npm install @photo-sphere-viewer/plan-plugin leaflet
npm install --save-dev @types/leaflet
导入代码:
import 'leaflet/dist/leaflet.css';
import { PlanPlugin } from '@photo-sphere-viewer/plan-plugin';
import '@photo-sphere-viewer/plan-plugin/index.css';
CDN 方式需要额外引入 Leaflet:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/plan-plugin@5/index.css" />
<script type="importmap">
{
"imports": {
"leaflet": "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet-src.esm.js",
"@photo-sphere-viewer/plan-plugin": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/plan-plugin@5/index.module.js"
}
}
</script>
Leaflet 需要单独引入。如果使用 npm 方式,
import 'leaflet/dist/leaflet.css'是必须的;如果使用 CDN,需要在 HTML 中通过<link>标签引入 Leaflet CSS。
9.3.3 核心配置选项
必填配置:
| 选项 | 类型 | 说明 |
|---|---|---|
coordinates |
[number, number] |
全景图的 GPS 坐标,格式为 [longitude, latitude](经度在前,纬度在后) |
外观与行为:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size |
{ width: string, height: string } |
{ width: '300px', height: '200px' } |
地图组件的尺寸 |
position |
string |
'bottom left' |
地图在 Viewer 中的位置 |
bearing |
number \| string |
0 |
方向角偏移,使指针方向与全景朝向匹配 |
defaultZoom |
number |
15 |
默认 Leaflet 缩放级别 |
pinImage |
string |
默认 SVG | 中心位置指针图片 |
pinSize |
number |
35 |
中心指针大小 |
visibleOnLoad |
boolean |
true |
初始是否显示 |
minimizeOnHotspotClick |
boolean |
true |
点击热点后是否自动最小化地图 |
热点配置(hotspots):
PlanPlugin 的热点使用 GPS 坐标定位——这是与 MapPlugin 最本质的区别:
hotspots: [
{
id: 'tiananmen',
coordinates: [116.3975, 39.9087],
tooltip: '天安门',
},
{
id: 'gugong',
coordinates: [116.4039, 39.9158],
tooltip: '故宫博物院',
},
]
每个热点同样支持 style 属性覆盖 spotStyle。
自定义图层(layers):
默认使用 OpenStreetMap 瓦片,你可以指定多个图层供用户切换:
layers: [
{
name: 'OpenStreetMap',
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
attribution: '© OpenStreetMap contributors',
},
{
name: '卫星图',
urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
attribution: '© Esri',
},
]
每个图层对象包含 name、urlTemplate(或直接传 layer——任意 Leaflet 图层实例)和 attribution。如果定义了多个图层,地图上会出现一个图层切换按钮。
高级自定义:
通过 configureLeaflet(map) 回调,你可以在 Leaflet 地图实例创建后对其进行完全自定义——例如添加 GeoJSON 数据、自定义控件、设置地图边界等:
configureLeaflet(map) {
// map 是 Leaflet 的 L.Map 实例
L.control.scale().addTo(map);
map.setMaxBounds([[39.8, 116.2], [40.0, 116.6]]);
}
一旦使用了
configureLeaflet,layers选项将被忽略——你需要自己管理瓦片图层。
9.3.4 方法与事件
| 方法 | 说明 |
|---|---|
setCoordinates(coordinates) |
更新全景图在地图上的 GPS 坐标 |
setHotspots(hotspots) |
设置热点数组 |
clearHotspots() |
移除所有热点 |
setZoom(level) |
设置 Leaflet 缩放级别 |
open() / close() |
展开/折叠地图 |
maximize() / minimize() |
最大化/最小化地图 |
getLeaflet() |
获取底层 Leaflet 地图实例,用于高级自定义 |
事件与 MapPlugin 一致:select-hotspot(hotspotId) 和 view-changed(view)。
9.3.5 完整示例
import 'leaflet/dist/leaflet.css';
import { Viewer } from '@photo-sphere-viewer/core';
import { PlanPlugin } from '@photo-sphere-viewer/plan-plugin';
import '@photo-sphere-viewer/core/index.css';
import '@photo-sphere-viewer/plan-plugin/index.css';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'https://photo-sphere-viewer-data.netlify.app/pano/street.jpg',
caption: '北京 · 长安街',
plugins: [
[PlanPlugin, {
coordinates: [116.3975, 39.9087],
bearing: '15deg',
defaultZoom: 16,
size: { width: '320px', height: '240px' },
position: 'bottom left',
hotspots: [
{
id: 'tiananmen',
coordinates: [116.3975, 39.9087],
tooltip: '天安门广场',
},
{
id: 'gugong',
coordinates: [116.4039, 39.9158],
tooltip: '故宫博物院',
},
{
id: 'jingshan',
coordinates: [116.4023, 39.9226],
tooltip: '景山公园',
},
],
visibleOnLoad: true,
}],
],
});
const planPlugin = viewer.getPlugin(PlanPlugin);
planPlugin.addEventListener('select-hotspot', ({ hotspotId }) => {
// 此处可以配合 VirtualTourPlugin 实现场景切换
console.log(`选中景点: ${hotspotId}`);
});
9.4 CompassPlugin 详解(指南针)
9.4.1 功能定位
CompassPlugin 在 Viewer 角落显示一个指南针组件,实时指示当前视野方向。用户可以直观地感知自己”面朝哪个方向”,也可以点击指南针上的方向快速导航。它特别适合:
- 户外全景:配合真实方向,让用户知道南北朝向
- 室内看房:标注房间方位,辅助理解户型
- 导航类应用:作为方向的视觉参考
Photo-Sphere-Viewer 默认将 yaw=0(正前方)视为”北”。如果需要调整北方的定义,可以使用
sphereCorrection.pan或panoData.poseHeading进行全局校正。
9.4.2 安装与导入
npm install @photo-sphere-viewer/compass-plugin
import { CompassPlugin } from '@photo-sphere-viewer/compass-plugin';
import '@photo-sphere-viewer/compass-plugin/index.css';
9.4.3 配置选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size |
string |
'120px' |
指南针尺寸,支持 px、rem、vh 等 |
position |
string |
'top left' |
指南针在 Viewer 中的位置,支持 top/bottom + left/center/right 组合 |
navigation |
boolean |
true |
是否可点击指南针进行导航 |
resetPitch |
boolean |
true |
点击导航时是否将俯仰角重置为 defaultPitch |
hotspots |
CompassHotspot[] |
null |
指南针上的标记点 |
backgroundSvg |
string |
默认 SVG | 自定义指南针背景 SVG(必须是正方形) |
coneColor |
string |
'rgba(255, 255, 255, 0.2)' |
视野锥形区域颜色 |
navigationColor |
string |
'rgba(255, 0, 0, 0.2)' |
导航点击时的锥形指示颜色 |
hotspotColor |
string |
'rgba(0, 0, 0, 0.5)' |
热点默认颜色 |
className |
string |
— | 添加到指南针元素上的 CSS 类名 |
9.4.4 配置指南针热点
热点配置非常简洁,每个热点只需指定一个 yaw 角度(即指南针上的方位)和可选的 color:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
plugins: [
[CompassPlugin, {
size: '130px',
position: 'top right',
navigation: true,
resetPitch: true,
hotspots: [
{ yaw: '0deg', color: '#e74c3c' }, // 北 — 红色
{ yaw: '90deg', color: '#3498db' }, // 东 — 蓝色
{ yaw: '180deg', color: '#2ecc71' }, // 南 — 绿色
{ yaw: '270deg', color: '#f39c12' }, // 西 — 橙色
],
}],
],
});
指南针热点还可以与 Markers 联动——在 Marker 配置中设置 compass: true 或 compass: '#ff0000',该 Marker 就会自动显示在指南针上。
9.4.5 方法与事件
| 方法 | 说明 |
|---|---|
setHotspots(hotspots) |
更新指南针热点 |
clearHotspots() |
移除所有热点 |
指南针没有自己专属的事件(热点交互通过可视化反馈完成,无需事件监听)。如果需要监听与指南针相关的 Markers 点击,使用 MarkersPlugin 的 select-marker 事件即可。
9.5 AutorotatePlugin 详解(自动旋转)
9.5.1 功能定位
AutorotatePlugin 让全景图自动旋转,常用于以下场景:
- 展厅展示:无人操作时自动旋转展示全景内容,吸引观众注意力
- 产品 360° 展示:用户无需拖拽即可看到产品的各个角度
- 关键路径演示:按预设路径依次浏览多个关键视角,每个视角停留一段时间
9.5.2 安装与导入
npm install @photo-sphere-viewer/autorotate-plugin
import { AutorotatePlugin } from '@photo-sphere-viewer/autorotate-plugin';
import '@photo-sphere-viewer/autorotate-plugin/index.css';
插件会在导航栏自动添加一个”自动旋转”按钮,用户可以手动开关。
9.5.3 配置选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
autostartDelay |
number |
2000 |
用户停止交互后延迟多少毫秒开始自动旋转 |
autostartOnIdle |
boolean |
true |
用户闲置后是否自动重新开始旋转(手动点击按钮关闭后不会自动重启) |
autorotateSpeed |
string |
'2rpm' |
旋转速度,支持 rpm(每分钟转数),设负值可反转方向 |
autorotatePitch |
number \| string |
defaultPitch |
自动旋转时的俯仰角,设为 null 保持当前俯仰角 |
autorotateZoomLvl |
number |
null |
自动旋转时的缩放级别,设为 null 保持当前缩放 |
keypoints |
AutorotateKeypoint[] |
— | 关键路径点数组,定义按序访问的位置 |
startFromClosest |
boolean |
true |
是否从最近的关键点开始(而非数组第一个) |
9.5.4 关键路径点(Keypoints)
当配置了 keypoints,自动旋转不再是匀速绕圈,而是按预设路径在关键点之间平滑移动,并在每个关键点停留一段时间:
keypoints: [
{
position: { yaw: 0, pitch: '5deg' },
pauseTime: 3000, // 在此停留 3 秒
},
{
position: { yaw: '90deg', pitch: '-10deg' },
pauseTime: 2000,
},
{
position: { yaw: '180deg', pitch: '0deg' },
pauseTime: 4000,
},
{
position: { yaw: '270deg', pitch: '10deg' },
pauseTime: 2000,
},
]
如果未配置 keypoints,则使用匀速旋转模式(由 autorotateSpeed 控制速度)。
9.5.5 方法与事件
| 方法 | 说明 |
|---|---|
start() |
开始自动旋转 |
stop() |
停止自动旋转 |
toggle() |
切换开关状态 |
setKeypoints(keypoints) |
设置或更新关键路径点 |
事件:autorotate(enabled) — 自动旋转状态变更时触发,参数为布尔值表示当前状态。
9.5.6 完整示例
import { Viewer } from '@photo-sphere-viewer/core';
import { AutorotatePlugin } from '@photo-sphere-viewer/autorotate-plugin';
import '@photo-sphere-viewer/core/index.css';
import '@photo-sphere-viewer/autorotate-plugin/index.css';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './exhibition-hall.jpg',
caption: '数字展厅 · 自动导览',
navbar: ['autorotate', 'zoom', 'fullscreen', 'caption'],
plugins: [
[AutorotatePlugin, {
autostartDelay: 3000,
autostartOnIdle: true,
autorotateSpeed: '1.5rpm',
autorotatePitch: '-5deg',
autorotateZoomLvl: 60,
keypoints: [
{ position: { yaw: 0, pitch: '0deg' }, pauseTime: 3000 },
{ position: { yaw: '60deg', pitch: '-5deg' }, pauseTime: 2000 },
{ position: { yaw: '120deg', pitch: '0deg' }, pauseTime: 3000 },
{ position: { yaw: '180deg', pitch: '5deg' }, pauseTime: 2000 },
{ position: { yaw: '240deg', pitch: '0deg' }, pauseTime: 3000 },
{ position: { yaw: '300deg', pitch: '-5deg' }, pauseTime: 2000 },
],
startFromClosest: true,
}],
],
});
const autorotatePlugin = viewer.getPlugin(AutorotatePlugin);
// 监听状态变化
autorotatePlugin.addEventListener('autorotate', (enabled) => {
console.log(`自动旋转: ${enabled ? '开启' : '关闭'}`);
});
// 页面卸载时销毁
window.addEventListener('beforeunload', () => {
viewer.destroy();
});
9.6 SettingsPlugin 与 ResolutionPlugin
9.6.1 SettingsPlugin(设置面板)
SettingsPlugin 本身不做任何事情——它是一个基础设施插件,在导航栏添加一个”设置”按钮,点击后展开设置面板。其他插件(如 ResolutionPlugin)通过调用 addSetting() 向面板中注册具体的设置项。
npm install @photo-sphere-viewer/settings-plugin
import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin';
import '@photo-sphere-viewer/settings-plugin/index.css';
SettingsPlugin 支持两种设置类型:
Toggle 开关设置 — 只有 true / false 两种状态:
settingsPlugin.addSetting({
id: 'night-mode',
label: '夜间模式',
type: 'toggle',
active: () => nightMode,
toggle: () => { nightMode = !nightMode; },
});
Options 选项设置 — 多个可选项之间切换:
settingsPlugin.addSetting({
id: 'quality',
label: '画质',
type: 'options',
current: () => quality,
options: () => [
{ id: 'low', label: '流畅' },
{ id: 'mid', label: '标准' },
{ id: 'high', label: '高清' },
],
apply: (option) => { quality = option; },
});
持久化存储: 通过 persist: true 开启设置持久化,默认使用 localStorage 存放在 psvSettings 键下。你也可以通过 storage 选项自定义存储方案(如 LocalForage、后端 API 等):
[SettingsPlugin, {
persist: true,
storage: {
get(settingId) {
return myCustomStore.get(`psv_${settingId}`);
},
set(settingId, value) {
myCustomStore.set(`psv_${settingId}`, value);
},
},
}]
事件:setting-changed(settingId, settingValue) — 任何设置项变更时触发。
9.6.2 ResolutionPlugin(多分辨率切换)
ResolutionPlugin 必须配合 SettingsPlugin 使用,它在设置面板中添加一个”画质”选项,让用户在不同分辨率的全景图之间切换。这对于性能优化非常有用——默认加载低分辨率图片确保快速打开,用户可按需切换到高清版本。
npm install @photo-sphere-viewer/resolution-plugin
import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin';
import { ResolutionPlugin } from '@photo-sphere-viewer/resolution-plugin';
import '@photo-sphere-viewer/settings-plugin/index.css';
import '@photo-sphere-viewer/resolution-plugin/index.css';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
// 注意:若在 Viewer 上直接设置了 panorama,defaultResolution 将被忽略
plugins: [
SettingsPlugin,
[ResolutionPlugin, {
defaultResolution: 'HD',
showBadge: true,
resolutions: [
{
id: 'SD',
label: '标清',
panorama: 'https://example.com/pano-sd.jpg',
},
{
id: 'HD',
label: '高清',
panorama: 'https://example.com/pano-hd.jpg',
},
{
id: '4K',
label: '超清',
panorama: 'https://example.com/pano-4k.jpg',
},
],
}],
],
});
每个 resolution 对象包含 id(唯一标识)、label(显示名称)、panorama(对应的全景图 URL),以及可选的 panoData(为该分辨率的图片提供裁剪信息)。
重要兼容性提示: ResolutionPlugin 与 GalleryPlugin 不兼容——图集模式已自带场景管理,使用 ResolutionPlugin 会导致冲突。
事件:resolution-changed(resolutionId) — 分辨率切换时触发。
9.7 VisibleRangePlugin(可见范围限制)
9.7.1 功能定位
VisibleRangePlugin 锁定全景图的水平和垂直旋转范围,阻止用户旋转到范围之外的区域。它影响手动拖拽和自动旋转,但不限制 API 调用。典型场景:
- 部分全景图:当全景图本身只覆盖了 180° 或更窄的视角时,限制可见范围避免用户看到黑边
- 引导式体验:在虚拟展厅中限制用户只能看前方展品,避免不必要的视口移动
- 安全教育:限制儿童可浏览的范围
9.7.2 安装与导入
npm install @photo-sphere-viewer/visible-range-plugin
import { VisibleRangePlugin } from '@photo-sphere-viewer/visible-range-plugin';
import '@photo-sphere-viewer/visible-range-plugin/index.css';
9.7.3 配置与使用
方式一:手动指定范围
[VisibleRangePlugin, {
horizontalRange: ['-90deg', '90deg'], // 只能看正前方 180° 范围
verticalRange: ['-45deg', '45deg'], // 上下各 45°
}]
方式二:基于裁剪数据自动计算
如果全景图使用了 panoData 配置了裁剪信息(croppedX、croppedY、croppedWidth、croppedHeight),可以启用 usePanoData: true 自动将可见范围限制为裁剪后的有效区域:
const viewer = new Viewer({
panorama: './partial-pano.jpg',
panoData: {
fullWidth: 8192,
fullHeight: 4096,
croppedX: 1024,
croppedY: 512,
croppedWidth: 6144,
croppedHeight: 3072,
},
plugins: [
[VisibleRangePlugin, {
usePanoData: true,
}],
],
});
9.7.4 方法
| 方法 | 说明 |
|---|---|
setHorizontalRange(range) |
设置水平可见范围,传 null 取消限制 |
setVerticalRange(range) |
设置垂直可见范围,传 null 取消限制 |
setRangesFromPanoData() |
从当前全景的裁剪数据重新计算可见范围 |
9.8 OverlaysPlugin(叠加层)
9.8.1 功能定位
OverlaysPlugin 在全景球面上叠加额外的图像——与 MarkersPlugin 不同,叠加层是嵌入 3D 场景中的,而非浮在画面上层的 DOM 元素。这意味着叠加层会随全景旋转而自然变换视角,具有真实的立体感。典型用途:
- 虚拟装修:在空房间全景上叠加家具图片,展示装修效果
- 特效叠加:叠加光照效果、文字标注、水印等
- AR 预览:在现实场景全景上叠加虚拟物体
9.8.2 安装与导入
npm install @photo-sphere-viewer/overlays-plugin
import { OverlaysPlugin } from '@photo-sphere-viewer/overlays-plugin';
import '@photo-sphere-viewer/overlays-plugin/index.css';
9.8.3 叠加层类型
OverlaysPlugin 支持两种几何体类型:
球形叠加层(Spherical) — 使用等距柱状投影图片:
overlays: [
{
id: 'furniture',
path: 'https://example.com/furniture-overlay.png',
opacity: 0.9,
zIndex: 1,
},
]
球形叠加层也支持 panoData 裁剪,用于只覆盖球面的一部分(例如只在一面墙上叠加):
{
id: 'wall-art',
path: 'painting.png',
panoData: {
fullWidth: 4096,
fullHeight: 2048,
croppedX: 0,
croppedY: 500,
croppedWidth: 4096,
croppedHeight: 1048,
},
}
立方体叠加层(Cubemap) — 使用 6 张独立的面图片:
{
id: 'cube-overlay',
path: {
left: 'cubemap/overlay-left.jpg',
front: 'cubemap/overlay-front.jpg',
right: 'cubemap/overlay-right.jpg',
back: 'cubemap/overlay-back.jpg',
top: 'cubemap/overlay-top.jpg',
bottom: 'cubemap/overlay-bottom.jpg',
},
opacity: 0.7,
zIndex: 2,
}
9.8.4 配置选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
overlays |
OverlayConfig[] |
— | 初始叠加层配置数组 |
autoclear |
boolean |
true |
切换全景图时是否自动清除所有叠加层 |
inheritSphereCorrection |
boolean |
true |
是否继承全局 sphereCorrection 设置 |
每个叠加层在配置中还可以设置 sphereCorrection,用于覆盖全局的球体校正。叠加层支持 opacity(透明度)和 zIndex(层级顺序),zIndex 越大的叠加层越靠上。
9.8.5 方法与事件
| 方法 | 说明 |
|---|---|
addOverlay(config) |
动态添加一个叠加层 |
removeOverlay(id) |
通过 id 移除指定叠加层 |
clearOverlays() |
移除所有叠加层 |
事件:overlay-click(overlayId) — 用户点击叠加层时触发。
9.9 多插件组合最佳实践
随着学习的深入,你已经接触了 PSV 生态中的大量插件。在实际项目中,通常需要组合多个插件来实现完整功能。以下是经过验证的插件兼容性说明和推荐组合方案。
9.9.1 插件兼容性矩阵
大多数插件可以自由组合,但以下组合需要特别注意:
| 插件 A | 插件 B | 兼容性 | 说明 |
|---|---|---|---|
| GalleryPlugin | ResolutionPlugin | 不兼容 | 图集模式自带切换逻辑,与分辨率切换冲突 |
| MapPlugin | PlanPlugin | 可共存 | 两者定位不同,但通常不同时使用 |
| ResolutionPlugin | SettingsPlugin | 强依赖 | ResolutionPlugin 必须在 SettingsPlugin 之后加载 |
| VideoPlugin | ResolutionPlugin | 兼容 | 支持切换不同分辨率的视频源 |
| MarkersPlugin | MapPlugin | 兼容 | Markers 可通过 map 属性直接显示在地图上 |
| MarkersPlugin | CompassPlugin | 兼容 | Markers 可通过 compass 属性直接显示在指南针上 |
| MarkersPlugin | PlanPlugin | 兼容 | Markers 可通过 plan 属性直接显示在 Leaflet 地图上 |
9.9.2 按场景推荐的插件组合
场景一:房地产 VR 看房
这是最经典的 PSV 应用场景,核心需求是房间之间的快速切换、平面图导航和空间定位。
import { Viewer } from '@photo-sphere-viewer/core';
import { VirtualTourPlugin } from '@photo-sphere-viewer/virtual-tour-plugin';
import { MarkersPlugin } from '@photo-sphere-viewer/markers-plugin';
import { MapPlugin } from '@photo-sphere-viewer/map-plugin';
import { CompassPlugin } from '@photo-sphere-viewer/compass-plugin';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'living-room.jpg',
plugins: [
[VirtualTourPlugin, { /* 节点配置 */ }],
[MarkersPlugin, { /* 标记配置 */ }],
[MapPlugin, {
imageUrl: 'floor-plan.png',
center: { x: 500, y: 300 },
hotspots: [ /* 平面图热点 */ ],
}],
[CompassPlugin, {
size: '100px',
position: 'top right',
}],
],
});
组合要点:
- VirtualTourPlugin 管理多个房间节点的切换
- MapPlugin 提供直观的平面图导航
- CompassPlugin 辅助方向感知
- MarkersPlugin 在各个房间中标记细节(如橱柜材质、地板品牌等)
场景二:360° 视频展示
import { VideoPlugin } from '@photo-sphere-viewer/video-plugin';
import { ResolutionPlugin } from '@photo-sphere-viewer/resolution-plugin';
import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin';
import { MarkersPlugin } from '@photo-sphere-viewer/markers-plugin';
import { AutorotatePlugin } from '@photo-sphere-viewer/autorotate-plugin';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: { source: 'tour-360.mp4' },
plugins: [
SettingsPlugin,
[VideoPlugin, { /* 视频配置 */ }],
[ResolutionPlugin, {
resolutions: [
{ id: '720p', label: '720p', panorama: { source: 'tour-720p.mp4' } },
{ id: '1080p', label: '1080p', panorama: { source: 'tour-1080p.mp4' } },
],
}],
[AutorotatePlugin, { autorotateSpeed: '0.5rpm' }],
[MarkersPlugin, { /* 在视频中标记关键时间点 */ }],
],
});
场景三:移动端 VR 体验
import { GyroscopePlugin } from '@photo-sphere-viewer/gyroscope-plugin';
import { StereoPlugin } from '@photo-sphere-viewer/stereo-plugin';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'vr-scene.jpg',
mousewheel: false,
navbar: ['zoom', 'fullscreen', 'gyroscope', 'stereo'],
plugins: [
[GyroscopePlugin, {
touchMode: true,
absolutePosition: true,
}],
[StereoPlugin, {
stereoOverlay: 0.5,
}],
],
});
场景四:展厅自动展示
import { AutorotatePlugin } from '@photo-sphere-viewer/autorotate-plugin';
import { MarkersPlugin } from '@photo-sphere-viewer/markers-plugin';
import { OverlaysPlugin } from '@photo-sphere-viewer/overlays-plugin';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'exhibition.jpg',
navbar: ['autorotate', 'zoom', 'fullscreen'],
plugins: [
[AutorotatePlugin, {
autostartDelay: 5000,
autorotateSpeed: '1rpm',
autorotatePitch: '-3deg',
keypoints: [ /* 按展品顺序排列的关键路径点 */ ],
}],
[MarkersPlugin, {
markers: [ /* 展品信息标记 */ ],
}],
[OverlaysPlugin, {
overlays: [ /* 特效叠加层 */ ],
}],
],
});
9.9.3 插件加载顺序与最佳实践
- SettingsPlugin 必须最先加载——ResolutionPlugin 等依赖它注册设置面板
- VideoPlugin 必须在分辨率切换插件之前加载——视频源需要先初始化
- MapPlugin / PlanPlugin 的 CSS 导入不可遗漏——否则地图按钮和样式丢失
- 合理控制插件数量——每个插件都会增加初始加载体积。PSV 的 tree-shaking 支持良好,如果你使用 npm + 打包工具(如 Vite / Webpack),未引用的插件不会被打包进去
- 性能敏感场景——如果全景图体积大且网络慢,避免同时初始化过多插件,可以在
ready事件后按需动态添加
viewer.addEventListener('ready', () => {
// 全景图加载完成后,再创建地图和标记插件(非官方 API,仅示意思路)
// 实际中应使用插件系统的动态注册方式
}, { once: true });
- 测试兼容性——在开发阶段,每添加一个新插件后立即在全景图上测试一轮基本交互(拖拽、缩放、热点点击),确保没有插件冲突
9.10 本章小结
本章全面介绍了 Photo-Sphere-Viewer 的地图集成方案与辅助功能插件。这些插件虽不直接控制全景图的渲染,却大幅提升了应用的用户体验和实用价值。
知识要点回顾:
| 插件 | 一句话总结 | 核心依赖 |
|---|---|---|
| MapPlugin | 自定义静态地图,适合平面图/示意图 | 无 |
| PlanPlugin | 基于 Leaflet 的真实 GIS 地图,适合地理全景 | Leaflet |
| CompassPlugin | 角落指南针,指示当前视野方向 | 无 |
| AutorotatePlugin | 自动旋转或按关键路径顺序导览 | 无 |
| SettingsPlugin | 统一设置面板基础设施,其他插件注册设置项 | 无 |
| ResolutionPlugin | 多分辨率切换,必须配合 SettingsPlugin | SettingsPlugin |
| VisibleRangePlugin | 锁定水平和垂直旋转范围 | 无 |
| OverlaysPlugin | 在 3D 球面上叠加额外图像 | 无 |
关键区分:
- MapPlugin vs PlanPlugin:前者是自定义静态图片(室内平面图),后者是真实地理地图(经纬度 + OSM)。选哪个取决于你的全景图是否需要关联真实地理位置。
- MapPlugin 热点使用像素坐标(
x/y)或角度+距离(yaw+distance);PlanPlugin 热点使用 GPS 坐标(coordinates)。 - SettingsPlugin 是基础设施,本身不做任何事;ResolutionPlugin 依赖它来展示画质切换按钮。
- ResolutionPlugin 不能与 GalleryPlugin 同时使用。
下一章将进入实战整合——我们将把这些插件组合到真实项目框架(React / Vue / Angular)中,并讨论生产环境的打包优化与部署策略。