实现全景图的方式很多,但是目前国内最流行的成熟方案要数 Krpano。Krpano 提供了一系列工具来用于全景图的制作和播放,更重要的是 Krpano 的播放器有着灵活的编程接口,使得我们能够对全景图进行扩展和定制。
本文介绍如何基于 Krpano 进行全景图开发。
一、使用 Krpano 播放器展示一个全景图
krpano 提供的播放器有 Flash 和 HTML5 两种版本,我们主要使用 HTML5 版本。
启动播放器
使用 Krpano 展示一个全景图只需要以下材料:
- 播放器代码 krpano.js
- 全景图片
- 描述全景图的 Krpano XML
播放器需要挂载到页面上的一个元素上,页面在引入 krpano.js
之后需要通过 embedpano()
方法配置播放器的挂载点和一系列参数,embedpano()
支持的所有参数可以在官方的 Embedding Parameters 部分查看。
虽然配置中唯一必须的是用于指定挂载点 id 的 target
属性,但是想要播放全景图还需要在 xml
属性中指定 Krpano XML 的路径。
<body>
<div id="mount" style="position: absolute; width: 100%; height: 100%;"></div>
<script src="../krpano-lib/krpano.js"></script>
<script>
var swfPath = '../krpano-lib/krpano.swf';
var xmlPath = './krpano.xml';
embedpano({
// 引入krpano.js后获得的全局方法
swf: swfPath, // flash模式下用到的swf文件,非必须
xml: xmlPath, // 全景图xml路径,用于描述整个全景图
target: 'mount', // 挂载点id,唯一必须的参数
html5: 'prefer', // krpano支持flash和html5两种模式,此属性确定是否优先用html5,具体规则见:https://krpano.com/docu/html/#html5
onready() {
// 回调函数,类似的还有onerror
console.log('load krpano success!');
},
});
</script>
</body>
接下来编写我们的全景图 xml,来描述我们的全景图。常用的 krpano 元素有很多,我们先用其中最基本的几个搭建一个只有最基本的预览图的全景图。最基本的 xml 如下:
<krpano>
<!-- 预览图,目前使用网格预览 -->
<preview type="grid(cube,64,64,512,0xCCCCCC,0xF6F6F6,0x999999);" />
<!-- view/fov设置 -->
<view hlookat="0" vlookat="0" fovtype="VFOV" fov="90" fovmin="30" fovmax="150" />
</krpano>
可以看到 krpano xml 和标准的 xml 语法基本一致。此外,krpano xml 还有以下几个特点:
- xml 文件的根节点必须是
<krpano>
元素。 - 元素上的一些属性会被特殊处理。
- krpano xml 中的所有元素和属性都是可选的,能够以任意顺序定义,且能够定义多次。当一个元素被多次定义时,前面的声明将被后续的声明覆盖。
- krpano xml 只是一个传输格式,只用来向播放器传输数据。当 xml 被解析时,所有 xml 元素都会被转化为 krpano 内部的数据结构供后续使用。
此时效果很简单,就是一个空旷的网格空间,可以拖动、缩放:
这样的网格空间显然不是我们想要的全景图,我们还需要为其添加全景图片。全景图片有多种形式,常见的是六面图(Cube)和鱼眼图(Fisheye)。不同的图片引入的方式略有不同,接下来主要讲解六面图的使用。
首先在 xml 中添加一个 <image>
标签,作为图片显示的容器,然后根据图片类型添加子元素 <cube>
,表示我们使用的是六面图。接着设置六面图的 url。url 中比较特殊的是 %s
占位符,在程序实际运行时将会被替换为 f,b,l,r,t,d 六个字符,替换后的结果分别作为前后左右上下六个方向的图片。
<image>
<cube url="https://qhyxpicoss.kujiale.com/r/2017/09/01/L3D221IS3QKUQUQBOGAPEK3P3XU888_7500x1250.jpg_%s" />
</image>
这时候已经可以算是一个最基本的全景图了。
坐标系和视角
可以看到,之前的全景图 xml 中还有一个 <view>
元素。这个元素描述了用户观测相关的信息(视角、fov 等),类似于相机设置。
在解释视角设置之前,先介绍下 krpano 的坐标系统。
在 krpano 全景图中,无论使用的是什么类型的图片,都会被映射到一个球面中。因此 Krpano 使用球坐标系来表示空间内的一个点。其中半径 r 是固定的,使用方位角和仰角的组合 (h, v) 来确定全景图内的一个点。六面体正前方图片 f 的中心和坐标原点重合。
我们在 <view>
元素上设置了多个属性,其中最重要的是:
hlookat
:当前视角水平方向的角度,取值范围为 - 180 到 180 度。vlookat
:当前视角水平方向的角度,取值范围为 - 90 到 90 度。fov
:当前视角的视野,决定了视角所见区域的大小。在使用滚轮缩放全景图时,实际上就是在调整 fov。fov 越小,视觉上离全景图越近。fov 的默认值为 90 度,取值范围为 0 到 179 度。
下面的例子提供了视角数据的实时显示:
此外,还可以通过 <view>
元素对用户的视角进行一些限制。例如 fovmin
,fovmax
等属性可以用于限制用户调节 fov
的范围。
二、通过热点和事件与全景图交互
我们已经有了一个简单的全景图,但是和线上的全景图相比,还少了很多和用户交互的内容。这一节主要介绍如何为全景图添加热点和响应事件。
热点
热点是对全景图的标注,会随着全景图的旋转而运动。Krpano 中的热点有两种:
-
多边形热点由多个点定义得到的多边形区域
-
图片热点将图片作为热点
接下来以图片热点为例进行讲解,其他类型热点的使用可以参考官方文档中的对应章节。下面的图片热点的 xml 包含了热点最基本的几个参数
<hotspot
name="image_hotspot"
url="https://xxxx.xxx.com/hotspot/hs_001.png"
ath="10"
atv="10"
/>
- name 是热点名称,用于标识和管理热点。
- url 显而易见是指热点使用的图片的 url。
- ath 和 atv 则表示热点的坐标。
热点之所以叫 “热点”,是因为用户可以与其交互。热点支持一系列的事件,最常用的是 onclick
,一般会在点击热点时触发场景切换等行为。
<hotspot
onover=""
onhover=""
onout=""
ondown=""
onup=""
onclick=""
onloaded=""
/>
和 html 元素的事件类似,在事件的回调中可以调用 krpano 脚本的方法,如调用 loadScene()
方法加载新的场景,或是使用 js()
方法来调用 JavaScript 中定义的方法。
事件
热点和其他元素支持的事件只有和其交互相关的一部分,对于 krpano 本身的事件,我们需要使用 <event>
元素来进行回调的设置。
krpano 的事件分为两大类:
- krpano 全局事件:在
<events>
元素中定义,且不带有 name 属性。即使有多个<events>
元素,每个全局事件也只有一个回调生效,后来的定义将覆盖前面的。需要注意的是,如果加载了新的场景或 xml,全局事件依然会保留,直到被新的定义覆盖。 - 独立的局部事件:当
<events>
元素带有 name 属性的时候,将会成为独立的局部事件。其中定义的事件回调不会覆盖全局事件及其他局部事件,而是会同时生效。
无论是全局事件还是局部事件,都支持一系列不同作用的事件。可以在官方文档的 events 的介绍中找到支持的事件的列表。
例如,下面这段代码定义个一个局部事件元素,使得当前的场景内每次点击鼠标都会调用 JavaScript 的 console.log () 方法。
<events
name="localEvents"
onmousedown="js(console.log('message from local events'));"
/>
结合事件和热点,我们可以实现在视角变化 (onviewchange) 的时候显示出当前的视角设置:
三、场景
在我们之前的例子中,讲解了只包含一个场景的全景图如何定义。很多情况下,全景图是包含多个场景的,用户可以通过热点在多个场景中进行切换。在多场景的全景图中,使用 <scene>
元素来定义场景。每个场景可以具有自己独立的全景图片、热点、视角等内容。
<scene name="..." onstart="">
<image>...</image>
<hotspot>...</hotspot>
<!-- other contents -->
</scene>
<scene>
中的内容在场景被加载之前不会载入,无法被访问。因此,需要调用 loadscene()
这个内置的方法来载入一个场景作为初始值。场景的切换和加载初始场景一样,都是调用 loadscene()
。
<krpano onstart="loadscene(scene1);">
<scene name="scene1"></scene>
<scene name="scene2"></scene>
<view />
</krpano>
下面的这个例子结合了之前讲过的热点和场景加载切换,实现了一个基本的多场景全景图,使得用户能够点击热点切换场景。
<krpano onstart="loadscene(scene1);">
<!-- grid preview pano -->
<preview type="grid(cube,64,64,512,0xCCCCCC,0xF6F6F6,0x999999);" />
<scene name="scene1">
<image>
<cube url="https://qhyxpicoss.kujiale.com/r/2017/09/01/L3D221IS3QKUQUQBOGAPEK3P3XU888_7500x1250.jpg_%s" />
</image>
<hotspot
name="image_hotspot"
type="image"
url="https://720-cdn3.3vjia.com/Panorama/public3.2.2/hotspot/hs_001.png"
ath="0"
atv="0"
onclick="loadscene(scene2);"
/>
</scene>
<scene name="scene2">
<image>
<cube url="https://qhyxpicoss.kujiale.com/r/2017/12/25/L3D221I5P2YEIUG5TL4MUG3P3XE888_7500x1250.jpg_%s" />
</image>
<!-- place your scene hotspots here -->
</scene>
<!-- view/fov settings -->
<view hlookat="0" vlookat="0" fovtype="VFOV" fov="90" fovmin="30" fovmax="150" />
</krpano>
四、与 JavaScript 交互
我们通常还需要让 krpano 和 JavaScript 代码进行交互,从而实现诸如热点开关、场景选择、点击热点打开弹窗展示详情等功能。
Krpano 调用 JavaScript
krpano 调用 JavaScript 主要使用以下几个函数:
-
jsget()
:用于获取 JavaScript 变量的值或者某个方法的返回值。例如,jsget(ret, 'location.href');
表示将 location.href 的值赋给 ret -
js()
:调用 JavaScript 中的函数。这种方式一般是为了将 JavaScript 函数作为事件的回调。// code in .html or .js function jsFunction(p1, p2) { alert(`look at ${p1}, ${p2}`); } // code in .xml js(jsFunction(get(view.hlookat), get(view.vlookat)));
-
jscall()
:执行任意 JavaScript 代码,不推荐使用。
JavaScript 操作 Krpano
想要使用 JavaScript 控制 krpano,需要得到 krpano Javascript-Interface object。
这个对象可以通过两种方式得到:
- 在
embedpano()
的onready
回调函数被调用时,会作为参数传入,此时可以保存其引用。这是 krpano 推荐的做法。 - 或者,在 JavaScript 代码中使用
document.getElementById(id)
得到。其中的 id 可以在embedpano()
的配置中指定,其默认为krpanoSWFObject
这个对象对外暴露了以下几个方法:
set(variable, value)
:设置 krpano 变量的值get(variable)
:读取 krpano 变量的值call(action)
:调用任意 actionspheretoscreen(h,v)
:调用spheretoscreen
screentosphere(x,y)
:调用screentosphere
一个基本的例子是通过 JavaScript 读取和设置 Krpano 当前的视角值
var swfPath = '../krpano-lib/krpano.swf';
var xmlPath = './krpano.xml';
var hlookat = document.querySelector('input[name="hlookat"]');
var vlookat = document.querySelector('input[name="vlookat"]');
var fov = document.querySelector('input[name="fov"]');
var buttonGet = document.querySelector('button[name="getview"]');
var buttonSet = document.querySelector('button[name="setview"]');
embedpano({
swf: swfPath,
xml: xmlPath,
target: 'mount',
html5: 'prefer',
onready(krpano) {
function getView() {
hlookat.value = krpano.get('view.hlookat');
vlookat.value = krpano.get('view.vlookat');
fov.value = krpano.get('view.fov');
}
function setView() {
krpano.set('view.hlookat', hlookat.value);
krpano.set('view.vlookat', vlookat.value);
krpano.set('view.fov', fov.value);
}
buttonGet.onclick = getView;
buttonSet.onclick = setView;
getView();
},
});
更多的例子可以参考官方提供的 Demo,该 demo 展示了通过 JavaScript 操作全景图的效果。
五、更多内容
设备检测
很多时候,我们需要对不同平台或特性的设备进行差异化的配置,例如在浏览器不支持 webGL 的时候显示特定的提示元素或在 macOS 下停用自动旋转等。
krpano 内置了一定的设备检查能力,并提供了元素和属性两个层面的支持。
- 对于任意元素,可以通过 devices 属性使该元素及其子元在指定设备上生效。
- 对于元素的属性,可以使用属性的 device check 来进行控制
插件系统
krpano 支持很多官方和第三方插件,包括 WebVR、雷达图、陀螺仪、雨雪效果等。因为不同的插件使用方式不同,本文并没有对其进行讲解。
除了使用官方提供的插件以外,还可以使用 JavaScript 自行开发 krpano 插件。
Actions/Scripting
krpano 支持在 XML 中使用一种简单的脚本语言来控制全景图,就好比 HTML 中使用 JavaScript,这让 Krpnao 制作的全景图有了更丰富的交互能力。类似于 HTML 中的 <script>
,krpano 脚本在 XML 的 <action>
标签中定义。
需要注意的是,虽然 krpano 提供了这样的脚本语言,但是其能够实现的绝大多数功能都可以在 JavaScript 代码中通过操控 krpano 来实现,为了逻辑统一和代码的可维护性,推荐将这些逻辑写在 JavaScript 代码中。
下面的 action 示例定义了一个自动加载 startscene
指定的初始场景的脚本,其中使用了可选的 autorun
属性使得这段脚本会在应用启动时被自动触发。
<action name="startup" autorun="onstart">
if(startscene === null OR !scene[get(startscene)], copy(startscene,scene[0].name); );
loadscene(get(startscene), null, MERGE);
if(startactions !== null, startactions() );
</action>
Action 的详细使用方法可以参考:https://krpano.com/docu/actions/#top
本文所有 demo 代码可在此处查看