renderByPath.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. class MarkerRenderByPath {
  2. isClip = false;
  3. clipX = 0;
  4. container = null;
  5. config = null;
  6. lastPosition = null;
  7. ctx = null;
  8. width = 0;
  9. height = 0;
  10. top = 0;
  11. left = 0;
  12. markerEls = [];
  13. mouseDownStatus = false;
  14. // 当前轨道索引
  15. currentTrackIndex = 0;
  16. // 当前轨道配置数据
  17. currentTrackData = null;
  18. // 当前轨道路径
  19. currentTrackPath = null;
  20. // 当前角度,0-1 表示0-Math.PI*2
  21. currentAngle = 0;
  22. // 当前帧步
  23. currentFrameStep = 0;
  24. // 轨道下的图像数据
  25. trackImageMap = {
  26. }
  27. downloadImagesCount = 0
  28. downloadingImagesCount = 0
  29. downloadStartedAt = 0
  30. handler = {}
  31. // 相机
  32. camera = null;
  33. constructor(options){
  34. this.container = options.container
  35. this.container.style.cursor = 'grab'
  36. this.config = options.config
  37. this.isClip = options.isClip
  38. this.currentTrackData = this.config.tracks[0]
  39. this.generateEl()
  40. this.createMarkers()
  41. this.changeTrack(null)
  42. this.preDownloadImages()
  43. this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000);
  44. }
  45. on(type, fn){
  46. this.handler[type] = fn
  47. }
  48. generateEl(){
  49. const rect = this.container.getBoundingClientRect()
  50. const canvasEl = document.createElement('canvas')
  51. this.width = rect.width
  52. this.height = rect.height
  53. if(this.isClip){
  54. this.clipX = (this.config.globalMeta.photoSize[0] / this.config.globalMeta.photoSize[1]) * this.height - this.width
  55. }
  56. this.top = rect.top
  57. this.left = rect.left
  58. canvasEl.width = rect.width
  59. canvasEl.height = rect.height
  60. const ctx = canvasEl.getContext('2d')
  61. this.ctx = ctx;
  62. canvasEl.addEventListener('mousemove', this.mouseMoveHandler)
  63. canvasEl.addEventListener('mousedown', this.mouseDownHandler)
  64. canvasEl.addEventListener('mouseup', this.mouseUpHandler)
  65. this.container.appendChild(canvasEl)
  66. }
  67. /**
  68. * 获取当前轨道帧数据
  69. */
  70. renderFrameData(){
  71. const images = this.trackImageMap[this.currentTrackData.name]
  72. if(images){
  73. const frame = images[this.currentFrameStep]
  74. if(frame){
  75. this.ctx.clearRect(0,0,this.width,this.height)
  76. this.ctx.drawImage(frame.image, 0, 0, frame.image.width, frame.image.height, 0, 0, this.width, this.height)
  77. this.updateCamera(frame)
  78. this.updateMarkers(frame)
  79. }
  80. }
  81. }
  82. updateCamera(frame){
  83. const {camera} = this
  84. const _point = frame.frameData.cPoint
  85. const _target = frame.frameData.cTarget
  86. const cpoint = new THREE.Vector3(_point[0], _point[1], _point[2])
  87. camera.position.copy(cpoint)
  88. camera.lookAt(new THREE.Vector3(_target[0], _target[1], _target[2]))
  89. camera.updateMatrix()
  90. camera.updateMatrixWorld()
  91. camera.updateWorldMatrix()
  92. camera.updateProjectionMatrix()
  93. }
  94. /**
  95. * 创建markers
  96. */
  97. createMarkers(){
  98. this.config.markers.forEach(marker =>{
  99. const divEl = document.createElement('div')
  100. divEl.setAttribute('d-markerId', marker.id)
  101. marker._point = new THREE.Vector3(marker.position[0], marker.position[1], marker.position[2])
  102. this.markerEls.push({
  103. marker: marker,
  104. el: divEl
  105. })
  106. divEl.addEventListener('click', this.clickMarker)
  107. this.container.appendChild(divEl)
  108. })
  109. }
  110. clickMarker =(ev)=>{
  111. const fn = this.handler['markerClicked']
  112. if(fn){
  113. const markerId = ev.target.getAttribute('d-markerId')
  114. fn(markerId)
  115. }
  116. }
  117. updateMarkers(){
  118. this.markerEls.forEach(markerItem => {
  119. if(markerItem.marker.type.indexOf('camera')>-1){
  120. markerItem.el.className = 'marker-anmi-camera'
  121. }
  122. if(markerItem.marker.type.indexOf('mic')>-1){
  123. markerItem.el.className = 'marker-anmi-mic'
  124. }
  125. const sP = this.scenePositionToScreenLocation(new THREE.Vector3().copy(markerItem.marker._point))
  126. markerItem.el.style.top = `${sP.y}px`
  127. markerItem.el.style.left = `${sP.x}px`
  128. })
  129. }
  130. scenePositionToScreenLocation(position){
  131. const {camera, width, height} = this
  132. const centerX = width / 2
  133. const centerY = height / 2
  134. const ppv = position.project(camera)
  135. return {
  136. x: Math.round(centerX*ppv.x + centerX),
  137. y: Math.round(-centerY*ppv.y + centerY)
  138. }
  139. }
  140. /**
  141. * 预加载图片
  142. */
  143. preDownloadImages(){
  144. // 总步数
  145. if(this.trackImageMap[this.currentTrackData.name]){
  146. return
  147. }
  148. const vm = this
  149. this.config.tracks.forEach(track => {
  150. vm.downloadImagesCount += track.steps
  151. })
  152. this.config.tracks.forEach(track => {
  153. const frameDatas = []
  154. this.trackImageMap[track.name] = frameDatas
  155. const stepCount = track.steps
  156. for(let i=0;i<stepCount;i++){
  157. const image = new Image()
  158. const frameData = this.getFrameData(i / stepCount, track)
  159. image.onload = ()=>{
  160. vm.downloadingImagesCount++
  161. frameDatas.push({
  162. step: i,
  163. image: image,
  164. frameData: frameData
  165. })
  166. if(vm.downloadImagesCount === vm.downloadingImagesCount){
  167. const fn = this.handler['imagesDownloaded']
  168. if(fn){
  169. const at = new Date().getTime()
  170. fn(at - vm.downloadStartedAt)
  171. }
  172. console.log('downloadFinished')
  173. vm.changeTrack(null)
  174. vm.renderFrameData()
  175. }
  176. }
  177. image.src = frameData.imageUrl
  178. }
  179. })
  180. }
  181. /**
  182. * 获取帧数据
  183. * 首先通过公式取,再以值修正
  184. */
  185. getFrameData(step, track){
  186. // 首先通过公式取,再以值修正
  187. const fixData = track.stepParams.find(stepParamItem => {
  188. return Math.abs(stepParamItem.stepValue - step) < 0.05
  189. })
  190. let imageUrl = ''
  191. let cPoint = null
  192. let cTarget = null
  193. if(fixData){
  194. // 有修正数据,使用修正数据
  195. imageUrl = fixData.photoUrl,
  196. cTarget = fixData.target,
  197. cPoint = fixData.camera_position
  198. }else{
  199. // 使用轨道数据
  200. imageUrl = track.photoUrl.replace('{step}', step.toFixed(2))
  201. cTarget = track.target
  202. const cPath = new THREE.Path()
  203. cPath.absarc(track.origin[0], track.origin[1], track.radis)
  204. const point = cPath.getPointAt(Number(step))
  205. cPoint = [point.x, track.height, point.y]
  206. }
  207. return {
  208. cPoint: cPoint,
  209. imageUrl: imageUrl,
  210. cTarget: cTarget
  211. }
  212. }
  213. changeTrack(type){
  214. if(type === 'next'){
  215. // 切换下一个轨道
  216. this.currentTrackIndex++
  217. }
  218. if(type === 'prev'){
  219. // 切换上一个轨道
  220. this.currentTrackIndex--
  221. }
  222. if(this.currentTrackIndex > this.config.tracks.length){
  223. this.currentTrackIndex = this.config.tracks.length - 1
  224. }
  225. if(this.currentTrackIndex < 0){
  226. this.currentTrackIndex = 0
  227. }
  228. if(this.config.tracks[this.currentTrackIndex]){
  229. this.currentTrackData = this.config.tracks[this.currentTrackIndex]
  230. const cPath = new THREE.Path()
  231. cPath.absarc(this.currentTrackData.origin[0], this.currentTrackData.origin[1], this.currentTrackData.radis)
  232. this.currentTrackPath = cPath
  233. this.preDownloadImages()
  234. const fn = this.handler['trackChanged']
  235. if(fn){
  236. fn(this.currentTrackData.name)
  237. }
  238. this.renderFrameData()
  239. }
  240. }
  241. mouseMoveHandler = (ev)=>{
  242. if(this.mouseDownStatus){
  243. const diffX = ev.clientX - this.lastPosition.x
  244. const diffY = ev.clientY - this.lastPosition.y
  245. if(Math.abs(diffX) > Math.abs(diffY)){
  246. // 水平方向移动,触发图像更新
  247. if(Math.abs(diffX) > 10){
  248. // 阈值
  249. this.currentFrameStep += ev.clientX > this.lastPosition.x ? 1 : -1
  250. this.currentFrameStep = this.currentFrameStep%this.currentTrackData.steps
  251. if(this.currentFrameStep < 0){
  252. this.currentFrameStep = this.currentTrackData.steps + this.currentFrameStep
  253. }
  254. this.renderFrameData()
  255. this.lastPosition = {
  256. x: ev.clientX,
  257. y: ev.clientY
  258. }
  259. }
  260. }else{
  261. // 竖直方向移动,触发轨道切换
  262. if(Math.abs(diffY)>20){
  263. this.changeTrack(ev.clientY > this.lastPosition.y ? 'next' : 'prev')
  264. this.lastPosition = {
  265. x: ev.clientX,
  266. y: ev.clientY
  267. }
  268. }
  269. }
  270. }
  271. }
  272. mouseDownHandler = (ev)=>{
  273. this.mouseDownStatus = true
  274. this.lastPosition = {
  275. x: ev.clientX,
  276. y: ev.clientY
  277. }
  278. }
  279. mouseUpHandler = (ev)=>{
  280. this.mouseDownStatus = false
  281. }
  282. }