15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 思源笔记折腾记录-整一个全景图浏览器-简单的全景图渲染

思源笔记折腾记录-整一个全景图浏览器-简单的全景图渲染

时间:2023-07-19 23:36:02 | 来源:网站运营

时间:2023-07-19 23:36:02 来源:网站运营

思源笔记折腾记录-整一个全景图浏览器-简单的全景图渲染:

一、前情提要

其实我的本行是设计师嘛,所以总是经常需要浏览一下全景图什么的,但是网上的全景图网站总是有点限制,而且把图片全都交给在线服务总有种失控的感觉,所以这回我们来搞一个本地的全景图浏览器。

之前弄了一个noob-service-vite​​,用来通过vite伺服文件夹方便写点小功能。

思源笔记折腾记录 - 做一个白板 - 最最最基础的卡片显示 - 链滴 (ld246.com)

但是它只写死了伺服一个文件夹(也就是那个白板),为了方便鼓捣,我们来尝试弄一个新活。

二、准备工作

1、自动伺服新挂件

我们对之前的noob-service-vite做一点改造。

首先是让他能够自动识别data/viteWidgets​中的文件夹:

​noob-service-vite/util/file.js​

let fs = require('fs')let path = require('path')export let vite挂件目录 = window.siyuan.config.system.workspaceDir +'/data/viteWidgets'export function 读取挂件列表(){ let 挂件列表 =[] let 路径列表= require('fs').readdirSync(vite挂件目录) 路径列表.forEach( 路径名=>{ //如果目录下有vite.config.js if(fs.existsSync(path.join(vite挂件目录,路径名,'vite.config.js'))){ 挂件列表.push({ name:路径名, path:path.join(vite挂件目录,路径名) }) } } ) return 挂件列表}

2、自动伺服

这样之后,就可以改造最开始的noob-service-vite​​了

​noob-service-vite/index.js​

import noobApi from '../noobApi/index.js'import { 读取挂件列表 } from './util/file.js'import { 获取可用端口号 } from './util/port.js'if (window.require) { const path = require('path') const vite = require('vite') let vite服务注册表 = {} 读取挂件列表().forEach( 挂件属性 => { 创建vite服务(挂件属性.path) } ) function 创建vite服务(文件夹路径) { return new Promise(async (resolve, reject) => { let vite配置文件路径 = path.join(文件夹路径, 'vite.config.js') if (!vite服务注册表.文件夹路径) { let server = await vite.createServer({ configFile: vite配置文件路径, root: 文件夹路径, }) !vite服务注册表[文件夹路径]?vite服务注册表[文件夹路径]={}:null vite服务注册表[文件夹路径]['server'] = server let port if (server.config.server.port) { port = await 获取可用端口号(server.config.server.port) } else { port =await 获取可用端口号(6806) } await server.listen(port,'127.0.0.1').then( s => { console.log(文件夹路径,'的开发服务在:',port,'上启用') vite服务注册表[文件夹路径]['port'] = port } ) resolve(server) } }) }}获取可用端口号的实现其实就是尝试能不能监听这个端口,如果不能就试个新的:

export async function 获取可用端口号(端口号){ return new Promise((resolve, reject) => { let http = require('http') let 测试服务 = http.createServer() let 可用端口号 = 端口号||3000 测试服务.on( 'listening',()=>{ console.log(端口号) 测试服务.close(()=>{ resolve(可用端口号) }) } ) 测试服务.on( 'error',async(error)=>{ console.log(error) if(error.code==='EADDRINUSE'){ resolve(await 获取可用端口号(可用端口号+1)) } else{ reject(error) } } ) 测试服务.listen(端口号) }) }

二、实现简单的全景图浏览

1、安装photo sphere viewer

这个很简单啦:

npm i photo sphere viewer --registry=https://registry.npmmirror.com

2、最简单的球面全景图查看器

欸,我就是不写index.html​的内容,你们试着自己弄一下。

要实现一个最简单的全景图i浏览只需要几行代码啦:

import { Viewer } from 'photo-sphere-viewer';//这里是引入了css,vite里面你这么干跟在html里面引入差不多import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'const viewer = new Viewer({ container: document.querySelector('#viewer'), //这个是我之前上传到工作空间里的一张全景图 panorama: 'assets/11111D5_全景图 1_20221211_061304-20221211182953-7i0dt0q.jpg',});就像这样:





这张全景的原图长这样:





第一个全景图就渲染出来了~~

3、 加上画廊

photo sphere viewer有很多插件,其中有一个PhotoSphereViewer.GalleryPlugin​是用来生成一连串的全景图的。

PhotoSphereViewer.GalleryPlugin这里我们约定文件名为:全景图-<项目名>-<空间名>-<id>.jpg的附件文件就是某一个系列的全景图。

所以这里可以搞一个sql:

select * from assetswhere name like '全景图-${项目名}-%'然后我们来实验一下:

​sphereViewer/src/index.js​

import { Viewer } from 'photo-sphere-viewer';import {GalleryPlugin} from 'photo-sphere-viewer/dist/plugins/gallery.js'import 核心api from 'http://127.0.0.1:6806/snippets/noobApi/util/kernelApi.js'import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'import 'photo-sphere-viewer/dist/plugins/gallery.css'import { 获取地址参数 } from '../../whiteBoard/src/data';let {project} =获取地址参数()console.log(project)let sql =`select * from assets where name like '全景图-${project}-%'`let 全景图列表 = await 核心api.sql({stmt:sql})console.log(全景图列表)全景图列表.forEach( item=>{ item.panorama=item.path item.thumbnail =item.path let 空间名 = item.name.split('-')[2] item.options ={ caption: 空间名, } })console.log(Viewer,GalleryPlugin)const viewer = new Viewer({ container: document.querySelector('#viewer'), panorama: 全景图列表[0].path, plugins: [ [GalleryPlugin, { visibleOnLoad: true, }], ],});const gallery = viewer.getPlugin(GalleryPlugin);gallery.setItems(全景图列表)



嗯,好像渲染成功了~~~。

4、更方便的项目创建

啊,我们现在有一个全景图浏览器了,但是一张张图上传到思源的assets里面好像太麻烦了点,有没有更方便的办法咧?

首先来设想一个简单的全景图项目文件夹结构







全部通过assets​来管理好像有点麻烦,而且它的嵌套结构也不适合放到assets里面一起管理,全景图的数据也不大可能会被用到别的地方。

所以,我们来起一个后端吧,专门用来给这个网页提供服务(什么叫BFF啊~~)。

之前在noob-service-vite中,我们直接让vite来创建和监听服务器,这回改一下,使用中间件模式。

function 创建vite服务(文件夹路径) { return new Promise(async (resolve, reject) => { let vite配置文件路径 = path.join(文件夹路径, 'vite.config.js') if (!vite服务注册表.文件夹路径) { let vite中间件 = await vite.createServer({ configFile: vite配置文件路径, root: 文件夹路径, server: { middlewareMode: "html" } }) const app = express() !vite服务注册表[文件夹路径] ? vite服务注册表[文件夹路径] = {} : null vite服务注册表[文件夹路径]['server'] = app let port = vite中间件.config.server.port || 6807 port = await 获取可用端口号(port) let server = await app.listen(port, '127.0.0.1', async () => { vite服务注册表[文件夹路径]['port'] = port vite服务注册表[文件夹路径]['server'] = server vite服务注册表[文件夹路径]['app'] = app vite服务注册表[文件夹路径]['vite'] = vite中间件 console.log(文件夹路径, '的开发服务在:', 'http://127.0.0.1:' + port, '上启用') if (require('fs').existsSync(文件夹路径 + '/backend/index.js')) { //因为某些蛋疼的原因,这里只能用require let router = require(文件夹路径 +'/backend/index.js') //主意router不要妨碍vite的工作,也就是别占了vite的坑 app.use(router) app.use(vite中间件.middlewares) } })/*.then( s => { let {address,port} = server.httpServer.address() console.log(文件夹路径,'的开发服务在:','http://'+address+':'+port,'上启用') vite服务注册表[文件夹路径]['port'] = port console.log(server) } )*/ resolve(server) } }) }这样之后,随便试一下,就把文件存在data/widgetsData​里面吧。

按照上面约定的后端文件入口,先弄两个接口吧:

const express = require('express')let router = express.Router()const fs = require('fs')const path = require('path')let 数据路径 = path.join(window.siyuan.config.system.workspaceDir,'data','widgetsData','sphereViewer')if(fs.existsSync(数据路径)){ //为了避免跟思源的接口重合,挂件后端的接口全都加上widget前缀好了 router.use('/widgetData',express.static(数据路径)) router.use('/widgetApi/projects/getAll',(req,res)=>{ res.json(fs.readdirSync(数据路径) })}//上面已经做了自动导入,回自动把这个接口混入vite的开发服务器当中module.exports=router这样之后,往这个文件夹里面放一点文件:





这个时候访问一下127.0.0.1:6809/widgetApi/projects/listAllProjects​(这个端口是在vite.config.js里设置的)。





可以看到我们刚刚写的后端已经返回了项目列表。

我们之前是从思源的sql获取数据的,所以上面的查看器的获取方式也要改一下。

但是因为之后可能还需要其他来源的全景图数据的渲染,所以这里也使用一个跟之前做简单白板的时候类似的适配器。

​sphereViewer/src/adapters/sql.js​

import { 核心api } from "../../../whiteBoard/src/data"export class sql全景图适配器{ 获取项目列表(){ } async 获取全景图列表(项目名){ let sql =`select * from assets where name like '全景图-${项目名}-%'` let 全景图列表 = await 核心api.sql({stmt:sql}) 全景图列表.forEach( item=>{ item.panorama=item.path item.thumbnail =item.path let 空间名 = item.name.split('-')[2] item.options ={ caption: 空间名, } } ) return 全景图列表 }}import { Viewer } from 'photo-sphere-viewer';import {GalleryPlugin} from 'photo-sphere-viewer/dist/plugins/gallery.js'import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'import 'photo-sphere-viewer/dist/plugins/gallery.css'import { 获取地址参数 } from '../../whiteBoard/src/data';import {sql全景图适配器} from "./adapters/sql.js"let {project} =获取地址参数()console.log(project)let 当前适配器 = new sql全景图适配器()let 全景图列表= await 当前适配器.获取全景图列表(project) const viewer = new Viewer({ container: document.querySelector('#viewer'), panorama: 全景图列表[0].path, plugins: [ [GalleryPlugin, { visibleOnLoad: true, }], ],});const gallery = viewer.getPlugin(GalleryPlugin);gallery.setItems(全景图列表)这个时候访问127.0.0.1:6809/?project=云park,应该可以看到跟之前一样的结果。

然后来试一下写一个后端适配器:

export class sql全景图适配器{ 获取项目列表(){ } async 获取全景图列表(项目名){ let res = await fetch('widgetApi/projects/getScenesByName',{ method:"POST", body:{ name:项目名 } }) let 全景图列表 = await res.json() return 全景图列表 }}

啊,是不是跟从思源获取数据挺像的,这个时候getScenesByName​这个接口还不在,所以我们需要实现一下它。

router.post('widgetApi/projects/getScenesByName',(req,res)=>{ let {name} = req.body let 场景列表 = [] let 项目文件列表 = fs.readdirSync(path.join(数据路径,name)) 项目文件列表.forEach( 路径名=>{ let 缩略图路径 if( fs.existsSync(path.join(数据路径,name,路径名,'thumbnail.jpg'))){ 缩略图路径 = path.join('/widgetData',name,路径名,'thumbnail.jpg').replace(////g,'/') }else if( fs.existsSync(path.join(数据路径,name,路径名,'_f.jpg')) ){ 缩略图路径=path.join('/widgetData',name,路径名,'_f.jpg').replace(////g,'/') }else if( fs.existsSync(path.join(数据路径,name,路径名,'sphere.jpg')) ){ 缩略图路径=path.join('/widgetData',name,路径名,'sphere.jpg').replace(////g,'/') } if(fs.existsSync(path.join(数据路径,name,路径名,'_b.jpg'))){ 场景列表.push( { id:路径名, panorama:{ left: path.join('/widgetData',name,路径名,'_r.jpg').replace(////g,'/'), front: path.join('/widgetData',name,路径名,'_b.jpg').replace(////g,'/'), right: path.join('/widgetData',name,路径名,'_l.jpg').replace(////g,'/'), back: path.join('/widgetData',name,路径名,'_f.jpg').replace(////g,'/'), top: path.join('/widgetData',name,路径名,'_u.jpg').replace(////g,'/'), bottom: path.join('/widgetData',name,路径名,'_d.jpg').replace(////g,'/'), }, thumbnail:缩略图路径, options:{ caption:name, } } ) } else if(fs.existsSync(path.join(数据路径,name,路径名,'sphere.jpg'))){ 场景列表.push( { id:路径名, panorama:path.join('/widgetData',name,路径名,'sphere.jpg').replace(////g,'/'), options:{ caption:name, }, thumbnail:缩略图路径, } ) } } ) res.json(场景列表) })看起来应该跟上面的这个差不多对吧,就是识别文件夹里的文件,然后匹配下路径然后返回。

不过这里有个地方需要注意:

panorama:{ left: path.join('/widgetData',name,路径名,'_r.jpg').replace(////g,'/'), front: path.join('/widgetData',name,路径名,'_b.jpg').replace(////g,'/'), right: path.join('/widgetData',name,路径名,'_l.jpg').replace(////g,'/'), back: path.join('/widgetData',name,路径名,'_f.jpg').replace(////g,'/'), top: path.join('/widgetData',name,路径名,'_u.jpg').replace(////g,'/'), bottom: path.join('/widgetData',name,路径名,'_d.jpg').replace(////g,'/'),},因为渲染器给出的六面全景图一般是按照“人在方盒子里面”来适配六个面的,而PhotoSphereViewer​是按照“人在方盒子外面”来读取的,所以读取文件的时候,除了上下不用反之外,其他前后左右都要反一下~~~

我们试着使用一下这个适配器:

import {后端适配器} from './adapters/internal.js'let {project} =获取地址参数()console.log(project)let 当前适配器 = new 后端适配器()let 全景图列表= await 当前适配器.获取全景图列表(project) const viewer = new Viewer({ container: document.querySelector('#viewer'), panorama: 全景图列表[0].path, plugins: [ [GalleryPlugin, { visibleOnLoad: true, }], ],});const gallery = viewer.getPlugin(GalleryPlugin);gallery.setItems(全景图列表)然后它就报错了:





因为我们使用的express​后端并没有自带解析post​请求的body​的能力,这个时候需要一个body-parser​

一样的,首先需要安装它:

​npm i --registry=https://registry.npmmirror.com body-parser​

然后引入并使用:

const bodyParser = require('body-parser')router.post('/widgetApi/projects/getScenesByName', (req,res,next)=>{ //强制按json解析,这样前端就不用设置content-type了 req.headers['content-type']='application/json' //记得调用next() next() }, bodyParser.json(), let {name} = req.body let 场景列表 = [] let 项目文件列表 = fs.readdirSync(path.join(数据路径,name)) 项目文件列表.forEach( 路径名=>{ let 缩略图路径 if( fs.existsSync(path.join(数据路径,name,路径名,'thumbnail.jpg'))){ 缩略图路径 = path.join('/widgetData',name,路径名,'thumbnail.jpg').replace(////g,'/') }else if( fs.existsSync(path.join(数据路径,name,路径名,'_f.jpg')) ){ 缩略图路径=path.join('/widgetData',name,路径名,'_f.jpg').replace(////g,'/') }else if( fs.existsSync(path.join(数据路径,name,路径名,'sphere.jpg')) ){ 缩略图路径=path.join('/widgetData',name,路径名,'sphere.jpg').replace(////g,'/') } if(fs.existsSync(path.join(数据路径,name,路径名,'_b.jpg'))){ 场景列表.push( { id:路径名, panorama:{ left: path.join('/widgetData',name,路径名,'_r.jpg').replace(////g,'/'), front: path.join('/widgetData',name,路径名,'_b.jpg').replace(////g,'/'), right: path.join('/widgetData',name,路径名,'_l.jpg').replace(////g,'/'), back: path.join('/widgetData',name,路径名,'_f.jpg').replace(////g,'/'), top: path.join('/widgetData',name,路径名,'_u.jpg').replace(////g,'/'), bottom: path.join('/widgetData',name,路径名,'_d.jpg').replace(////g,'/'), }, thumbnail:缩略图路径, options:{ caption:name, } } ) } else if(fs.existsSync(path.join(数据路径,name,路径名,'sphere.jpg'))){ 场景列表.push( { id:路径名, panorama:path.join('/widgetData',name,路径名,'sphere.jpg').replace(////g,'/'), options:{ caption:name, }, thumbnail:缩略图路径, } ) } } ) res.json(场景列表)}然后再看一下127.0.0.1:6809/?project=云park48

这个时候它显示:





这是因为PhotoSphereViewer​默认使用的是球面图适配器,而我们优先返回的是六面图,有关它的适配器可以参考文档:

Adapters | Photo Sphere Viewer (photo-sphere-viewer.js.org)

先不纠结,按照文档使用六面图适配器看看:

​sphereViewer/src/index.js​

let 全景图选项 = { container: document.querySelector('#viewer'), panorama: 全景图列表[0].panorama, plugins: [ [GalleryPlugin, { visibleOnLoad: true, }], ],}//如果有六面图文件,就使用六面图适配器if(全景图列表[0].panorama.left){ 全景图选项.adapter = CubemapAdapter}let viewer= new Viewer(全景图选项)这回显示对了,效果就像这样:






这回好像终于干了点跟设计师沾边的活儿了~~~


目前的代码片段的地址位于:

leolee9086/snippets (github.com)

viteWidgets的地址位于

leolee9086/viteWidgets (github.com)

啊 全景图文件我就不提供了,自己试试吧。

关键词:浏览,简单,渲染,笔记,折腾,记录

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭