vTour is a 3D virtual tour web SDK based on Three.js by Tencent Media Lab.
The aim of the project is to create an easy-to-use, customizable and smooth-experience 3D virtual tour library for building virtual tour products quickly.
Features
-
Panorama HD rendering (up to 4K)
-
Building model display (Free-drag mode / Floorplan mode)
-
Custom 3D labels. Pictures, videos, hyperlinks can be embeded
-
Custom 2D labels. DOM nodes can be mounted
-
Scenes switching
Advantages
-
High quality display. Picture quality up to 4k
-
Strong sense of immersion
-
Fast loading
-
Highly customizable
-
Beautiful and smooth animation
Installation
vTour has been published to Tencent Mirrors - npm registry.
Please refer here to learn how to use this private registry.
tnpm install '@tencent/vtour'
Usage
// index.ts
import { Player } from '@tencent/vtour'
const data = {
scenes = [
{
index: 's0',
name: 'Scene 0',
panos: [
{
index: 'p0',
name: 'Pano 0',
position: [0.2, 0.0, 0.1],
visibleNodes: ['p1'],
scene: 's0',
},
{
index: 'p1',
name: 'Pano 1',
position: [0.3, 0.0, -0.25],
visibleNodes: ['p0'],
scene: 's0',
},
],
model: {
index: 'm0',
mesh: 'https://example.com/mesh.glb',
},
},
],
tileTemplate: 'https://example.com/{sceneIndex}/{panoIndex}/{layer}/{face}_{x}_{y}.jpg',
tilingLayers: {
{
index: '512',
faceRes: 512,
tileRes: 512,
},
{
index: '1024',
faceRes: 1024,
tileRes: 512,
},
{
index: '2048',
faceRes: 2048,
tileRes: 512,
},
}
}
const container = document.createElement('div')
container.style.position = 'relative'
container.style.height = '100vh'
container.style.width = '100vw'
document.body.append(container)
;(async () => {
const player = new Player({
container,
data,
})
player.on('loadProgress', (e) => {
console.log('loaded ' + e.progress)
})
// player.start() is asynchronous
await player.start()
})()
Examples
All examples are under /examples. Please check out respective documentations for details:
-
/frameworkless: A frameworkless example by Webpack5, TypeScript and SCSS -
/react: A CRA (Create React App) example. -
/copypaste: An example for importing vtour lib by copy-paste
Essential Types
Essential types used in the player
Pano
Pano description data
interface Pano {
index: string // Pano index
scene: string // Index of the belonged scene
position: number[] // Pano position
visibleNodes: string[] // Visible(Accessible) pano indexes
lonLat?: number[] // Preset lonLat
fov?: number // Preset fov
name?: string // Pano name
thumbnail?: string // Thumbnail image
active?: boolean // Pano rendered or not. Useful for hidding pano temporarily
tags?: string[]
}
Model
Model description data
interface Model {
index: string // Model index
mesh: string // Mesh url
}
Scene
Scene description data
One Scene is composed of multiple Panos and single Model.
interface Scene {
index: string // Scene index
panos: Pano[] // Panos
model: Model // Model
name?: string // Scene name
thumbnail?: string // Scene thumbnail
initPano?: string // Initial pano index
initLonLat?: number[] // Initial longtitude & latitude (in drgrees)
tileTemplate?: string // tileTemplate for this scene
tilingLayers?: Layer[] // tilingLayers for this scene
faceMapping?: string[] // faceMapping for this scene
}
Mode
Display mode
-
pano - panoroma mode
-
mesh - model mode
-
floorplan - floorplan mode
type Mode = 'pano' | 'mesh' | 'floorplan'
Layer
Tiling layer description data
interface Layer {
faceRes: number // Face resolution
tileRes: number // Tile resolution
index?: string // Layer index
leFov?: number // Render layer when less than or euqual to this fov value
}
Data
Player's core data containing pano, scene, model and tiling options.
tileTemplate - tile image download url for all scenes. Parameters must be in {}, which are:
-
sceneIndex: scene index -
panoIndex: pano index -
layer: layer index -
face: face name -
x: tile's x coordinate -
y: tile's y coordinate
tilingLayers - tiling layer configurations for all scenes
faceMapping - face mapping for face names for all scenes. Default is ['f', 'b', 'u', 'd', 'l', 'r']
cameraHeight - camera stand height. Default is 1.3(m)
initScene - initial scene index. Default is the first scene
initMode - initial mode name. Default is 'pano'
useModel - if using model or not
kiv - model decryption data
interface Data {
scenes: Scene[]
tileTemplate?: string
tilingLayers?: Layer[]
faceMapping?: string[]
cameraHeight?: number
initScene?: string
initMode?: string
modelType?: ModelType
useModel?: boolean
kiv?: [string, string]
}
Configs
Player configurations
interface Configs {
/* Scene */
antialias: boolean
sRGBEncoding: boolean
autoResize: boolean
enableKeyControl: boolean
fov: number
minFov: number
maxFov: number
ctlDampingFactor: number
ctlRotateSpeedPano: number
ctlRotateSpeedModel: number
ctlZoomSpeed: number
/* Animation */
movingDuration: number
modeChangingDuration: number
cameraUpdatingDuration: number
/* Cursor */
cursorEnabled: boolean
cursorURL: string
cursorColor: string
cursorOpacity: number
cursorRadius: number
/* Hotspots */
hotspotEnabled: boolean
hotspotURL: string
hotspotColor: string
hotspotOpacity: number
hotspotRadius: number
/* Compass */
compassEnabled: boolean
compassURL: string
compassRadius: number
/* Overlay */
overlayColor: string
autoMovingOverlay: boolean
}
PlayerOptions
Player constructor options
interface PlayerOptions {
container: HTMLElement // Appended parent element
data: Data // Core data
configs?: Configs // Configs
}
EventListener
Player event listener callback type
type EventListener = (data: Record<string, any>) => void
Player API
Constructor
Player(options: PlayerOptions)
Those options are optional:
configs - default values are as follows:
const CONFIGS_DEFAULT: Configs = {
antialias: false,
sRGBEncoding: true,
autoResize: true,
enableKeyControl: true,
fov: 75,
minFov: 40,
maxFov: 100,
ctlDampingFactor: 0.22,
ctlRotateSpeedPano: -0.7,
ctlRotateSpeedModel: 0.7,
ctlZoomSpeed: 1,
movingDuration: 1400,
modeChangingDuration: 1000,
cameraUpdatingDuration: 1000,
cursorEnabled: true,
cursorURL: '/hotspot_alpha.jpg',
cursorColor: '#00bcf5',
cursorOpacity: 0.6,
cursorRadius: 0.2,
hotspotEnabled: true,
hotspotURL: '/hotspot_alpha.jpg',
hotspotColor: '#ffffff',
hotspotOpacity: 0.8,
hotspotRadius: 0.2,
compassEnabled: true,
compassURL: '/compass.png',
compassRadius: 1.2,
overlayColor: '#303030',
autoMovingOverlay: true,
}
Properties
.scenes: Scene[]
Imported player scenes
.configs: Configs
Imported player configurations
.container: HTMLElement
Appended parent element
.eventLayer: HTMLElement
Mounted layer for event listeners
.activeCamera: PerspectiveCamera | OrthographicCamera
Current camera in use
.threeScene: THREE.Scene
THREE.Scene instance in use
.currentScene: Scene
Current scene in use
.currentPanos: Pano[]
Current panos in use
.currentPano: Pano
Current pano in use
.currentMode: Mode
Current mode
.previousMode: Mode
Previous mode
.enablePanoMove: boolean
Enable or disable pano movement
.model?: Object3D
Current model
.started?: boolean
Whether player started
.disposed: boolean
Whether player disposed
Methods
.start: (data?: Data) => Promise<void>
Start player (Load resources & start rendering)
An optional data can be passed to override the default data
data?: Data
.moveTo: (nextIndex: string, options?: { duration?: MoveToOptions }) => Promise<void>
Move to another pano
nextIndex: string
Next pano index
options?: MoveToOptions
interface MoveToOptions {
duration?: number // moving duration
useOverlay?: boolean // if use overlay(black-in/out) or not
lonLat?: number[] // lonLat when moving ends
fov?: number // fov when moving ends
onEnd?: () => void // end callback
}
.changeMode: (mode: Mode) => void
Change current mode
.changeScene: (nextIndex: string, options?: ChangeSceneOptions) => Promise<void>
Change current scene
nextIndex: string
Next scene index
options?: ChangeSceneOptions
interface ChangeSceneOptions {
panoIndex?: string // first pano in the next scene
lonLat?: number[] // initial lonLat in the next scene
}
.updateCamera: (options?: UpdateCameraOptions) => Promise<void>
Update camera's lonLat/fov/position/pivot
options?: UpdateCameraOptions
interface UpdateCameraOptions {
lonLat?: number[] // target lonLat
fov?: number // target fov
position?: number[] // target position
pivot?: number[] // target pivot
duration?: number // duration of updating
onEnd?: () => void
} // end callback
.computeScreenCoord: (position: { x: number; y: number; z: number }, options?: ComputeScreenCoordOptions) => Vector3 | null
Compute screen coordination from position
position: string
position in the scene
options?: ComputeScreenCoordOptions
interface ComputeScreenCoordOptions {
maxDistance?: number // max distance for computing. Default is 10(m)
}
.computeScenePos: (coord: { x: number; y: number }, options?: ComputeScenePosOptions) => Vector3 | null
Compute scene position from coordination
coord: coordination on the screen
options?: ComputeScenePosOptions
interface ComputeScenePosOptions {
maxDistance?: number // max distance for computing. Default is 100(m)
offset?: number // offset of the position point. Positive towards 'inner', negative towards 'outer'
}
.takeScreenshot: (type?: 'image/png' | 'image/jpeg' | 'image/webp', quality?: number) => string
Take a screenshot of current frame
type: 'image/png' | 'image/jpeg' | 'image/webp'
Screenshot MIME type. Default is 'image/jpeg'. Supports png, jpeg and webp
quality?: number
A number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as 'image/jpeg' or 'image/webp')
.getFov: () => number
Get perspective camera fov
.getLongitudeAndLatitude: () => number[]
Get current camera longitude and latitude
.on: (name: string, listener: EventListener) => void
Add event listener
.hasListener: (name: string, listener: EventListener) => boolean
Check if the player has the listener
.emit: (name: EventName, data: Record<string, any>) => boolean | void
Emit event. In some events (see event notes), return false in callback(s) to prevent events. If multiple callbacks exist, any one return false will prevent the event proceeding.
.off: (name: string, listener: EventListener) => void
Remove event listener
.resize: () => void
Resize the renderer and the scene to keep everything look normal
.dispose: () => void
Dispose player (release resources)
Events
loadStart: { pano: string, scene: string }
Fires when the player start to load resources.
pano - target pano index
scene - target scene index
loadProgress: { progress: number, pano: string, scene: string }
Fires when the player is loading resources.
progress - loading progress (range: [0, 1])
pano - target pano index
scene - target scene index
loadEnd: { pano: string, scene: string }
Fires when the player is loading resources.
progress -
pano - target pano index
scene - target scene index
moveStart: { pano: string, scene: string }
Fires when a pano movement is initiated (start loading cubemaps if not cached).
pano - target pano index
scene - target scene index
moveReady: { pano: string, scene: string }
Fires when a pano movement is ready (loading has finished).
pano - target pano index
scene - target scene index
moveEnd: { pano: string, scene: string }
Fires when a pano movement has finished.
pano - target pano index
scene - target scene index
modeChangeStart: { mode: Mode }
Fires if the mode is changed.
mode - target mode name
modeChangeEnd: { mode: Mode }
Fires when the mode change has finished.
mode - target mode name
sceneChangeStart: { pano: string, scene: string }
Fires when the current scene is changed.
pano - target pano index
scene - target scene index
sceneChangeEnd: { pano: string, scene: string }
Fires when the scene change has finished.
pano - target pano index
scene - target scene index
pointerIntersect: { position: number[], lonLat: number[] }
Fires when pointer (mouse/touch) intersects with the current model.
position - intersection position [x, y, z]
lonLat - camera longitude and latitude [longitude, latitude]
cameraChangeStart: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's direction/position/zoom starts to change
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraChange: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's direction/position/zoom changes
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraChangeEnd: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's direction/position/zoom finishes changing
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
fromControl - whether triggered from the control
cameraMoveStart: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's position starts to change
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraMove: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's position changes
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraMoveEnd: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's position finishes changing
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraRotateStart: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's direction start to change
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraRotate: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera direction changes
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraRotateEnd: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's direction finishes changing
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoomStart: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's zoom level starts to change
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoom: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera fov changes
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoomEnd: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's zoom level finishes changing
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoomFovStart: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's fov starts to change
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoomFov: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera fov changes
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
cameraZoomFovEnd: { lonLat: number[], position: number[], fov: number, pivot: number[], fromControl: boolean }
Fires when camera's fov finishes changing
lonLat - camera longitude and latitude [longitude, latitude]
position - camera position [x, y, z]
fov - camera fov
pivot - camera looking pivot
fromControl - whether triggered from the control
render
Fired when current frame rendering finished
(Caution: DO NOT put complex calculations into this event, which could cause severe latency)
