senangwebs-tour 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +653 -0
- package/dist/swt-editor.css +869 -0
- package/dist/swt-editor.css.map +1 -0
- package/dist/swt-editor.js +2853 -0
- package/dist/swt-editor.js.map +1 -0
- package/dist/swt-editor.min.css +1 -0
- package/dist/swt-editor.min.js +1 -0
- package/dist/swt.js +873 -0
- package/dist/swt.js.map +1 -0
- package/dist/swt.min.js +1 -0
- package/package.json +39 -0
- package/src/AssetManager.js +153 -0
- package/src/HotspotManager.js +193 -0
- package/src/SceneManager.js +162 -0
- package/src/components/hotspot-listener.js +114 -0
- package/src/editor/css/main.css +1002 -0
- package/src/editor/editor-entry.css +4 -0
- package/src/editor/editor-entry.js +30 -0
- package/src/editor/js/editor.js +629 -0
- package/src/editor/js/export-manager.js +286 -0
- package/src/editor/js/hotspot-editor.js +237 -0
- package/src/editor/js/preview-controller.js +556 -0
- package/src/editor/js/scene-manager.js +202 -0
- package/src/editor/js/storage-manager.js +193 -0
- package/src/editor/js/ui-controller.js +387 -0
- package/src/editor/js/ui-init.js +164 -0
- package/src/editor/js/utils.js +217 -0
- package/src/index.js +245 -0
package/dist/swt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swt.js","sources":["../src/components/hotspot-listener.js","../src/AssetManager.js","../src/SceneManager.js","../src/HotspotManager.js","../src/index.js"],"sourcesContent":["/**\r\n * A-Frame component that listens for clicks on hotspots and emits a custom event\r\n */\r\nif (typeof AFRAME !== 'undefined') {\r\n AFRAME.registerComponent('swt-hotspot-listener', {\r\n schema: {\r\n hotspotData: { type: 'string', default: '{}' }\r\n },\r\n\r\n init: function () {\r\n this.onClick = this.onClick.bind(this);\r\n this.onMouseEnter = this.onMouseEnter.bind(this);\r\n this.onMouseLeave = this.onMouseLeave.bind(this);\r\n },\r\n\r\n play: function () {\r\n this.el.addEventListener('click', this.onClick);\r\n this.el.addEventListener('mouseenter', this.onMouseEnter);\r\n this.el.addEventListener('mouseleave', this.onMouseLeave);\r\n },\r\n\r\n pause: function () {\r\n this.el.removeEventListener('click', this.onClick);\r\n this.el.removeEventListener('mouseenter', this.onMouseEnter);\r\n this.el.removeEventListener('mouseleave', this.onMouseLeave);\r\n },\r\n\r\n onClick: function (evt) {\r\n const hotspotData = JSON.parse(this.data.hotspotData);\r\n \r\n // Emit custom event that the Tour class can listen to\r\n this.el.sceneEl.emit('swt-hotspot-clicked', {\r\n hotspotData: hotspotData\r\n });\r\n },\r\n\r\n onMouseEnter: function (evt) {\r\n const hotspotData = JSON.parse(this.data.hotspotData);\r\n \r\n // Show tooltip if exists\r\n if (hotspotData.tooltip) {\r\n this.el.sceneEl.emit('swt-hotspot-hover', {\r\n hotspotData: hotspotData,\r\n isHovering: true\r\n });\r\n }\r\n\r\n // Add visual feedback\r\n this.el.setAttribute('scale', this.getScaledSize(1.2));\r\n },\r\n\r\n onMouseLeave: function (evt) {\r\n const hotspotData = JSON.parse(this.data.hotspotData);\r\n \r\n // Hide tooltip\r\n if (hotspotData.tooltip) {\r\n this.el.sceneEl.emit('swt-hotspot-hover', {\r\n hotspotData: hotspotData,\r\n isHovering: false\r\n });\r\n }\r\n\r\n // Reset scale\r\n this.el.setAttribute('scale', this.getOriginalScale());\r\n },\r\n\r\n getOriginalScale: function () {\r\n const hotspotData = JSON.parse(this.data.hotspotData);\r\n return hotspotData.appearance?.scale || '1 1 1';\r\n },\r\n\r\n getScaledSize: function (multiplier) {\r\n const scale = this.getOriginalScale();\r\n const parts = scale.split(' ').map(Number);\r\n return `${parts[0] * multiplier} ${parts[1] * multiplier} ${parts[2] * multiplier}`;\r\n }\r\n });\r\n\r\n /**\r\n * Billboard component - Makes an entity always face the camera\r\n */\r\n AFRAME.registerComponent('swt-billboard', {\r\n schema: {\r\n enabled: { type: 'boolean', default: true }\r\n },\r\n\r\n init: function () {\r\n this.camera = null;\r\n },\r\n\r\n tick: function () {\r\n if (!this.data.enabled) return;\r\n\r\n // Find camera if not cached\r\n if (!this.camera) {\r\n this.camera = this.el.sceneEl.camera;\r\n if (!this.camera) return;\r\n }\r\n\r\n // Get camera world position\r\n const cameraPosition = new THREE.Vector3();\r\n this.camera.getWorldPosition(cameraPosition);\r\n\r\n // Get this element's world position\r\n const elementPosition = new THREE.Vector3();\r\n this.el.object3D.getWorldPosition(elementPosition);\r\n\r\n // Make the element look at the camera\r\n this.el.object3D.lookAt(cameraPosition);\r\n }\r\n });\r\n}\r\n\r\nexport default 'swt-hotspot-listener';\r\n","/**\r\n * AssetManager - Handles preloading and management of assets in A-Frame's <a-assets>\r\n */\r\nexport class AssetManager {\r\n constructor(sceneEl) {\r\n this.sceneEl = sceneEl;\r\n this.assetsEl = null;\r\n this.loadedAssets = new Map();\r\n this.init();\r\n }\r\n\r\n init() {\r\n // Find or create <a-assets> element\r\n this.assetsEl = this.sceneEl.querySelector('a-assets');\r\n if (!this.assetsEl) {\r\n this.assetsEl = document.createElement('a-assets');\r\n // Wait for scene to be ready before inserting\r\n if (this.sceneEl.hasLoaded) {\r\n this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);\r\n } else {\r\n this.sceneEl.addEventListener('loaded', () => {\r\n this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);\r\n }, { once: true });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Ensure assets element is ready\r\n * @returns {Promise}\r\n */\r\n ensureAssetsReady() {\r\n return new Promise((resolve) => {\r\n if (this.assetsEl && this.assetsEl.parentNode) {\r\n resolve();\r\n } else if (this.sceneEl.hasLoaded) {\r\n if (this.assetsEl && !this.assetsEl.parentNode) {\r\n this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);\r\n }\r\n resolve();\r\n } else {\r\n this.sceneEl.addEventListener('loaded', () => {\r\n if (this.assetsEl && !this.assetsEl.parentNode) {\r\n this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);\r\n }\r\n resolve();\r\n }, { once: true });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Preload an image asset\r\n * @param {string} url - URL of the image\r\n * @param {string} id - Unique ID for the asset\r\n * @returns {Promise} - Resolves when the asset is loaded\r\n */\r\n async preloadImage(url, id) {\r\n if (this.loadedAssets.has(id)) {\r\n return Promise.resolve(this.loadedAssets.get(id));\r\n }\r\n\r\n // Ensure assets element is ready\r\n await this.ensureAssetsReady();\r\n\r\n return new Promise((resolve, reject) => {\r\n const imgEl = document.createElement('img');\r\n imgEl.setAttribute('id', id);\r\n imgEl.setAttribute('src', url);\r\n imgEl.setAttribute('crossorigin', 'anonymous');\r\n\r\n imgEl.addEventListener('load', () => {\r\n this.loadedAssets.set(id, imgEl);\r\n resolve(imgEl);\r\n });\r\n\r\n imgEl.addEventListener('error', (err) => {\r\n console.error(`Failed to load asset: ${url}`, err);\r\n reject(err);\r\n });\r\n\r\n this.assetsEl.appendChild(imgEl);\r\n });\r\n }\r\n\r\n /**\r\n * Preload a video asset\r\n * @param {string} url - URL of the video\r\n * @param {string} id - Unique ID for the asset\r\n * @returns {Promise} - Resolves when the asset is loaded\r\n */\r\n async preloadVideo(url, id) {\r\n if (this.loadedAssets.has(id)) {\r\n return Promise.resolve(this.loadedAssets.get(id));\r\n }\r\n\r\n // Ensure assets element is ready\r\n await this.ensureAssetsReady();\r\n\r\n return new Promise((resolve, reject) => {\r\n const videoEl = document.createElement('video');\r\n videoEl.setAttribute('id', id);\r\n videoEl.setAttribute('src', url);\r\n videoEl.setAttribute('crossorigin', 'anonymous');\r\n videoEl.setAttribute('preload', 'auto');\r\n videoEl.setAttribute('playsinline', '');\r\n videoEl.setAttribute('loop', '');\r\n\r\n videoEl.addEventListener('loadeddata', () => {\r\n this.loadedAssets.set(id, videoEl);\r\n resolve(videoEl);\r\n });\r\n\r\n videoEl.addEventListener('error', (err) => {\r\n console.error(`Failed to load video asset: ${url}`, err);\r\n reject(err);\r\n });\r\n\r\n this.assetsEl.appendChild(videoEl);\r\n });\r\n }\r\n\r\n /**\r\n * Get an asset by ID\r\n * @param {string} id - Asset ID\r\n * @returns {HTMLElement} - The asset element\r\n */\r\n getAsset(id) {\r\n return this.loadedAssets.get(id) || document.getElementById(id);\r\n }\r\n\r\n /**\r\n * Check if asset is a video based on URL\r\n * @param {string} url - Asset URL\r\n * @returns {boolean}\r\n */\r\n isVideo(url) {\r\n const videoExtensions = ['.mp4', '.webm', '.ogg', '.ogv'];\r\n return videoExtensions.some(ext => url.toLowerCase().endsWith(ext));\r\n }\r\n\r\n /**\r\n * Clean up all assets created by the manager\r\n */\r\n destroy() {\r\n this.loadedAssets.forEach((asset, id) => {\r\n if (asset && asset.parentNode) {\r\n asset.parentNode.removeChild(asset);\r\n }\r\n });\r\n this.loadedAssets.clear();\r\n }\r\n}\r\n","/**\r\n * SceneManager - Handles scene transitions and <a-sky> management\r\n */\r\nexport class SceneManager {\r\n constructor(sceneEl, assetManager) {\r\n this.sceneEl = sceneEl;\r\n this.assetManager = assetManager;\r\n this.skyEl = null;\r\n this.currentSceneId = null;\r\n this.transitionDuration = 500; // milliseconds\r\n this.init();\r\n }\r\n\r\n init() {\r\n // Find or create <a-sky> element\r\n this.skyEl = this.sceneEl.querySelector('a-sky');\r\n if (!this.skyEl) {\r\n this.skyEl = document.createElement('a-sky');\r\n this.skyEl.setAttribute('id', 'swt-sky');\r\n this.sceneEl.appendChild(this.skyEl);\r\n }\r\n }\r\n\r\n /**\r\n * Load a scene by its ID\r\n * @param {string} sceneId - The scene ID\r\n * @param {Object} sceneData - The scene configuration object\r\n * @returns {Promise} - Resolves when the scene is loaded\r\n */\r\n async loadScene(sceneId, sceneData) {\r\n this.currentSceneId = sceneId;\r\n\r\n // Preload the panorama\r\n const panoramaUrl = sceneData.panorama;\r\n const assetId = `scene-${sceneId}`;\r\n \r\n const isVideo = this.assetManager.isVideo(panoramaUrl);\r\n \r\n if (isVideo) {\r\n await this.assetManager.preloadVideo(panoramaUrl, assetId);\r\n } else {\r\n await this.assetManager.preloadImage(panoramaUrl, assetId);\r\n }\r\n\r\n // Set the sky source\r\n this.skyEl.setAttribute('src', `#${assetId}`);\r\n\r\n // If it's a video, play it\r\n if (isVideo) {\r\n const videoEl = this.assetManager.getAsset(assetId);\r\n if (videoEl && videoEl.play) {\r\n videoEl.play().catch(err => {\r\n console.warn('Video autoplay failed:', err);\r\n });\r\n }\r\n }\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n /**\r\n * Transition to a new scene with fade effect\r\n * @param {string} sceneId - The target scene ID\r\n * @param {Object} sceneData - The scene configuration object\r\n * @returns {Promise} - Resolves when the transition is complete\r\n */\r\n async transitionTo(sceneId, sceneData) {\r\n // Fade out\r\n await this.fadeOut();\r\n\r\n // Load new scene\r\n await this.loadScene(sceneId, sceneData);\r\n\r\n // Fade in\r\n await this.fadeIn();\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n /**\r\n * Fade out the scene\r\n * @returns {Promise}\r\n */\r\n fadeOut() {\r\n return new Promise((resolve) => {\r\n // Create a fade overlay\r\n const fadeEl = document.createElement('a-entity');\r\n fadeEl.setAttribute('id', 'swt-fade');\r\n fadeEl.setAttribute('geometry', 'primitive: sphere; radius: 1');\r\n fadeEl.setAttribute('material', 'color: black; opacity: 0; shader: flat; side: back');\r\n fadeEl.setAttribute('scale', '0.1 0.1 0.1');\r\n \r\n // Position it at the camera\r\n const cameraEl = this.sceneEl.querySelector('[camera]');\r\n if (cameraEl && cameraEl.tagName) {\r\n fadeEl.setAttribute('position', '0 0 0');\r\n cameraEl.appendChild(fadeEl);\r\n } else {\r\n // Fallback: append to scene\r\n fadeEl.setAttribute('position', '0 1.6 0');\r\n this.sceneEl.appendChild(fadeEl);\r\n }\r\n\r\n // Animate opacity\r\n fadeEl.setAttribute('animation', {\r\n property: 'material.opacity',\r\n to: 1,\r\n dur: this.transitionDuration,\r\n easing: 'easeInQuad'\r\n });\r\n\r\n setTimeout(() => {\r\n resolve(fadeEl);\r\n }, this.transitionDuration);\r\n });\r\n }\r\n\r\n /**\r\n * Fade in the scene\r\n * @returns {Promise}\r\n */\r\n fadeIn() {\r\n return new Promise((resolve) => {\r\n const fadeEl = document.getElementById('swt-fade');\r\n if (fadeEl) {\r\n fadeEl.setAttribute('animation', {\r\n property: 'material.opacity',\r\n to: 0,\r\n dur: this.transitionDuration,\r\n easing: 'easeOutQuad'\r\n });\r\n\r\n setTimeout(() => {\r\n if (fadeEl.parentNode) {\r\n fadeEl.parentNode.removeChild(fadeEl);\r\n }\r\n resolve();\r\n }, this.transitionDuration);\r\n } else {\r\n resolve();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Get the current scene ID\r\n * @returns {string}\r\n */\r\n getCurrentSceneId() {\r\n return this.currentSceneId;\r\n }\r\n\r\n /**\r\n * Clean up the scene manager\r\n */\r\n destroy() {\r\n if (this.skyEl && this.skyEl.parentNode) {\r\n this.skyEl.setAttribute('src', '');\r\n }\r\n this.currentSceneId = null;\r\n }\r\n}\r\n","/**\r\n * HotspotManager - Creates, manages, and removes hotspot entities in the A-Frame scene\r\n */\r\nexport class HotspotManager {\r\n constructor(sceneEl, assetManager, defaultHotspotSettings = {}) {\r\n this.sceneEl = sceneEl;\r\n this.assetManager = assetManager;\r\n this.defaultSettings = defaultHotspotSettings;\r\n this.activeHotspots = [];\r\n this.tooltipEl = null;\r\n this.tooltipCreated = false;\r\n \r\n // Listen for hover events\r\n this.sceneEl.addEventListener('swt-hotspot-hover', (evt) => {\r\n this.handleHotspotHover(evt.detail);\r\n });\r\n }\r\n\r\n /**\r\n * Create a tooltip element for displaying hotspot information\r\n */\r\n createTooltip() {\r\n if (this.tooltipCreated) {\r\n return;\r\n }\r\n\r\n this.tooltipEl = document.createElement('a-entity');\r\n this.tooltipEl.setAttribute('id', 'swt-tooltip');\r\n this.tooltipEl.setAttribute('visible', 'false');\r\n \r\n // Text element\r\n const textEl = document.createElement('a-text');\r\n textEl.setAttribute('value', '');\r\n textEl.setAttribute('align', 'center');\r\n textEl.setAttribute('color', '#FFFFFF');\r\n textEl.setAttribute('width', '2');\r\n textEl.setAttribute('wrap-count', '20');\r\n this.tooltipEl.appendChild(textEl);\r\n \r\n // Background plane\r\n const bgEl = document.createElement('a-plane');\r\n bgEl.setAttribute('color', '#000000');\r\n bgEl.setAttribute('opacity', '0.7');\r\n bgEl.setAttribute('width', '2');\r\n bgEl.setAttribute('height', '0.5');\r\n bgEl.setAttribute('position', '0 0 -0.01');\r\n this.tooltipEl.appendChild(bgEl);\r\n \r\n this.sceneEl.appendChild(this.tooltipEl);\r\n this.tooltipCreated = true;\r\n }\r\n\r\n /**\r\n * Handle hotspot hover events to show/hide tooltip\r\n */\r\n handleHotspotHover(detail) {\r\n if (detail.isHovering && detail.hotspotData.tooltip) {\r\n const text = detail.hotspotData.tooltip.text;\r\n const position = detail.hotspotData.position;\r\n \r\n // Update tooltip text\r\n const textEl = this.tooltipEl.querySelector('a-text');\r\n textEl.setAttribute('value', text);\r\n \r\n // Position tooltip above the hotspot\r\n const offsetY = 1.5;\r\n this.tooltipEl.setAttribute('position', `${position.x} ${position.y + offsetY} ${position.z}`);\r\n this.tooltipEl.setAttribute('visible', 'true');\r\n \r\n // Make tooltip face the camera (using billboard component)\r\n if (!this.tooltipEl.hasAttribute('swt-billboard')) {\r\n this.tooltipEl.setAttribute('swt-billboard', 'enabled: true');\r\n }\r\n } else {\r\n this.tooltipEl.setAttribute('visible', 'false');\r\n }\r\n }\r\n\r\n /**\r\n * Create hotspots for a scene\r\n * @param {Array} hotspots - Array of hotspot objects\r\n * @returns {Promise} - Resolves when all hotspots are created\r\n */\r\n async createHotspots(hotspots = []) {\r\n // Create tooltip if not already created\r\n if (!this.tooltipCreated) {\r\n this.createTooltip();\r\n }\r\n\r\n // First, preload all hotspot icons (with error handling)\r\n const iconPromises = hotspots.map((hotspot, index) => {\r\n const icon = hotspot.appearance?.icon || this.defaultSettings.icon;\r\n if (icon) {\r\n const assetId = `hotspot-icon-${index}`;\r\n return this.assetManager.preloadImage(icon, assetId).catch(err => {\r\n console.warn(`Failed to load icon for hotspot ${index}, will use color instead`);\r\n return null; // Continue even if icon fails to load\r\n });\r\n }\r\n return Promise.resolve();\r\n });\r\n\r\n await Promise.all(iconPromises);\r\n\r\n // Then create the hotspot entities\r\n hotspots.forEach((hotspot, index) => {\r\n this.createHotspot(hotspot, index);\r\n });\r\n }\r\n\r\n /**\r\n * Create a single hotspot entity\r\n * @param {Object} hotspot - Hotspot configuration\r\n * @param {number} index - Hotspot index\r\n */\r\n createHotspot(hotspot, index) {\r\n const hotspotEl = document.createElement('a-entity');\r\n hotspotEl.classList.add('swt-hotspot');\r\n \r\n // Set position\r\n const pos = hotspot.position;\r\n hotspotEl.setAttribute('position', `${pos.x} ${pos.y} ${pos.z}`);\r\n\r\n // Set icon or fallback to plane with color\r\n const icon = hotspot.appearance?.icon || this.defaultSettings.icon;\r\n const assetId = `hotspot-icon-${index}`;\r\n const assetEl = icon ? document.getElementById(assetId) : null;\r\n \r\n let visualEl;\r\n \r\n // Check if icon was successfully loaded\r\n if (icon && assetEl) {\r\n visualEl = document.createElement('a-image');\r\n visualEl.setAttribute('src', `#${assetId}`);\r\n // Make images double-sided\r\n visualEl.setAttribute('material', 'side', 'double');\r\n } else {\r\n // Fallback to a plane with color\r\n visualEl = document.createElement('a-plane');\r\n visualEl.setAttribute('color', hotspot.appearance?.color || '#4CC3D9');\r\n visualEl.setAttribute('width', '1');\r\n visualEl.setAttribute('height', '1');\r\n // Make plane double-sided and always face camera\r\n visualEl.setAttribute('material', 'side', 'double');\r\n }\r\n\r\n // Set scale\r\n const scale = hotspot.appearance?.scale || this.defaultSettings.scale || '1 1 1';\r\n visualEl.setAttribute('scale', scale);\r\n\r\n // Make it always face the camera (billboard effect)\r\n visualEl.setAttribute('swt-billboard', 'enabled: true');\r\n\r\n // Add cursor interaction\r\n visualEl.setAttribute('class', 'clickable');\r\n\r\n // Add the swt-hotspot-listener component\r\n visualEl.setAttribute('swt-hotspot-listener', {\r\n hotspotData: JSON.stringify(hotspot)\r\n });\r\n\r\n hotspotEl.appendChild(visualEl);\r\n this.sceneEl.appendChild(hotspotEl);\r\n this.activeHotspots.push(hotspotEl);\r\n }\r\n\r\n /**\r\n * Remove all active hotspots from the scene\r\n */\r\n removeAllHotspots() {\r\n this.activeHotspots.forEach(hotspot => {\r\n if (hotspot.parentNode) {\r\n hotspot.parentNode.removeChild(hotspot);\r\n }\r\n });\r\n this.activeHotspots = [];\r\n \r\n // Hide tooltip\r\n if (this.tooltipEl) {\r\n this.tooltipEl.setAttribute('visible', 'false');\r\n }\r\n }\r\n\r\n /**\r\n * Clean up the hotspot manager\r\n */\r\n destroy() {\r\n this.removeAllHotspots();\r\n if (this.tooltipEl && this.tooltipEl.parentNode) {\r\n this.tooltipEl.parentNode.removeChild(this.tooltipEl);\r\n }\r\n }\r\n}\r\n","/**\r\n * SenangWebs Tour (SWT) - Main Library Entry Point\r\n * Version 1.0.0\r\n */\r\n\r\nimport './components/hotspot-listener.js';\r\nimport { AssetManager } from './AssetManager.js';\r\nimport { SceneManager } from './SceneManager.js';\r\nimport { HotspotManager } from './HotspotManager.js';\r\n\r\n/**\r\n * Main Tour class - The public API for the SWT library\r\n */\r\nclass Tour {\r\n constructor(aframeSceneEl, tourConfig) {\r\n if (!aframeSceneEl) {\r\n throw new Error('SWT.Tour requires an A-Frame scene element');\r\n }\r\n\r\n if (!tourConfig || !tourConfig.scenes || !tourConfig.initialScene) {\r\n throw new Error('SWT.Tour requires a valid tour configuration with scenes and initialScene');\r\n }\r\n\r\n this.sceneEl = aframeSceneEl;\r\n this.config = tourConfig;\r\n this.isStarted = false;\r\n\r\n // Initialize managers\r\n this.assetManager = new AssetManager(this.sceneEl);\r\n this.sceneManager = new SceneManager(this.sceneEl, this.assetManager);\r\n \r\n const defaultHotspotSettings = this.config.settings?.defaultHotspot || {};\r\n this.hotspotManager = new HotspotManager(\r\n this.sceneEl, \r\n this.assetManager, \r\n defaultHotspotSettings\r\n );\r\n\r\n // Event listeners\r\n this.boundHandleHotspotClick = this.handleHotspotClick.bind(this);\r\n this.sceneEl.addEventListener('swt-hotspot-clicked', this.boundHandleHotspotClick);\r\n\r\n // Ensure cursor exists for interaction\r\n this.ensureCursor();\r\n }\r\n\r\n /**\r\n * Ensure the scene has a cursor for interaction\r\n */\r\n ensureCursor() {\r\n const camera = this.sceneEl.querySelector('[camera]');\r\n if (camera) {\r\n let cursor = camera.querySelector('[cursor]');\r\n if (!cursor) {\r\n cursor = document.createElement('a-cursor');\r\n cursor.setAttribute('fuse', 'true');\r\n cursor.setAttribute('fuse-timeout', '1500');\r\n camera.appendChild(cursor);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Start the tour - Load the initial scene\r\n * @returns {Promise}\r\n */\r\n async start() {\r\n if (this.isStarted) {\r\n console.warn('Tour has already been started');\r\n return Promise.resolve();\r\n }\r\n\r\n const initialSceneId = this.config.initialScene;\r\n const initialSceneData = this.config.scenes[initialSceneId];\r\n\r\n if (!initialSceneData) {\r\n throw new Error(`Initial scene \"${initialSceneId}\" not found in tour configuration`);\r\n }\r\n\r\n try {\r\n // Emit scene-loading event\r\n this.emit('scene-loading', { sceneId: initialSceneId });\r\n\r\n // Load the scene\r\n await this.sceneManager.loadScene(initialSceneId, initialSceneData);\r\n\r\n // Create hotspots\r\n await this.hotspotManager.createHotspots(initialSceneData.hotspots || []);\r\n\r\n this.isStarted = true;\r\n\r\n // Emit events\r\n this.emit('scene-loaded', { sceneId: initialSceneId });\r\n this.emit('tour-started', { sceneId: initialSceneId });\r\n\r\n return Promise.resolve();\r\n } catch (error) {\r\n console.error('Failed to start tour:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Navigate to a specific scene\r\n * @param {string} sceneId - The ID of the scene to navigate to\r\n * @returns {Promise}\r\n */\r\n async navigateTo(sceneId) {\r\n const sceneData = this.config.scenes[sceneId];\r\n\r\n if (!sceneData) {\r\n throw new Error(`Scene \"${sceneId}\" not found in tour configuration`);\r\n }\r\n\r\n if (this.sceneManager.getCurrentSceneId() === sceneId) {\r\n console.warn(`Already at scene \"${sceneId}\"`);\r\n return Promise.resolve();\r\n }\r\n\r\n try {\r\n // Emit scene-loading event\r\n this.emit('scene-loading', { sceneId: sceneId });\r\n\r\n // Remove old hotspots\r\n this.hotspotManager.removeAllHotspots();\r\n\r\n // Transition to new scene\r\n await this.sceneManager.transitionTo(sceneId, sceneData);\r\n\r\n // Create new hotspots\r\n await this.hotspotManager.createHotspots(sceneData.hotspots || []);\r\n\r\n // Emit scene-loaded event\r\n this.emit('scene-loaded', { sceneId: sceneId });\r\n\r\n return Promise.resolve();\r\n } catch (error) {\r\n console.error(`Failed to navigate to scene \"${sceneId}\":`, error);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Handle hotspot click events\r\n * @param {CustomEvent} evt - The hotspot click event\r\n */\r\n handleHotspotClick(evt) {\r\n const { hotspotData } = evt.detail;\r\n const currentSceneId = this.sceneManager.getCurrentSceneId();\r\n\r\n // Emit hotspot-activated event\r\n this.emit('hotspot-activated', {\r\n hotspotData: hotspotData,\r\n sceneId: currentSceneId\r\n });\r\n\r\n // Handle the action\r\n if (hotspotData.action) {\r\n this.handleAction(hotspotData.action);\r\n }\r\n }\r\n\r\n /**\r\n * Handle a hotspot action\r\n * @param {Object} action - The action object\r\n */\r\n handleAction(action) {\r\n switch (action.type) {\r\n case 'navigateTo':\r\n if (action.target) {\r\n this.navigateTo(action.target).catch(err => {\r\n console.error('Navigation failed:', err);\r\n });\r\n }\r\n break;\r\n \r\n default:\r\n console.warn(`Unknown action type: ${action.type}`);\r\n }\r\n }\r\n\r\n /**\r\n * Get the current scene ID\r\n * @returns {string}\r\n */\r\n getCurrentSceneId() {\r\n return this.sceneManager.getCurrentSceneId();\r\n }\r\n\r\n /**\r\n * Emit a custom event\r\n * @param {string} eventName - The event name\r\n * @param {Object} detail - Event detail object\r\n */\r\n emit(eventName, detail = {}) {\r\n const event = new CustomEvent(eventName, {\r\n detail: detail,\r\n bubbles: true,\r\n cancelable: true\r\n });\r\n this.sceneEl.dispatchEvent(event);\r\n }\r\n\r\n /**\r\n * Add an event listener\r\n * @param {string} eventName - The event name\r\n * @param {Function} handler - The event handler\r\n */\r\n addEventListener(eventName, handler) {\r\n this.sceneEl.addEventListener(eventName, handler);\r\n }\r\n\r\n /**\r\n * Remove an event listener\r\n * @param {string} eventName - The event name\r\n * @param {Function} handler - The event handler\r\n */\r\n removeEventListener(eventName, handler) {\r\n this.sceneEl.removeEventListener(eventName, handler);\r\n }\r\n\r\n /**\r\n * Destroy the tour and clean up resources\r\n */\r\n destroy() {\r\n // Remove event listeners\r\n this.sceneEl.removeEventListener('swt-hotspot-clicked', this.boundHandleHotspotClick);\r\n\r\n // Clean up managers\r\n this.hotspotManager.destroy();\r\n this.sceneManager.destroy();\r\n this.assetManager.destroy();\r\n\r\n this.isStarted = false;\r\n }\r\n}\r\n\r\n// Export the Tour class as the main API\r\nexport { Tour };\r\n\r\n// Also attach to window for UMD usage\r\nif (typeof window !== 'undefined') {\r\n window.SWT = window.SWT || {};\r\n window.SWT.Tour = Tour;\r\n}\r\n"],"names":[],"mappings":"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"}
|
package/dist/swt.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).SWT={})}(this,function(t){"use strict";"undefined"!=typeof AFRAME&&(AFRAME.registerComponent("swt-hotspot-listener",{schema:{hotspotData:{type:"string",default:"{}"}},init:function(){this.onClick=this.onClick.bind(this),this.onMouseEnter=this.onMouseEnter.bind(this),this.onMouseLeave=this.onMouseLeave.bind(this)},play:function(){this.el.addEventListener("click",this.onClick),this.el.addEventListener("mouseenter",this.onMouseEnter),this.el.addEventListener("mouseleave",this.onMouseLeave)},pause:function(){this.el.removeEventListener("click",this.onClick),this.el.removeEventListener("mouseenter",this.onMouseEnter),this.el.removeEventListener("mouseleave",this.onMouseLeave)},onClick:function(t){const e=JSON.parse(this.data.hotspotData);this.el.sceneEl.emit("swt-hotspot-clicked",{hotspotData:e})},onMouseEnter:function(t){const e=JSON.parse(this.data.hotspotData);e.tooltip&&this.el.sceneEl.emit("swt-hotspot-hover",{hotspotData:e,isHovering:!0}),this.el.setAttribute("scale",this.getScaledSize(1.2))},onMouseLeave:function(t){const e=JSON.parse(this.data.hotspotData);e.tooltip&&this.el.sceneEl.emit("swt-hotspot-hover",{hotspotData:e,isHovering:!1}),this.el.setAttribute("scale",this.getOriginalScale())},getOriginalScale:function(){const t=JSON.parse(this.data.hotspotData);return t.appearance?.scale||"1 1 1"},getScaledSize:function(t){const e=this.getOriginalScale().split(" ").map(Number);return`${e[0]*t} ${e[1]*t} ${e[2]*t}`}}),AFRAME.registerComponent("swt-billboard",{schema:{enabled:{type:"boolean",default:!0}},init:function(){this.camera=null},tick:function(){if(!this.data.enabled)return;if(!this.camera&&(this.camera=this.el.sceneEl.camera,!this.camera))return;const t=new THREE.Vector3;this.camera.getWorldPosition(t);const e=new THREE.Vector3;this.el.object3D.getWorldPosition(e),this.el.object3D.lookAt(t)}}));class e{constructor(t){this.sceneEl=t,this.assetsEl=null,this.loadedAssets=new Map,this.init()}init(){this.assetsEl=this.sceneEl.querySelector("a-assets"),this.assetsEl||(this.assetsEl=document.createElement("a-assets"),this.sceneEl.hasLoaded?this.sceneEl.insertBefore(this.assetsEl,this.sceneEl.firstChild):this.sceneEl.addEventListener("loaded",()=>{this.sceneEl.insertBefore(this.assetsEl,this.sceneEl.firstChild)},{once:!0}))}ensureAssetsReady(){return new Promise(t=>{this.assetsEl&&this.assetsEl.parentNode?t():this.sceneEl.hasLoaded?(this.assetsEl&&!this.assetsEl.parentNode&&this.sceneEl.insertBefore(this.assetsEl,this.sceneEl.firstChild),t()):this.sceneEl.addEventListener("loaded",()=>{this.assetsEl&&!this.assetsEl.parentNode&&this.sceneEl.insertBefore(this.assetsEl,this.sceneEl.firstChild),t()},{once:!0})})}async preloadImage(t,e){return this.loadedAssets.has(e)?Promise.resolve(this.loadedAssets.get(e)):(await this.ensureAssetsReady(),new Promise((s,i)=>{const o=document.createElement("img");o.setAttribute("id",e),o.setAttribute("src",t),o.setAttribute("crossorigin","anonymous"),o.addEventListener("load",()=>{this.loadedAssets.set(e,o),s(o)}),o.addEventListener("error",e=>{console.error(`Failed to load asset: ${t}`,e),i(e)}),this.assetsEl.appendChild(o)}))}async preloadVideo(t,e){return this.loadedAssets.has(e)?Promise.resolve(this.loadedAssets.get(e)):(await this.ensureAssetsReady(),new Promise((s,i)=>{const o=document.createElement("video");o.setAttribute("id",e),o.setAttribute("src",t),o.setAttribute("crossorigin","anonymous"),o.setAttribute("preload","auto"),o.setAttribute("playsinline",""),o.setAttribute("loop",""),o.addEventListener("loadeddata",()=>{this.loadedAssets.set(e,o),s(o)}),o.addEventListener("error",e=>{console.error(`Failed to load video asset: ${t}`,e),i(e)}),this.assetsEl.appendChild(o)}))}getAsset(t){return this.loadedAssets.get(t)||document.getElementById(t)}isVideo(t){return[".mp4",".webm",".ogg",".ogv"].some(e=>t.toLowerCase().endsWith(e))}destroy(){this.loadedAssets.forEach((t,e)=>{t&&t.parentNode&&t.parentNode.removeChild(t)}),this.loadedAssets.clear()}}class s{constructor(t,e){this.sceneEl=t,this.assetManager=e,this.skyEl=null,this.currentSceneId=null,this.transitionDuration=500,this.init()}init(){this.skyEl=this.sceneEl.querySelector("a-sky"),this.skyEl||(this.skyEl=document.createElement("a-sky"),this.skyEl.setAttribute("id","swt-sky"),this.sceneEl.appendChild(this.skyEl))}async loadScene(t,e){this.currentSceneId=t;const s=e.panorama,i=`scene-${t}`,o=this.assetManager.isVideo(s);if(o?await this.assetManager.preloadVideo(s,i):await this.assetManager.preloadImage(s,i),this.skyEl.setAttribute("src",`#${i}`),o){const t=this.assetManager.getAsset(i);t&&t.play&&t.play().catch(t=>{console.warn("Video autoplay failed:",t)})}return Promise.resolve()}async transitionTo(t,e){return await this.fadeOut(),await this.loadScene(t,e),await this.fadeIn(),Promise.resolve()}fadeOut(){return new Promise(t=>{const e=document.createElement("a-entity");e.setAttribute("id","swt-fade"),e.setAttribute("geometry","primitive: sphere; radius: 1"),e.setAttribute("material","color: black; opacity: 0; shader: flat; side: back"),e.setAttribute("scale","0.1 0.1 0.1");const s=this.sceneEl.querySelector("[camera]");s&&s.tagName?(e.setAttribute("position","0 0 0"),s.appendChild(e)):(e.setAttribute("position","0 1.6 0"),this.sceneEl.appendChild(e)),e.setAttribute("animation",{property:"material.opacity",to:1,dur:this.transitionDuration,easing:"easeInQuad"}),setTimeout(()=>{t(e)},this.transitionDuration)})}fadeIn(){return new Promise(t=>{const e=document.getElementById("swt-fade");e?(e.setAttribute("animation",{property:"material.opacity",to:0,dur:this.transitionDuration,easing:"easeOutQuad"}),setTimeout(()=>{e.parentNode&&e.parentNode.removeChild(e),t()},this.transitionDuration)):t()})}getCurrentSceneId(){return this.currentSceneId}destroy(){this.skyEl&&this.skyEl.parentNode&&this.skyEl.setAttribute("src",""),this.currentSceneId=null}}class i{constructor(t,e,s={}){this.sceneEl=t,this.assetManager=e,this.defaultSettings=s,this.activeHotspots=[],this.tooltipEl=null,this.tooltipCreated=!1,this.sceneEl.addEventListener("swt-hotspot-hover",t=>{this.handleHotspotHover(t.detail)})}createTooltip(){if(this.tooltipCreated)return;this.tooltipEl=document.createElement("a-entity"),this.tooltipEl.setAttribute("id","swt-tooltip"),this.tooltipEl.setAttribute("visible","false");const t=document.createElement("a-text");t.setAttribute("value",""),t.setAttribute("align","center"),t.setAttribute("color","#FFFFFF"),t.setAttribute("width","2"),t.setAttribute("wrap-count","20"),this.tooltipEl.appendChild(t);const e=document.createElement("a-plane");e.setAttribute("color","#000000"),e.setAttribute("opacity","0.7"),e.setAttribute("width","2"),e.setAttribute("height","0.5"),e.setAttribute("position","0 0 -0.01"),this.tooltipEl.appendChild(e),this.sceneEl.appendChild(this.tooltipEl),this.tooltipCreated=!0}handleHotspotHover(t){if(t.isHovering&&t.hotspotData.tooltip){const e=t.hotspotData.tooltip.text,s=t.hotspotData.position;this.tooltipEl.querySelector("a-text").setAttribute("value",e);const i=1.5;this.tooltipEl.setAttribute("position",`${s.x} ${s.y+i} ${s.z}`),this.tooltipEl.setAttribute("visible","true"),this.tooltipEl.hasAttribute("swt-billboard")||this.tooltipEl.setAttribute("swt-billboard","enabled: true")}else this.tooltipEl.setAttribute("visible","false")}async createHotspots(t=[]){this.tooltipCreated||this.createTooltip();const e=t.map((t,e)=>{const s=t.appearance?.icon||this.defaultSettings.icon;if(s){const t=`hotspot-icon-${e}`;return this.assetManager.preloadImage(s,t).catch(t=>(console.warn(`Failed to load icon for hotspot ${e}, will use color instead`),null))}return Promise.resolve()});await Promise.all(e),t.forEach((t,e)=>{this.createHotspot(t,e)})}createHotspot(t,e){const s=document.createElement("a-entity");s.classList.add("swt-hotspot");const i=t.position;s.setAttribute("position",`${i.x} ${i.y} ${i.z}`);const o=t.appearance?.icon||this.defaultSettings.icon,n=`hotspot-icon-${e}`,a=o?document.getElementById(n):null;let r;o&&a?(r=document.createElement("a-image"),r.setAttribute("src",`#${n}`),r.setAttribute("material","side","double")):(r=document.createElement("a-plane"),r.setAttribute("color",t.appearance?.color||"#4CC3D9"),r.setAttribute("width","1"),r.setAttribute("height","1"),r.setAttribute("material","side","double"));const l=t.appearance?.scale||this.defaultSettings.scale||"1 1 1";r.setAttribute("scale",l),r.setAttribute("swt-billboard","enabled: true"),r.setAttribute("class","clickable"),r.setAttribute("swt-hotspot-listener",{hotspotData:JSON.stringify(t)}),s.appendChild(r),this.sceneEl.appendChild(s),this.activeHotspots.push(s)}removeAllHotspots(){this.activeHotspots.forEach(t=>{t.parentNode&&t.parentNode.removeChild(t)}),this.activeHotspots=[],this.tooltipEl&&this.tooltipEl.setAttribute("visible","false")}destroy(){this.removeAllHotspots(),this.tooltipEl&&this.tooltipEl.parentNode&&this.tooltipEl.parentNode.removeChild(this.tooltipEl)}}class o{constructor(t,o){if(!t)throw new Error("SWT.Tour requires an A-Frame scene element");if(!o||!o.scenes||!o.initialScene)throw new Error("SWT.Tour requires a valid tour configuration with scenes and initialScene");this.sceneEl=t,this.config=o,this.isStarted=!1,this.assetManager=new e(this.sceneEl),this.sceneManager=new s(this.sceneEl,this.assetManager);const n=this.config.settings?.defaultHotspot||{};this.hotspotManager=new i(this.sceneEl,this.assetManager,n),this.boundHandleHotspotClick=this.handleHotspotClick.bind(this),this.sceneEl.addEventListener("swt-hotspot-clicked",this.boundHandleHotspotClick),this.ensureCursor()}ensureCursor(){const t=this.sceneEl.querySelector("[camera]");if(t){let e=t.querySelector("[cursor]");e||(e=document.createElement("a-cursor"),e.setAttribute("fuse","true"),e.setAttribute("fuse-timeout","1500"),t.appendChild(e))}}async start(){if(this.isStarted)return console.warn("Tour has already been started"),Promise.resolve();const t=this.config.initialScene,e=this.config.scenes[t];if(!e)throw new Error(`Initial scene "${t}" not found in tour configuration`);try{return this.emit("scene-loading",{sceneId:t}),await this.sceneManager.loadScene(t,e),await this.hotspotManager.createHotspots(e.hotspots||[]),this.isStarted=!0,this.emit("scene-loaded",{sceneId:t}),this.emit("tour-started",{sceneId:t}),Promise.resolve()}catch(t){throw console.error("Failed to start tour:",t),t}}async navigateTo(t){const e=this.config.scenes[t];if(!e)throw new Error(`Scene "${t}" not found in tour configuration`);if(this.sceneManager.getCurrentSceneId()===t)return console.warn(`Already at scene "${t}"`),Promise.resolve();try{return this.emit("scene-loading",{sceneId:t}),this.hotspotManager.removeAllHotspots(),await this.sceneManager.transitionTo(t,e),await this.hotspotManager.createHotspots(e.hotspots||[]),this.emit("scene-loaded",{sceneId:t}),Promise.resolve()}catch(e){throw console.error(`Failed to navigate to scene "${t}":`,e),e}}handleHotspotClick(t){const{hotspotData:e}=t.detail,s=this.sceneManager.getCurrentSceneId();this.emit("hotspot-activated",{hotspotData:e,sceneId:s}),e.action&&this.handleAction(e.action)}handleAction(t){if("navigateTo"===t.type)t.target&&this.navigateTo(t.target).catch(t=>{console.error("Navigation failed:",t)});else console.warn(`Unknown action type: ${t.type}`)}getCurrentSceneId(){return this.sceneManager.getCurrentSceneId()}emit(t,e={}){const s=new CustomEvent(t,{detail:e,bubbles:!0,cancelable:!0});this.sceneEl.dispatchEvent(s)}addEventListener(t,e){this.sceneEl.addEventListener(t,e)}removeEventListener(t,e){this.sceneEl.removeEventListener(t,e)}destroy(){this.sceneEl.removeEventListener("swt-hotspot-clicked",this.boundHandleHotspotClick),this.hotspotManager.destroy(),this.sceneManager.destroy(),this.assetManager.destroy(),this.isStarted=!1}}"undefined"!=typeof window&&(window.SWT=window.SWT||{},window.SWT.Tour=o),t.Tour=o});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "senangwebs-tour",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "VR 360° virtual tour system with visual editor and viewer library",
|
|
6
|
+
"main": "dist/swt.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"src/"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rollup -c",
|
|
13
|
+
"dev": "rollup -c -w",
|
|
14
|
+
"serve": "http-server -p 8080"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"aframe",
|
|
18
|
+
"virtual-tour",
|
|
19
|
+
"360",
|
|
20
|
+
"panorama",
|
|
21
|
+
"vr",
|
|
22
|
+
"editor"
|
|
23
|
+
],
|
|
24
|
+
"author": "SenangWebs",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
28
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
29
|
+
"@rollup/plugin-virtual": "^3.0.2",
|
|
30
|
+
"concat-with-sourcemaps": "^1.1.0",
|
|
31
|
+
"postcss-import": "^16.1.1",
|
|
32
|
+
"rollup": "^4.9.6",
|
|
33
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
34
|
+
"terser": "^5.44.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"aframe": "^1.4.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetManager - Handles preloading and management of assets in A-Frame's <a-assets>
|
|
3
|
+
*/
|
|
4
|
+
export class AssetManager {
|
|
5
|
+
constructor(sceneEl) {
|
|
6
|
+
this.sceneEl = sceneEl;
|
|
7
|
+
this.assetsEl = null;
|
|
8
|
+
this.loadedAssets = new Map();
|
|
9
|
+
this.init();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
init() {
|
|
13
|
+
// Find or create <a-assets> element
|
|
14
|
+
this.assetsEl = this.sceneEl.querySelector('a-assets');
|
|
15
|
+
if (!this.assetsEl) {
|
|
16
|
+
this.assetsEl = document.createElement('a-assets');
|
|
17
|
+
// Wait for scene to be ready before inserting
|
|
18
|
+
if (this.sceneEl.hasLoaded) {
|
|
19
|
+
this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);
|
|
20
|
+
} else {
|
|
21
|
+
this.sceneEl.addEventListener('loaded', () => {
|
|
22
|
+
this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);
|
|
23
|
+
}, { once: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Ensure assets element is ready
|
|
30
|
+
* @returns {Promise}
|
|
31
|
+
*/
|
|
32
|
+
ensureAssetsReady() {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
if (this.assetsEl && this.assetsEl.parentNode) {
|
|
35
|
+
resolve();
|
|
36
|
+
} else if (this.sceneEl.hasLoaded) {
|
|
37
|
+
if (this.assetsEl && !this.assetsEl.parentNode) {
|
|
38
|
+
this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);
|
|
39
|
+
}
|
|
40
|
+
resolve();
|
|
41
|
+
} else {
|
|
42
|
+
this.sceneEl.addEventListener('loaded', () => {
|
|
43
|
+
if (this.assetsEl && !this.assetsEl.parentNode) {
|
|
44
|
+
this.sceneEl.insertBefore(this.assetsEl, this.sceneEl.firstChild);
|
|
45
|
+
}
|
|
46
|
+
resolve();
|
|
47
|
+
}, { once: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Preload an image asset
|
|
54
|
+
* @param {string} url - URL of the image
|
|
55
|
+
* @param {string} id - Unique ID for the asset
|
|
56
|
+
* @returns {Promise} - Resolves when the asset is loaded
|
|
57
|
+
*/
|
|
58
|
+
async preloadImage(url, id) {
|
|
59
|
+
if (this.loadedAssets.has(id)) {
|
|
60
|
+
return Promise.resolve(this.loadedAssets.get(id));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Ensure assets element is ready
|
|
64
|
+
await this.ensureAssetsReady();
|
|
65
|
+
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const imgEl = document.createElement('img');
|
|
68
|
+
imgEl.setAttribute('id', id);
|
|
69
|
+
imgEl.setAttribute('src', url);
|
|
70
|
+
imgEl.setAttribute('crossorigin', 'anonymous');
|
|
71
|
+
|
|
72
|
+
imgEl.addEventListener('load', () => {
|
|
73
|
+
this.loadedAssets.set(id, imgEl);
|
|
74
|
+
resolve(imgEl);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
imgEl.addEventListener('error', (err) => {
|
|
78
|
+
console.error(`Failed to load asset: ${url}`, err);
|
|
79
|
+
reject(err);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.assetsEl.appendChild(imgEl);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Preload a video asset
|
|
88
|
+
* @param {string} url - URL of the video
|
|
89
|
+
* @param {string} id - Unique ID for the asset
|
|
90
|
+
* @returns {Promise} - Resolves when the asset is loaded
|
|
91
|
+
*/
|
|
92
|
+
async preloadVideo(url, id) {
|
|
93
|
+
if (this.loadedAssets.has(id)) {
|
|
94
|
+
return Promise.resolve(this.loadedAssets.get(id));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Ensure assets element is ready
|
|
98
|
+
await this.ensureAssetsReady();
|
|
99
|
+
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const videoEl = document.createElement('video');
|
|
102
|
+
videoEl.setAttribute('id', id);
|
|
103
|
+
videoEl.setAttribute('src', url);
|
|
104
|
+
videoEl.setAttribute('crossorigin', 'anonymous');
|
|
105
|
+
videoEl.setAttribute('preload', 'auto');
|
|
106
|
+
videoEl.setAttribute('playsinline', '');
|
|
107
|
+
videoEl.setAttribute('loop', '');
|
|
108
|
+
|
|
109
|
+
videoEl.addEventListener('loadeddata', () => {
|
|
110
|
+
this.loadedAssets.set(id, videoEl);
|
|
111
|
+
resolve(videoEl);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
videoEl.addEventListener('error', (err) => {
|
|
115
|
+
console.error(`Failed to load video asset: ${url}`, err);
|
|
116
|
+
reject(err);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
this.assetsEl.appendChild(videoEl);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get an asset by ID
|
|
125
|
+
* @param {string} id - Asset ID
|
|
126
|
+
* @returns {HTMLElement} - The asset element
|
|
127
|
+
*/
|
|
128
|
+
getAsset(id) {
|
|
129
|
+
return this.loadedAssets.get(id) || document.getElementById(id);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if asset is a video based on URL
|
|
134
|
+
* @param {string} url - Asset URL
|
|
135
|
+
* @returns {boolean}
|
|
136
|
+
*/
|
|
137
|
+
isVideo(url) {
|
|
138
|
+
const videoExtensions = ['.mp4', '.webm', '.ogg', '.ogv'];
|
|
139
|
+
return videoExtensions.some(ext => url.toLowerCase().endsWith(ext));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Clean up all assets created by the manager
|
|
144
|
+
*/
|
|
145
|
+
destroy() {
|
|
146
|
+
this.loadedAssets.forEach((asset, id) => {
|
|
147
|
+
if (asset && asset.parentNode) {
|
|
148
|
+
asset.parentNode.removeChild(asset);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
this.loadedAssets.clear();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HotspotManager - Creates, manages, and removes hotspot entities in the A-Frame scene
|
|
3
|
+
*/
|
|
4
|
+
export class HotspotManager {
|
|
5
|
+
constructor(sceneEl, assetManager, defaultHotspotSettings = {}) {
|
|
6
|
+
this.sceneEl = sceneEl;
|
|
7
|
+
this.assetManager = assetManager;
|
|
8
|
+
this.defaultSettings = defaultHotspotSettings;
|
|
9
|
+
this.activeHotspots = [];
|
|
10
|
+
this.tooltipEl = null;
|
|
11
|
+
this.tooltipCreated = false;
|
|
12
|
+
|
|
13
|
+
// Listen for hover events
|
|
14
|
+
this.sceneEl.addEventListener('swt-hotspot-hover', (evt) => {
|
|
15
|
+
this.handleHotspotHover(evt.detail);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a tooltip element for displaying hotspot information
|
|
21
|
+
*/
|
|
22
|
+
createTooltip() {
|
|
23
|
+
if (this.tooltipCreated) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.tooltipEl = document.createElement('a-entity');
|
|
28
|
+
this.tooltipEl.setAttribute('id', 'swt-tooltip');
|
|
29
|
+
this.tooltipEl.setAttribute('visible', 'false');
|
|
30
|
+
|
|
31
|
+
// Text element
|
|
32
|
+
const textEl = document.createElement('a-text');
|
|
33
|
+
textEl.setAttribute('value', '');
|
|
34
|
+
textEl.setAttribute('align', 'center');
|
|
35
|
+
textEl.setAttribute('color', '#FFFFFF');
|
|
36
|
+
textEl.setAttribute('width', '2');
|
|
37
|
+
textEl.setAttribute('wrap-count', '20');
|
|
38
|
+
this.tooltipEl.appendChild(textEl);
|
|
39
|
+
|
|
40
|
+
// Background plane
|
|
41
|
+
const bgEl = document.createElement('a-plane');
|
|
42
|
+
bgEl.setAttribute('color', '#000000');
|
|
43
|
+
bgEl.setAttribute('opacity', '0.7');
|
|
44
|
+
bgEl.setAttribute('width', '2');
|
|
45
|
+
bgEl.setAttribute('height', '0.5');
|
|
46
|
+
bgEl.setAttribute('position', '0 0 -0.01');
|
|
47
|
+
this.tooltipEl.appendChild(bgEl);
|
|
48
|
+
|
|
49
|
+
this.sceneEl.appendChild(this.tooltipEl);
|
|
50
|
+
this.tooltipCreated = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle hotspot hover events to show/hide tooltip
|
|
55
|
+
*/
|
|
56
|
+
handleHotspotHover(detail) {
|
|
57
|
+
if (detail.isHovering && detail.hotspotData.tooltip) {
|
|
58
|
+
const text = detail.hotspotData.tooltip.text;
|
|
59
|
+
const position = detail.hotspotData.position;
|
|
60
|
+
|
|
61
|
+
// Update tooltip text
|
|
62
|
+
const textEl = this.tooltipEl.querySelector('a-text');
|
|
63
|
+
textEl.setAttribute('value', text);
|
|
64
|
+
|
|
65
|
+
// Position tooltip above the hotspot
|
|
66
|
+
const offsetY = 1.5;
|
|
67
|
+
this.tooltipEl.setAttribute('position', `${position.x} ${position.y + offsetY} ${position.z}`);
|
|
68
|
+
this.tooltipEl.setAttribute('visible', 'true');
|
|
69
|
+
|
|
70
|
+
// Make tooltip face the camera (using billboard component)
|
|
71
|
+
if (!this.tooltipEl.hasAttribute('swt-billboard')) {
|
|
72
|
+
this.tooltipEl.setAttribute('swt-billboard', 'enabled: true');
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
this.tooltipEl.setAttribute('visible', 'false');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create hotspots for a scene
|
|
81
|
+
* @param {Array} hotspots - Array of hotspot objects
|
|
82
|
+
* @returns {Promise} - Resolves when all hotspots are created
|
|
83
|
+
*/
|
|
84
|
+
async createHotspots(hotspots = []) {
|
|
85
|
+
// Create tooltip if not already created
|
|
86
|
+
if (!this.tooltipCreated) {
|
|
87
|
+
this.createTooltip();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// First, preload all hotspot icons (with error handling)
|
|
91
|
+
const iconPromises = hotspots.map((hotspot, index) => {
|
|
92
|
+
const icon = hotspot.appearance?.icon || this.defaultSettings.icon;
|
|
93
|
+
if (icon) {
|
|
94
|
+
const assetId = `hotspot-icon-${index}`;
|
|
95
|
+
return this.assetManager.preloadImage(icon, assetId).catch(err => {
|
|
96
|
+
console.warn(`Failed to load icon for hotspot ${index}, will use color instead`);
|
|
97
|
+
return null; // Continue even if icon fails to load
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return Promise.resolve();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await Promise.all(iconPromises);
|
|
104
|
+
|
|
105
|
+
// Then create the hotspot entities
|
|
106
|
+
hotspots.forEach((hotspot, index) => {
|
|
107
|
+
this.createHotspot(hotspot, index);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create a single hotspot entity
|
|
113
|
+
* @param {Object} hotspot - Hotspot configuration
|
|
114
|
+
* @param {number} index - Hotspot index
|
|
115
|
+
*/
|
|
116
|
+
createHotspot(hotspot, index) {
|
|
117
|
+
const hotspotEl = document.createElement('a-entity');
|
|
118
|
+
hotspotEl.classList.add('swt-hotspot');
|
|
119
|
+
|
|
120
|
+
// Set position
|
|
121
|
+
const pos = hotspot.position;
|
|
122
|
+
hotspotEl.setAttribute('position', `${pos.x} ${pos.y} ${pos.z}`);
|
|
123
|
+
|
|
124
|
+
// Set icon or fallback to plane with color
|
|
125
|
+
const icon = hotspot.appearance?.icon || this.defaultSettings.icon;
|
|
126
|
+
const assetId = `hotspot-icon-${index}`;
|
|
127
|
+
const assetEl = icon ? document.getElementById(assetId) : null;
|
|
128
|
+
|
|
129
|
+
let visualEl;
|
|
130
|
+
|
|
131
|
+
// Check if icon was successfully loaded
|
|
132
|
+
if (icon && assetEl) {
|
|
133
|
+
visualEl = document.createElement('a-image');
|
|
134
|
+
visualEl.setAttribute('src', `#${assetId}`);
|
|
135
|
+
// Make images double-sided
|
|
136
|
+
visualEl.setAttribute('material', 'side', 'double');
|
|
137
|
+
} else {
|
|
138
|
+
// Fallback to a plane with color
|
|
139
|
+
visualEl = document.createElement('a-plane');
|
|
140
|
+
visualEl.setAttribute('color', hotspot.appearance?.color || '#4CC3D9');
|
|
141
|
+
visualEl.setAttribute('width', '1');
|
|
142
|
+
visualEl.setAttribute('height', '1');
|
|
143
|
+
// Make plane double-sided and always face camera
|
|
144
|
+
visualEl.setAttribute('material', 'side', 'double');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Set scale
|
|
148
|
+
const scale = hotspot.appearance?.scale || this.defaultSettings.scale || '1 1 1';
|
|
149
|
+
visualEl.setAttribute('scale', scale);
|
|
150
|
+
|
|
151
|
+
// Make it always face the camera (billboard effect)
|
|
152
|
+
visualEl.setAttribute('swt-billboard', 'enabled: true');
|
|
153
|
+
|
|
154
|
+
// Add cursor interaction
|
|
155
|
+
visualEl.setAttribute('class', 'clickable');
|
|
156
|
+
|
|
157
|
+
// Add the swt-hotspot-listener component
|
|
158
|
+
visualEl.setAttribute('swt-hotspot-listener', {
|
|
159
|
+
hotspotData: JSON.stringify(hotspot)
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
hotspotEl.appendChild(visualEl);
|
|
163
|
+
this.sceneEl.appendChild(hotspotEl);
|
|
164
|
+
this.activeHotspots.push(hotspotEl);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Remove all active hotspots from the scene
|
|
169
|
+
*/
|
|
170
|
+
removeAllHotspots() {
|
|
171
|
+
this.activeHotspots.forEach(hotspot => {
|
|
172
|
+
if (hotspot.parentNode) {
|
|
173
|
+
hotspot.parentNode.removeChild(hotspot);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
this.activeHotspots = [];
|
|
177
|
+
|
|
178
|
+
// Hide tooltip
|
|
179
|
+
if (this.tooltipEl) {
|
|
180
|
+
this.tooltipEl.setAttribute('visible', 'false');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Clean up the hotspot manager
|
|
186
|
+
*/
|
|
187
|
+
destroy() {
|
|
188
|
+
this.removeAllHotspots();
|
|
189
|
+
if (this.tooltipEl && this.tooltipEl.parentNode) {
|
|
190
|
+
this.tooltipEl.parentNode.removeChild(this.tooltipEl);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|