前段时间公司某项目需要做全景图展示和图上附加Mark标签,各种地方东拼西凑算是实现了。
1.Photo Sphere Viewer插件安装:
npm install photo-sphere-viewer -S
或
yarn add photo-sphere-viewer -S
2.原始全景图展示
<div id="viewer"></div>
// 全景图的根节点必须要具备宽高
<style> #viewer { width: 100vw; height: 100vh; } </style>
<script lang="ts" setup>
//先导入photo-sphere-viewer插件和它的样式
import { Viewer } from "photo-sphere-viewer";
import "photo-sphere-viewer/dist/photo-sphere-viewer.css";
// 定义视图容器
let viewer: Viewer | null;
//注意这里应该在onMounted初始化,不然可能找不到html元素
onMounted(() => {
viewer = new Viewer({
container: "viewer",
//全景图路径,全景图放置在public路径下的写法;放置在src路径下需要改写为require("路径")
panorama: '/panorama/vr.jpg',
navbar: undefined,
plugins: [],
})
})
</script>
使用本地的原始全景图资源文件来进行展示初次加载需要很长时间,如果全景图是小于2MB的,这种展示方式的加载稍微能容忍。但是全景图基本上都是高分辨率的高清大图,所以需将全景图进行切片,切成一块块比较小的图片然后按需加载,提高加载速度。
3.全景图切片
①降低初始全景级别分辨率
根据全景图名称(例如vr.jpg)新建一个文件夹(例如vr),然后把原始全景图修改一下尺寸,根据实际开发需要降低全景图的分辨率(例如:1920x1080或2560x1080),另存为low.jpg,保存到vr文件夹下;
②原始高清大图切片
将原始全景图vr.jpg复制到vr文件夹下,现在将原始全景图切片,切成32列16行,相当于把原始全景图切成32×16也就是512份(根据实际开发需求制定切片方案)。
具体的切片步骤如下:
切片工具可使用ImageMagick(需要下载相关软件后使用cmd命令行切片)或online tool,使用ImageMagick的切片命令在原始全景图vr.jpg的路径下运行cmd命令行:-crop **x** ,这里的切片分辨率(即下方切片命令中的-crop 600x600)需要由切片期望(panorama配置中的cols和rows的值,32列x16行或者其他切片行列数)自行计算。filename用于对切片后的瓦片进行命名,这里使用了官方Demo中的默认命名方式,有兴趣的可自行研究。
magick vr.jpg -crop 600x600 -set filename:tile "%[fx:page.x/600]_%[fx:page.y/600]" -set filename:orig %t %[filename:orig]_%[filename:tile].jpg
切片效果:
4.加载全景切片
//引入切片适配器
import { EquirectangularTilesAdapter } from "photo-sphere-viewer/dist/adapters/equirectangular-tiles"
// 定义视图容器
let viewer: Viewer | null;
onMounted(() => {
viewer = new Viewer({
adapter: EquirectangularTilesAdapter,
container: "viewer",
panorama: {
width: 1200,
//这里的列数cols和行数rows的值必须是4的幂(8,16,32,64),最大值为64
cols: 32,
rows: 16,
//这里需要使用降低分辨率后的全景图
baseUrl: `/panorama/low.jpg`,
tileUrl: (col:number, row:number) => {
return `/panorama/vr/vr_${col}_${row}.jpg`
}
},
navbar: undefined,
plugins: [],
})
})
此时进入VR场景基本是秒开的,加载时间能缩短到几十毫秒。
5.Photo Sphere Viewer插件——标记插件MarkersPlugin
// 插件导入
import { MarkersPlugin } from "photo-sphere-viewer/dist/plugins/markers";
import "photo-sphere-viewer/dist/plugins/markers.css";
该插件提供了一个强大的标记系统,允许使用可选的工具定义全景图上的点。标记可以动态添加/删除,您可以定义用户点击事件的响应。
该插件提供四种可选的地图标记类型:
①HTML defined with the attribute html
②Images defined with the attribute image、imageLayer
③SVGs defined with the attribute square、rect、circle、ellipse、path
④Dynamic polygons & polylines defined with the attribute polygon 、 polygonPixels、polyline、polylinePixels
viewer = new Viewer({
adapter: EquirectangularTilesAdapter,
container: "viewer",
// panorama: '/vr/low.jpg',
panorama: {
width: 1200,
cols: 32,
rows: 16,
baseUrl: "/vr/low.jpg",
tileUrl: (col: number, row: number) => {
return `/vr/vr_${col}_${row}.jpg`;
},
},
// navbar: undefined,
// navbar: ["autorotate", "zoom", "caption", "fullscreen"],
plugins: [
[
MarkersPlugin,
{
markers: [
//HTML类型的地图标记
{
id: "新华书店",
longitude: 5.679685955994602,
latitude: -0.1118557478031823,
html: `<div class="marker-content-container published"></div><div class="init-title">新华书店</div> \n <div class="jump-container"> <image style="height:30px;width:30px" src="/mark/pin-red.png"/>\n </div>\n`,
//定义标记朝向其定义位置的放置位置。任何 CSS 位置都有效,例如 或 。bottom center20% 80%
anchor: "bottom center",
//要在标记上设置的 CSS 属性(背景、边框等),对于Image类型标记忽略
style: {
maxWidth: "100px",
color: "white",
fontSize: "20px",
fontFamily: "Helvetica, sans-serif",
textAlign: "center",
},
zoomLvl: 1,
},
],
},
],
],
});
//标记事件初始化
const markersPlugin = viewer.getPlugin(MarkersPlugin);
// 事件——单击标记
markersPlugin?.on("select-marker", (e, marker, data) => {
console.log(`Clicked on marker ${marker.id}`);
console.log(marker.id.split("-"));
console.log(dataInJs().find((arry) => arry.name === `${marker.id}`));
const dec = dataInJs().find((arry) => arry.name === `${marker.id}`)?.dec;
markersPlugin.updateMarker({
id: marker.id,
html: marker.config.html?.replace("pin-red.png", "pin-blue.png"),
tooltip: {
content: `\n <div class="marker-tooltip-container">\n <div class="marker-img-continaer">\n \n <div class="scroll-container marker-img-scroll-container "><img style="width:100%;height:100px" src="/mark/${marker.id}.png"/></div>\n \n <p class="marker-title" style="text-align: center">\n ${marker.id}\n </p>\n <p class="marker-description"> 简介:${dec}</p>\n </div>\n `,
position: "center top",
trigger: "click",
},
});
});
// 事件——已选中标记并且单击其他位置
markersPlugin?.on("unselect-marker", (e, marker) => {
markersPlugin.updateMarker({
id: marker.id,
html: marker.config.html?.replace("pin-blue.png", "pin-red.png"),
});
});
//取点位置初始化
const position = viewer.getPosition();
viewer.on("click", (e, data) => {
console.log(
`${data.rightclick ? "right " : ""}clicked at longitude: ${
data.longitude
} latitude: ${data.latitude}` );
//创建标记
markersPlugin?.addMarker({
id: "标点测试",
longitude: data.longitude,
latitude: data.latitude,
html: `<div class="marker-content-container published"></div><div class="init-title">标点测试</div> \n <div class="jump-container"> <image style="height:30px;width:30px" src="/mark/pin-red.png"/>\n </div>\n`,
anchor: "bottom center",
style: {
maxWidth: "100px",
color: "white",
fontSize: "20px",
fontFamily: "Helvetica, sans-serif",
textAlign: "center",
},
zoomLvl: 1,
});
});
暂时只到这里,因公司项目安排全景图被毙掉了,后续有缘待续……