Files
iD/modules/services/pannellum_photo.js
Martin Raifer 3c9d232d41 make sure pannellum viewer is ready to load new photo, fixes #11054
while loading a "scene", pannellum cannot load a new panorama scene. This means we have to wait while the viewer is busy loading an existing panorama until we can load a new one.
2025-05-21 16:38:31 +02:00

204 lines
5.3 KiB
JavaScript

import { select as d3_select } from 'd3-selection';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { utilRebind } from '../util';
const pannellumViewerCSS = 'pannellum/pannellum.css';
const pannellumViewerJS = 'pannellum/pannellum.js';
const dispatch = d3_dispatch('viewerChanged');
let _currScenes = [];
let _pannellumViewer;
let _activeSceneKey;
export default {
init: async function(context, selection) {
selection
.append('div')
.attr('class', 'photo-frame pannellum-frame')
.attr('id', 'ideditor-pannellum-viewer')
.classed('hide', true)
.on('mousedown', function(e) { e.stopPropagation(); });
if (!window.pannellum) {
await this.loadPannellum(context);
}
const options = {
'default': { firstScene: '' },
scenes: {},
minHfov: 20,
disableKeyboardCtrl: true,
sceneFadeDuration: 0
};
_pannellumViewer = window.pannellum.viewer('ideditor-pannellum-viewer', options);
_pannellumViewer
.on('mousedown', () => {
d3_select(window)
.on('pointermove.pannellum mousemove.pannellum', () => {
dispatch.call('viewerChanged');
});
})
.on('mouseup', () => {
d3_select(window)
.on('pointermove.pannellum mousemove.pannellum', null);
})
.on('animatefinished', () => {
dispatch.call('viewerChanged');
});
context.ui().photoviewer.on('resize.pannellum', () => {
_pannellumViewer.resize();
});
this.event = utilRebind(this, dispatch, 'on');
return this;
},
loadPannellum: function(context) {
const head = d3_select('head');
return Promise.all([
new Promise((resolve, reject) => {
// load pannellum viewer css
head
.selectAll('#ideditor-pannellum-viewercss')
.data([0])
.enter()
.append('link')
.attr('id', 'ideditor-pannellum-viewercss')
.attr('rel', 'stylesheet')
.attr('crossorigin', 'anonymous')
.attr('href', context.asset(pannellumViewerCSS))
.on('load.pannellum', resolve)
.on('error.pannellum', reject);
}),
new Promise((resolve, reject) => {
// load pannellum viewer js
head
.selectAll('#ideditor-pannellum-viewerjs')
.data([0])
.enter()
.append('script')
.attr('id', 'ideditor-pannellum-viewerjs')
.attr('crossorigin', 'anonymous')
.attr('src', context.asset(pannellumViewerJS))
.on('load.pannellum', resolve)
.on('error.pannellum', reject);
})
]);
},
/**
* Shows the photo frame if hidden
* @param {*} context the HTML wrap of the frame
*/
showPhotoFrame: function(context) {
const isHidden = context.selectAll('.photo-frame.pannellum-frame.hide').size();
if (isHidden) {
context
.selectAll('.photo-frame:not(.pannellum-frame)')
.classed('hide', true);
context
.selectAll('.photo-frame.pannellum-frame')
.classed('hide', false);
}
return this;
},
/**
* Hides the photo frame if shown
* @param {*} context the HTML wrap of the frame
*/
hidePhotoFrame: function(viewerContext) {
viewerContext
.select('photo-frame.pannellum-frame')
.classed('hide', false);
return this;
},
/**
* Renders an image inside the frame
* @param {*} data the image data, it should contain an image_path attribute, a link to the actual image.
* @param {boolean} keepOrientation if true, HFOV, pitch and yaw will be kept between images
*/
selectPhoto: function(data, keepOrientation) {
const key = _activeSceneKey = data.image_path;
if (!_currScenes.includes(key)) {
let newSceneOptions = {
showFullscreenCtrl: false,
autoLoad: false,
compass: false,
yaw: 0,
type: 'equirectangular',
preview: data.preview_path,
panorama: data.image_path,
northOffset: data.ca
};
_currScenes.push(key);
_pannellumViewer.addScene(key, newSceneOptions);
}
let yaw = 0;
let pitch = 0;
let hfov = 0;
if (keepOrientation) {
yaw = this.getYaw();
pitch = this.getPitch();
hfov = this.getHfov();
}
if (_pannellumViewer.isLoaded() !== false) {
_pannellumViewer.loadScene(key, pitch, yaw, hfov);
dispatch.call('viewerChanged');
} else {
// pannellum is currently loading another scene: wait for it to finish
// loading the previous panorama first
const retry = setInterval(() => {
if (_pannellumViewer.isLoaded() === false) {
// still not done: wait a bit longer
return;
}
if (_activeSceneKey === key) {
// only load scene if no other photo has been selected in the meantime
_pannellumViewer.loadScene(key, pitch, yaw, hfov);
dispatch.call('viewerChanged');
}
clearInterval(retry);
}, 100);
}
if (_currScenes.length > 3) {
const old_key = _currScenes.shift();
_pannellumViewer.removeScene(old_key);
}
_pannellumViewer.resize();
return this;
},
getYaw: function() {
return _pannellumViewer.getYaw();
},
getPitch: function() {
return _pannellumViewer.getPitch();
},
getHfov: function() {
return _pannellumViewer.getHfov();
}
};