全景图渲染Vue3+TS使用Photo Sphere Viewer插件实现

617 阅读4分钟

前段时间公司某项目需要做全景图展示和图上附加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

切片效果:

微信截图_20230624162118.png

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, 
   }); 
});

暂时只到这里,因公司项目安排全景图被毙掉了,后续有缘待续……