第02章:环境搭建与第一个全景应用
工欲善其事,必先利其器。本章将从零开始,手把手带你搭建 Photo-Sphere-Viewer 的开发环境,并运行第一个全景查看器应用。读完本章,你将拥有一份可直接使用的 HTML 文件,双击打开就能在浏览器中漫游 360° 全景图。
2.1 环境要求
Node.js
Photo-Sphere-Viewer v5.x 要求 Node.js 22 或更高版本。如果你计划使用 npm 构建项目,请先确认 Node.js 版本:
node -v
# 应输出 v22.x.x 或更高
npm -v
# 应输出 10.x.x 或更高
如果版本过低,建议使用 nvm-windows 或直接从 Node.js 官网 下载安装最新 LTS 版本。
如果你只使用 CDN 方式(纯 HTML 文件),则不需要 Node.js,只需一个现代浏览器即可。
浏览器
Photo-Sphere-Viewer 基于 Three.js 在 WebGL 上渲染全景图,因此必须使用支持 WebGL 的现代浏览器:
| 浏览器 | 最低版本 | 推荐版本 |
|---|---|---|
| Chrome | 56+ | 最新版 |
| Firefox | 52+ | 最新版 |
| Edge | 79+ | 最新版 |
| Safari | 11+ | 最新版 |
你可以在浏览器地址栏输入 chrome://gpu(Chrome)或 about:support(Firefox)查看 WebGL 支持状态。也可以访问 get.webgl.org 快速检测。
全景图素材准备
在开始之前,你需要准备一张等距柱状投影(Equirectangular Projection)的全景图作为素材。这种投影方式将 360° 球面信息映射到一个 2:1 宽高比的矩形平面中——宽度恰好是高度的两倍。
| 分辨率 | 宽高比 | 常见用途 |
|---|---|---|
| 4096 × 2048 | 2:1 | 标准全景展示 |
| 8192 × 4096 | 2:1 | 高清晰度展示 |
| 16384 × 8192 | 2:1 | VR 头显级别 |
如果你手头暂时没有全景图,以下资源可以免费获取测试素材:
- polyhaven.com — 高质量 HDR 全景图,下载时可选择 JPG 格式
- unsplash.com — 搜索 “360 panorama” 获取摄影作品
- 使用手机拍摄全景照片后,用工具拼接成 2:1 等距柱状投影
本教程示例使用一张示例图,你替换成自己的图片路径即可。
2.2 安装方式详解
Photo-Sphere-Viewer 提供三种安装方式,适应不同的开发场景。
2.2.1 npm / yarn 安装(推荐用于工程化项目)
这是标准的前端工程化安装方式,适合使用 Vite、Webpack 等打包工具的项目。
# 使用 npm
npm install @photo-sphere-viewer/core
# 或使用 yarn
yarn add @photo-sphere-viewer/core
安装完成后,node_modules/@photo-sphere-viewer/core 目录下包含完整的源码、类型定义和样式文件。后续章节安装插件时,也使用相同的 npm scope 前缀,例如:
npm install @photo-sphere-viewer/markers-plugin
npm install @photo-sphere-viewer/gallery-plugin
2.2.2 CDN 方式(推荐用于快速原型)
如果你不想搭建构建工具链,或者只是快速验证想法,可以使用 jsDelivr CDN 配合浏览器原生 Import Map 直接在 HTML 中加载。Import Map 是现代浏览器的一项特性,它允许你用简洁的模块名替代冗长的 CDN 路径。
完整的 Import Map 配置如下:
<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"
}
}
</script>
几点说明:
type="importmap"告诉浏览器这是一张模块映射表,必须在<script type="module">之前声明。three是 Photo-Sphere-Viewer 的底层依赖,必须显式指定。版本号应与@photo-sphere-viewer/core要求的 Three.js 版本匹配。v5.x 通常依赖 Three.js r163 左右,建议查阅官方文档确认精确版本。@photo-sphere-viewer/core指向 jsDelivr 上的 ES Module 版本(index.module.js)。@5表示 v5 的最新版;生产环境建议锁定精确版本号,例如@5.11.0/index.module.js。- Import Map 在 Chrome 89+、Edge 89+、Firefox 108+、Safari 16.4+ 中得到支持。如需兼容更老的浏览器,可使用 es-module-shims polyfill。
2.2.3 手动下载
你还可以从 GitHub Releases 页面 下载预构建的打包文件。下载后的目录结构如下:
photo-sphere-viewer-5.x.x/
├── index.module.js # ES Module 版本
├── index.js # UMD 版本
├── index.css # 样式文件
└── plugins/ # 插件文件
下载后将文件放到项目目录中,通过相对路径引用即可。这种方式适用于无网络环境或需要内网部署的场景。
2.3 项目初始化
下面分别演示两种最常见的项目初始化方式。
2.3.1 方式一:纯 HTML/JS 项目(零构建)
新建一个 index.html 文件,写入以下内容。这是你能写出的最简全景查看器,不到 30 行代码,双击即可运行:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>我的第一个全景应用</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; }
#viewer {
width: 100%;
height: 100%;
}
</style>
</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"
}
}
</script>
<script type="module">
import { Viewer } from '@photo-sphere-viewer/core';
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: 'https://photo-sphere-viewer-data.netlify.app/pano/example.jpg',
});
</script>
</body>
</html>
将上述代码保存为 .html 文件,用浏览器打开,你应该能看到一张可以拖拽旋转的全景图。
重要提醒:由于使用了 ES Module 和 Import Map,不能直接双击本地 HTML 文件打开——浏览器会因
file://协议限制而拒绝加载远程模块。你需要使用本地静态服务器:# 如果安装了 Node.js npx serve . # 或使用 Python python -m http.server 8080 # 或使用 VS Code 的 Live Server 插件然后通过
http://localhost:3000(或对应端口)访问页面。
2.3.2 方式二:Vite + TypeScript 项目
对于正式项目,推荐使用 Vite + TypeScript 的组合。以下是完整的初始化步骤:
步骤 1:创建 Vite 项目
npm create vite@latest my-panorama -- --template vanilla-ts
cd my-panorama
步骤 2:安装依赖
npm install @photo-sphere-viewer/core
步骤 3:配置 TypeScript
编辑 tsconfig.json,确保 moduleResolution 设置为 bundler(Vite 推荐配置):
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
步骤 4:编写代码
编辑 src/main.ts:
import '@photo-sphere-viewer/core/index.css';
import { Viewer } from '@photo-sphere-viewer/core';
const viewer = new Viewer({
container: document.querySelector<HTMLDivElement>('#viewer')!,
panorama: 'https://photo-sphere-viewer-data.netlify.app/pano/example.jpg',
});
编辑 index.html,确保有一个 #viewer 容器,并且 body 和 html 铺满全屏:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>全景应用</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; }
#viewer { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="viewer"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
步骤 5:启动开发服务器
npm run dev
打开浏览器访问 Vite 提示的本地地址(通常是 http://localhost:5173),即可看到全景图。
关键点:CSS 导入
@photo-sphere-viewer/core/index.css是必须的。如果没有这行,Viewer 的核心 UI 组件(导航栏、加载提示等)将没有任何样式,页面看起来会像是坏掉了一样。
2.4 创建第一个 Viewer
上一节的示例能跑起来,但我们对每一行代码背后的含义还需要深入理解。这一节逐行拆解 Viewer 的创建过程。
2.4.1 container — 挂载容器
container: document.querySelector('#viewer')
container 接受一个 DOM 元素或 CSS 选择器字符串(如 '#viewer')。Photo-Sphere-Viewer 会将 Three.js 的 <canvas> 以及导航栏等 UI 组件渲染到这个容器内部。
容器在创建 Viewer 时必须已经存在于 DOM 中,并且具有明确的尺寸。 如果容器的 width 或 height 为 0,canvas 将无法渲染,页面显示为一片空白或黑屏。
常见的容器样式设置:
/* 方式一:铺满整个视口 */
#viewer {
width: 100vw;
height: 100vh;
}
/* 方式二:固定尺寸 */
#viewer {
width: 800px;
height: 600px;
}
/* 方式三:通过父元素撑开 */
.parent {
display: flex;
height: 100vh;
}
#viewer {
flex: 1;
}
2.4.2 panorama — 全景图源
panorama: 'https://example.com/pano.jpg'
panorama 参数指定全景图的来源,支持多种格式:
| 类型 | 示例 | 说明 |
|---|---|---|
| URL 字符串 | './pano.jpg' |
最常用,直接给出图片地址 |
| 函数 | () => '/dynamic/path.jpg' |
动态决定图片路径,每次创建时调用 |
| Blob / File | new Blob(...) 或 fileInput.files[0] |
从用户上传的文件中读取 |
| 相对路径 | '../assets/pano.jpg' |
相对于 HTML 页面的路径 |
对于等距柱状投影(Equirectangular)全景图,Photo-Sphere-Viewer 使用默认的适配器即可。更多全景图类型(立方体贴图、鱼眼等)将在第 04 章详细介绍。
2.4.3 CSS 导入
无论使用 npm 还是 CDN,都必须在代码中导入 CSS:
// npm 方式
import '@photo-sphere-viewer/core/index.css';
// CDN 方式(在 HTML 中)
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5/index.css" />
如果你看到导航栏文字挤在一起、按钮没有图标、加载动画不显示,十有八九是忘记了导入 CSS。
2.5 Viewer 生命周期
一个 Viewer 实例从创建到销毁,经历以下生命周期阶段:
new Viewer(config)
│
▼
启动 Three.js 渲染器,创建场景与相机
│
▼
开始加载全景纹理(TextureLoader)
│
▼
纹理加载完成 → 渲染第一帧 → 触发 'ready' 事件
│
▼
用户交互(拖拽、缩放、调用 API)
│
▼
viewer.destroy() → 释放 WebGL 资源、移除事件监听、DOM 清理
2.5.1 监听 ready 事件
ready 事件在纹理加载完成、首帧渲染之后触发。此时 Viewer 的所有功能已就绪,可以安全地调用 API 或添加插件:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
});
viewer.addEventListener('ready', () => {
console.log('全景图加载完成,可以开始交互');
// 此时可以安全地调用 API
viewer.rotate({
yaw: Math.PI / 2,
pitch: 0,
});
// 或动态添加插件
// viewer.getPlugin(...)
}, { once: true });
{ once: true } 表示该监听器只执行一次,执行后自动移除。对于 ready 事件来说这是好习惯,避免重复触发时执行多余逻辑。
2.5.2 错误处理
全景图加载可能因为多种原因失败——网络断开、图片不存在、跨域限制等。Photo-Sphere-Viewer 会在控制台输出错误信息,但你也可以通过监听事件来做更精细的错误处理:
viewer.addEventListener('load-error', (e) => {
console.error('全景图加载失败:', e.error);
// 在页面上显示错误提示
const msg = document.createElement('div');
msg.textContent = '全景图加载失败,请检查网络或图片地址。';
msg.style.cssText = 'color: red; padding: 20px; text-align: center;';
e.target.container.appendChild(msg);
});
在 Vite 或 TypeScript 环境中,也可以使用 try-catch 包裹构造函数调用(尽管大部分错误是异步的,需要在事件中捕获):
try {
const viewer = new Viewer({ ... });
// 异步错误由事件系统处理
} catch (err) {
// 构造阶段同步错误
console.error('Viewer 创建失败:', err);
}
2.5.3 destroy() — 销毁实例
当 Viewer 不再需要时(例如 SPA 页面切换、组件卸载),必须调用 destroy() 来释放资源。
为什么必须调用 destroy()?
- Three.js 的 WebGL 上下文不会自动释放,不调用
destroy()会导致 GPU 内存泄漏。 - 事件监听器不会自动移除,不销毁会导致”幽灵监听器”累积。
- 在单页应用中,反复创建和忘记销毁 Viewer 会让浏览器标签页越来越卡,最终崩溃。
正确做法:
// 页面卸载时销毁
window.addEventListener('beforeunload', () => {
viewer.destroy();
});
// 或 SPA 路由切换时销毁
function onRouteLeave() {
viewer.destroy();
}
destroy() 调用后,Viewer 实例不应再被使用。如果之后需要再次显示全景图,应当重新 new Viewer(...)。
2.6 基础交互操作
Viewer 默认开启了一套完整的交互系统。以下是各交互方式及其对应的配置选项。
2.6.1 鼠标拖拽旋转
这是最核心的交互方式:按住鼠标左键拖拽,全景图跟随旋转。
- 水平拖拽 → 调整 yaw(偏航角),即左右旋转
- 垂直拖拽 → 调整 pitch(俯仰角),即上下旋转
如果需要捕获鼠标交互事件(例如实现自定义光标、记录用户行为),可以监听 click、position-updated 等事件。
2.6.2 滚轮缩放
滚动鼠标滚轮控制缩放,实际改变的是 FOV(视场角,Field of View)——FOV 越小,画面越”拉近”;FOV 越大,画面越”推远”。
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
mousewheel: true, // 默认开启滚轮缩放
mousewheelCtrlKey: false, // 设为 true 则需按住 Ctrl 才能缩放
});
mousewheelCtrlKey 的意义:在全景图占据整个页面的场景中,用户本能地会用滚轮滚动页面。如果你的页面还有可滚动内容,将该选项设为 true 可以避免误触缩放,只有按住 Ctrl + 滚轮时才触发全景缩放。
2.6.3 键盘控制
方向键和 WASD 提供了另一种导航方式:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
keyboard: true, // 默认开启(也可以通过 'always' 强制)
});
键盘映射:
| 按键 | 动作 |
|---|---|
| ← / A | 向左旋转 |
| → / D | 向右旋转 |
| ↑ / W | 向上旋转 |
| ↓ / S | 向下旋转 |
| + / = | 放大(减小 FOV) |
| - | 缩小(增大 FOV) |
注意:如果页面中存在 <input> 或 <textarea> 等可编辑元素,Photo-Sphere-Viewer 会自动在这些元素获得焦点时禁用键盘导航,避免干扰文字输入。如果你希望始终响应键盘(即使焦点在输入框上),可以将 keyboard 设置为 'always'。
2.6.4 移动端触摸手势
在手机上,Photo-Sphere-Viewer 自动适配触摸交互:
| 手势 | 动作 |
|---|---|
| 单指滑动 | 旋转全景图 |
| 双指捏合/张开(pinch) | 缩放 |
| 双指旋转 | 调整视角(部分浏览器支持) |
移动端有两个特殊选项需要关注:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
touchmoveTwoFingers: false, // 设为 true 则单指滑动不旋转,必须双指
});
touchmoveTwoFingers:如果你的全景图嵌入在一个可滚动的页面中,设为 true 可以避免用户单指滑动页面时误触发全景旋转——只有双指滑动才会旋转全景,单指滑动正常滚动页面。对于独立的全景页面(body 无滚动),保持 false 即可。
2.7 简单自定义
Viewer 提供了一系列配置选项,让你在零代码的情况下实现常见的定制需求。
2.7.1 初始视角
默认情况下,全景图打开时视角对准正前方(yaw = 0, pitch = 0)。你可以指定初始视角,让用户打开页面时直接看到你最想展示的方向:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
defaultYaw: '90deg', // 初始水平旋转 90 度(朝右看)
defaultPitch: '-20deg', // 初始俯仰 -20 度(稍向下看)
});
defaultYaw 和 defaultPitch 接受数字(弧度)或带单位的字符串(deg、rad、grad)。建议使用 deg 字符串,直观易读。
2.7.2 缩放范围
限制用户可以缩放到的最近和最远距离:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
minFov: 30, // 最小视场角(最大放大倍数),默认约 30 度
maxFov: 90, // 最大视场角(最小放大倍数),默认约 90 度
defaultZoomLvl: 50, // 初始缩放级别对应的 FOV,默认约 75 度
});
- FOV 越小,画面越大(更”近”)——防止用户放大到看到像素格子
- FOV 越大,画面越小(更”远”)——防止用户缩到全景图只占屏幕一小块
defaultZoomLvl 的值应在 minFov 和 maxFov 之间。如果不确定设多少,保持默认即可。
2.7.3 移动和缩放速度
调整旋转和缩放的灵敏度:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
moveSpeed: 0.5, // 旋转速度倍率,默认 1。值越大旋转越快
zoomSpeed: 0.8, // 缩放速度倍率,默认 1。值越大缩放越灵敏
});
moveSpeed: 0.3— 适合需要精细调整的场景(如虚拟展厅,用户需要仔细看展品)moveSpeed: 2.0— 适合快速浏览的场合
2.7.4 添加标题(caption)
导航栏默认显示在顶部,caption 选项可以在导航栏中显示标题文字:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
caption: '黄石国家公园 · 老忠实泉',
});
标题文字显示在导航栏的中间位置,字体大小和颜色由主题 CSS 控制。
2.7.5 添加描述(description)
description 将文本显示在侧边信息面板中(需要用户点击导航栏的信息按钮):
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
caption: '黄石国家公园',
description: '老忠实泉(Old Faithful)是黄石国家公园最著名的间歇泉,每隔约 90 分钟喷发一次,水柱高达 30-55 米。',
});
描述支持纯文本。如果需要更丰富的内容(图片、链接),可以结合 MarkersPlugin 来实现,这部分将在第 05 章介绍。
2.7.6 加载中提示
在全景图加载期间,Photo-Sphere-Viewer 会显示一个加载动画。你可以自定义这个阶段的提示:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
loadingImg: './assets/loading.gif', // 自定义加载动画图片
loadingTxt: '正在加载全景图,请稍候...', // 自定义加载文字
});
loadingImg为null时,使用默认的 CSS 旋转圆圈动画loadingTxt的默认值为'Loading...',你可以改为中文提示
2.7.7 导航栏按钮
默认导航栏包含缩放按钮和全屏按钮。你可以在初始化时调整导航栏的按钮布局:
const viewer = new Viewer({
container: document.querySelector('#viewer'),
panorama: './pano.jpg',
navbar: [
'zoom', // 缩放按钮组(放大/缩小/重置)
'fullscreen', // 全屏按钮
'download', // 下载按钮
'caption', // 标题文字
'description', // 描述信息按钮
],
});
navbar 数组中的字符串按顺序从左到右排列。如果你不需要某个按钮,只需在数组中移除对应的字符串即可。
2.8 完整示例代码
下面给出一个功能完整的 HTML 文件,整合了本章介绍的各项功能。你可以直接保存运行:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Photo-Sphere-Viewer 完整示例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
#viewer {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="viewer"></div>
<!-- Import Map:映射模块名到 CDN 地址 -->
<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"
}
}
</script>
<script type="module">
import { Viewer } from '@photo-sphere-viewer/core';
const viewer = new Viewer({
// ========== 基础配置 ==========
container: document.querySelector('#viewer'),
panorama: 'https://photo-sphere-viewer-data.netlify.app/pano/example.jpg',
// ========== 初始视角 ==========
defaultYaw: '0deg',
defaultPitch: '-15deg',
// ========== 缩放 ==========
minFov: 30,
maxFov: 90,
defaultZoomLvl: 60,
mousewheel: true,
mousewheelCtrlKey: false,
// ========== 速度 ==========
moveSpeed: 1.0,
zoomSpeed: 1.0,
// ========== 键盘 ==========
keyboard: true,
// ========== 触摸 ==========
touchmoveTwoFingers: false,
// ========== 导航栏 ==========
navbar: [
'zoom',
'fullscreen',
'download',
'caption',
'description',
],
// ========== 标题与描述 ==========
caption: '全景图示例',
description: `这是一个完整的 Photo-Sphere-Viewer 示例。
操作提示:
· 鼠标拖拽旋转视角
· 滚轮缩放画面
· 方向键/WASD 移动视角
· 移动端单指滑动旋转,双指缩放`,
// ========== 加载提示 ==========
loadingTxt: '正在加载全景图...',
});
// ========== 生命周期事件 ==========
viewer.addEventListener('ready', () => {
console.log('[PSV] 全景图已就绪,可以开始交互');
}, { once: true });
viewer.addEventListener('load-error', (e) => {
console.error('[PSV] 加载失败:', e.error);
});
// ========== 页面卸载时销毁 ==========
window.addEventListener('beforeunload', () => {
viewer.destroy();
});
</script>
</body>
</html>
运行效果:打开页面后,你会看到一个全屏的全景图查看器,顶部导航栏显示标题和操作按钮,左下角有描述信息入口。拖拽鼠标即可自由探索全景场景。
2.9 常见问题排查
全景图不显示 / 黑屏
这是最常被问到的问题。按以下顺序逐一排查:
1. 容器尺寸为零
打开浏览器开发者工具(F12),选中 #viewer 元素,查看其 width 和 height 是否为 0。如果是,检查 CSS 中是否设置了尺寸:
html, body { width: 100%; height: 100%; }
#viewer { width: 100%; height: 100%; }
2. 图片路径错误
检查浏览器 Network 面板,看全景图片的 HTTP 请求是否返回 404 或 CORS 错误。如果是相对路径,确认路径相对于 HTML 文件的位置是否正确。
3. 跨域(CORS)问题
当全景图片托管在不同域名下,且服务器未设置 Access-Control-Allow-Origin 响应头时,浏览器会阻止跨域加载。表现为控制台报错:
Access to image at 'https://example.com/pano.jpg' from origin 'http://localhost:3000'
has been blocked by CORS policy
解决方法:
- 确保图片服务器设置了正确的 CORS 头
- 将图片放在与 HTML 同域下
- 在开发阶段,可使用支持 CORS 的测试图源或代理服务器
4. WebGL 不支持
在控制台输入以下代码检测:
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
console.log(gl ? 'WebGL 已支持' : 'WebGL 不可用');
如果返回 null,检查浏览器是否启用了硬件加速,或者尝试更新显卡驱动。
图片加载失败
除了 CORS,以下情况也会导致加载失败:
- 文件名包含特殊字符或中文:建议文件名仅使用英文字母、数字和连字符
- 文件格式不兼容:确保使用 JPEG、PNG 或 WebP 格式(推荐 JPEG 以获得较好兼容性和压缩比)
- 图片体积过大:超高清全景图可能因加载超时而失败,建议控制在 5MB 以内用于 Web 展示
交互不响应
如果全景图显示正常,但鼠标拖拽、滚轮缩放无效:
- 检查是否有其他元素覆盖在
#viewer之上(例如一个透明的<div>遮罩层) - 检查
mousewheel是否被显式设为false - 检查
navbar数组中是否错误地移除了某些按钮(这不影响底层交互,但可能让你觉得”按钮消失了”) - 在开发者工具中检查是否有 JavaScript 错误导致 Viewer 初始化中断
内存泄漏 / 页面越来越卡
在 SPA(单页应用)中,如果切换路由时没有销毁 Viewer,每次切换都会在内存中残留一个完整的 WebGL 上下文:
// 错误做法:路由切换时不销毁
router.beforeEach(() => {
new Viewer({ ... }); // 每次创建新实例,旧的未销毁 → 内存泄漏
});
// 正确做法:先销毁旧实例
let viewer = null;
router.beforeEach(() => {
if (viewer) {
viewer.destroy();
viewer = null;
}
viewer = new Viewer({ ... });
});
你可以在 Chrome 开发者工具的 Performance Monitor 中观察 JS heap size 和 DOM Nodes 数量——如果这些数字只增不减,说明存在内存泄漏。
CSS 未导入
如果导航栏裸奔(无样式)、按钮没有图标、加载动画不显示,十有八九是没导入 CSS:
// 务必在入口文件顶部加入这行
import '@photo-sphere-viewer/core/index.css';
2.10 本章小结
本章从环境准备开始,详细介绍了 Photo-Sphere-Viewer 的三种安装方式,演示了纯 HTML 和 Vite + TypeScript 两种初始化路径,深入讲解了 Viewer 的生命周期、交互系统和常见配置项。最后给出了一个开箱即用的完整示例。
关键要点回顾:
| 要点 | 一句话总结 |
|---|---|
| 容器尺寸 | 必须明确设置,否则黑屏 |
| CSS 导入 | 不导入则 UI 裸奔 |
| Import Map | CDN 用户的必经之路,注意加载顺序 |
| lifecycle | 在 ready 事件之后才能安全操作 Viewer |
| destroy() | 不用时一定要销毁,否则内存泄漏 |
| CORS | 跨域图片需要服务端配合或同域部署 |
从下一章开始,我们将深入 Viewer 的核心配置与 API,学习如何通过代码精确控制全景图的每一个行为。