@tencent/vtour

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)

Tencent Media Lab
We would like to use performance and analytics cookies (“Cookies”) to help us recognize whether you are a returning visitor and to track the number of website views and visits. For more information about the Cookies we use and your options (including how to change your preferences) see our Cookies Policy.