From 8077f927044b10d57d037507eb70c9c52163be47 Mon Sep 17 00:00:00 2001 From: Wensheng Yan Date: Wed, 28 Jun 2017 00:43:48 -0400 Subject: [PATCH] add hotspot support --- index.html | 22 ++++++++- src/scripts/Components/Marker.js | 77 +++++++++++++++++++++++++++++++- src/scripts/Panorama.js | 26 ++++++++++- src/scripts/tech/BasePlayer.js | 4 ++ src/scripts/types/Settings.js | 14 +++--- src/styles/plugin.scss | 13 ++++++ 6 files changed, 145 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index b21fadb..6a9b176 100644 --- a/index.html +++ b/index.html @@ -63,7 +63,27 @@ Notice: { Enable: true, Message: (isMobile())? "please drag and drop the video" : "please use your mouse drag and drop the video" - } + }, + Markers: [ + { + location: { + lat: 0, + lon: 180 + }, + radius: 500, + element: "Marker 1", + keyPoint: 5000, + duration: 10000 + }, + { + location: { + lat: 20, + lon: 160 + }, + radius: 500, + element: "Marker 2", + } + ] }); window.player = player; diff --git a/src/scripts/Components/Marker.js b/src/scripts/Components/Marker.js index 815f616..aaae5c6 100644 --- a/src/scripts/Components/Marker.js +++ b/src/scripts/Components/Marker.js @@ -1,19 +1,92 @@ // @flow -import type { Player, MarkerSettings } from '../types'; +import type { Player, MarkerSettings, Point } from '../types'; +import THREE from "three"; import Component from './Component'; +import { mergeOptions } from '../utils'; const defaults = { + keyPoint: -1, duration: -1 }; class Marker extends Component{ - constructor(player: Player, options: MarkerSettings){ + _canvas: Component; + _position: THREE.Vector3; + _enable: boolean; + + constructor(player: Player, options: MarkerSettings & { + el?: HTMLElement; + }){ + let el: HTMLElement; + + let elem = options.element; + if(typeof elem === "string"){ + el = document.createElement('div'); + el.innerText = elem; + }else { + el = elem; + } + el.id = options.id || ""; + el.className = "vjs-marker"; + + options.el = el; + super(player, options); + this._options = mergeOptions({}, defaults, options); + + this._canvas = player.getComponent("VideoCanvas"); + let phi = THREE.Math.degToRad( 90 - options.location.lat ); + let theta = THREE.Math.degToRad( options.location.lon ); + this._position = new THREE.Vector3( + options.radius * Math.sin( phi ) * Math.cos( theta ), + options.radius * Math.cos( phi ), + options.radius * Math.sin( phi ) * Math.sin( theta ), + ); + if(this.options.keyPoint > 0){ + let timeupdate; + this.player.on("timeupdate", timeupdate = () => { + let currentTime = this.player.getVideoEl().currentTime * 1000; + if(this.options.duration > 0){ + (this.options.keyPoint <= currentTime && currentTime < this.options.keyPoint + this.options.duration)? + !this._enable && this.attachEvents() : this._enable && this.detachEvents(); + }else{ + (this.options.keyPoint <= currentTime)? + !this._enable && this.attachEvents() : this._enable && this.detachEvents(); + } + }); + }else{ + this.attachEvents(); + } + } attachEvents(){ + this._enable = true; + this.addClass("vjs-marker--enable"); + this._canvas.addListener("render", this.render.bind(this)); + } + + detachEvents(){ + this._enable = false; + this.removeClass("vjs-marker--enable"); + this._canvas.removeListener("render", this.render.bind(this)); + } + render(){ + let angle = this._position.angleTo(this._canvas._camera.target); + if(angle > Math.PI * 0.4){ + this.addClass("vjs-marker--backside"); + }else{ + this.removeClass("vjs-marker--backside"); + let vector = this._position.clone().project(this._canvas._camera); + let point: Point = { + x: (vector.x + 1) / 2 * this._canvas._width, + y: - (vector.y - 1) / 2 * this._canvas._height + }; + this.el().style.left = `${point.x}px`; + this.el().style.top = `${point.y}px`; + } } } diff --git a/src/scripts/Panorama.js b/src/scripts/Panorama.js index 47fe792..eda5a0a 100644 --- a/src/scripts/Panorama.js +++ b/src/scripts/Panorama.js @@ -11,8 +11,8 @@ import ThreeDVideo from './Components/ThreeDVideo'; import Notification from './Components/Notification'; import Thumbnail from './Components/Thumbnail'; import VRButton from './Components/VRButton'; -import { Detector, webGLErrorMessage, crossDomainWarning, transitionEvent, mergeOptions, mobileAndTabletcheck, isIos, isRealIphone } from './utils'; -import { warning } from './utils/index'; +import Marker from './Components/Marker'; +import { Detector, webGLErrorMessage, crossDomainWarning, transitionEvent, mergeOptions, mobileAndTabletcheck, isIos, isRealIphone, warning } from './utils'; const runOnMobile = mobileAndTabletcheck(); @@ -107,6 +107,8 @@ class Panorama extends EventEmitter{ _player: Player; _videoCanvas: BaseCanvas; _thumbnailCanvas: BaseCanvas | null; + //save total markers enable to generate marker id + _totalMarkers: number; /** * check legacy option settings and produce warning message if user use legacy options, automatically set it to new options. @@ -212,6 +214,7 @@ class Panorama extends EventEmitter{ Panorama.checkOptions(options); this._options = mergeOptions({}, defaults, options); this._player = player; + this._totalMarkers = 0; this.player.addClass("vjs-panorama"); @@ -314,6 +317,13 @@ class Panorama extends EventEmitter{ this.videoCanvas.startAnimation(); this.videoCanvas.show(); + //initial markers + if(this.options.Markers){ + this.options.Markers.forEach((markSetting: any)=>{ + this.addMarker(markSetting); + }); + } + //detect black screen if(window.console && window.console.error){ let originalErrorFunction = window.console.error; @@ -378,6 +388,18 @@ class Panorama extends EventEmitter{ } } + addMarker(markSetting: any): Marker{ + this._totalMarkers++; + markSetting.id = markSetting.id? markSetting.id : `marker_${this._totalMarkers}`; + let marker = new Marker(this.player, markSetting); + this.player.addComponent(markSetting.id, marker); + return marker; + } + + removeMarker(markerId: string): void{ + this.player.removeComponent(markerId); + } + get thumbnailCanvas(): BaseCanvas | null{ return this._thumbnailCanvas; } diff --git a/src/scripts/tech/BasePlayer.js b/src/scripts/tech/BasePlayer.js index 167d0e3..aa3c01c 100644 --- a/src/scripts/tech/BasePlayer.js +++ b/src/scripts/tech/BasePlayer.js @@ -140,6 +140,10 @@ class BasePlayer implements Player { ready(fn: Function): void{ throw Error('Not implemented'); } + + get components(): Array{ + return this._components; + } } export default BasePlayer; \ No newline at end of file diff --git a/src/scripts/types/Settings.js b/src/scripts/types/Settings.js index fcbbf55..f64745b 100644 --- a/src/scripts/types/Settings.js +++ b/src/scripts/types/Settings.js @@ -1,27 +1,29 @@ // @flow -import { Point } from './Point'; +import { Point, Coordinates } from './Point'; export type VideoTypes = "equirectangular" | "fisheye" | "3dVideo" | "dual_fisheye"; export type MarkerSettings = { - lat: number; - lon: number; + location: Coordinates; + radius: number; + id?: string; + /** * use custom dom */ - element?: HTMLElement; + element: HTMLElement | string; /** * timeline when should marker be shown */ - keyPoint: number; + keyPoint?: number; /** * when should marker disappear, set -1 if you don't want it disappear */ - duration: number; + duration?: number; /** * callback function when marker is disappear diff --git a/src/styles/plugin.scss b/src/styles/plugin.scss index 0a640a0..42c7795 100644 --- a/src/styles/plugin.scss +++ b/src/styles/plugin.scss @@ -132,6 +132,19 @@ -webkit-animation-delay: 0.44s; animation-delay: 0.44s; } + .vjs-marker{ + position: absolute; + display: none; + + &.vjs-marker--enable{ + display: block!important; + } + + &.vjs-marker--backside{ + display: none!important; + } + } + .vjs-VR-control{ cursor: pointer;