class MarkerRenderByPath { isClip = false; clipX = 0; container = null; config = null; lastPosition = null; ctx = null; width = 0; height = 0; top = 0; left = 0; markerEls = []; mouseDownStatus = false; // 当前轨道索引 currentTrackIndex = 0; // 当前轨道配置数据 currentTrackData = null; // 当前轨道路径 currentTrackPath = null; // 当前角度,0-1 表示0-Math.PI*2 currentAngle = 0; // 当前帧步 currentFrameStep = 0; // 轨道下的图像数据 trackImageMap = { } downloadImagesCount = 0 downloadingImagesCount = 0 downloadStartedAt = 0 handler = {} // 相机 camera = null; constructor(options){ this.container = options.container this.container.style.cursor = 'grab' this.config = options.config this.isClip = options.isClip this.currentTrackData = this.config.tracks[0] this.generateEl() this.createMarkers() this.changeTrack(null) this.preDownloadImages() this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000); } on(type, fn){ this.handler[type] = fn } generateEl(){ const rect = this.container.getBoundingClientRect() const canvasEl = document.createElement('canvas') this.width = rect.width this.height = rect.height if(this.isClip){ this.clipX = (this.config.globalMeta.photoSize[0] / this.config.globalMeta.photoSize[1]) * this.height - this.width } this.top = rect.top this.left = rect.left canvasEl.width = rect.width canvasEl.height = rect.height const ctx = canvasEl.getContext('2d') this.ctx = ctx; canvasEl.addEventListener('mousemove', this.mouseMoveHandler) canvasEl.addEventListener('mousedown', this.mouseDownHandler) canvasEl.addEventListener('mouseup', this.mouseUpHandler) this.container.appendChild(canvasEl) } /** * 获取当前轨道帧数据 */ renderFrameData(){ const images = this.trackImageMap[this.currentTrackData.name] if(images){ const frame = images[this.currentFrameStep] if(frame){ this.ctx.clearRect(0,0,this.width,this.height) this.ctx.drawImage(frame.image, 0, 0, frame.image.width, frame.image.height, 0, 0, this.width, this.height) this.updateCamera(frame) this.updateMarkers(frame) } } } updateCamera(frame){ const {camera} = this const _point = frame.frameData.cPoint const _target = frame.frameData.cTarget const cpoint = new THREE.Vector3(_point[0], _point[1], _point[2]) camera.position.copy(cpoint) camera.lookAt(new THREE.Vector3(_target[0], _target[1], _target[2])) camera.updateMatrix() camera.updateMatrixWorld() camera.updateWorldMatrix() camera.updateProjectionMatrix() } /** * 创建markers */ createMarkers(){ this.config.markers.forEach(marker =>{ const divEl = document.createElement('div') divEl.setAttribute('d-markerId', marker.id) marker._point = new THREE.Vector3(marker.position[0], marker.position[1], marker.position[2]) this.markerEls.push({ marker: marker, el: divEl }) divEl.addEventListener('click', this.clickMarker) this.container.appendChild(divEl) }) } clickMarker =(ev)=>{ const fn = this.handler['markerClicked'] if(fn){ const markerId = ev.target.getAttribute('d-markerId') fn(markerId) } } updateMarkers(){ this.markerEls.forEach(markerItem => { if(markerItem.marker.type.indexOf('camera')>-1){ markerItem.el.className = 'marker-anmi-camera' } if(markerItem.marker.type.indexOf('mic')>-1){ markerItem.el.className = 'marker-anmi-mic' } const sP = this.scenePositionToScreenLocation(new THREE.Vector3().copy(markerItem.marker._point)) markerItem.el.style.top = `${sP.y}px` markerItem.el.style.left = `${sP.x}px` }) } scenePositionToScreenLocation(position){ const {camera, width, height} = this const centerX = width / 2 const centerY = height / 2 const ppv = position.project(camera) return { x: Math.round(centerX*ppv.x + centerX), y: Math.round(-centerY*ppv.y + centerY) } } /** * 预加载图片 */ preDownloadImages(){ // 总步数 if(this.trackImageMap[this.currentTrackData.name]){ return } const vm = this this.config.tracks.forEach(track => { vm.downloadImagesCount += track.steps }) this.config.tracks.forEach(track => { const frameDatas = [] this.trackImageMap[track.name] = frameDatas const stepCount = track.steps for(let i=0;i{ vm.downloadingImagesCount++ frameDatas.push({ step: i, image: image, frameData: frameData }) if(vm.downloadImagesCount === vm.downloadingImagesCount){ const fn = this.handler['imagesDownloaded'] if(fn){ const at = new Date().getTime() fn(at - vm.downloadStartedAt) } console.log('downloadFinished') vm.changeTrack(null) vm.renderFrameData() } } image.src = frameData.imageUrl } }) } /** * 获取帧数据 * 首先通过公式取,再以值修正 */ getFrameData(step, track){ // 首先通过公式取,再以值修正 const fixData = track.stepParams.find(stepParamItem => { return Math.abs(stepParamItem.stepValue - step) < 0.05 }) let imageUrl = '' let cPoint = null let cTarget = null if(fixData){ // 有修正数据,使用修正数据 imageUrl = fixData.photoUrl, cTarget = fixData.target, cPoint = fixData.camera_position }else{ // 使用轨道数据 imageUrl = track.photoUrl.replace('{step}', step.toFixed(2)) cTarget = track.target const cPath = new THREE.Path() cPath.absarc(track.origin[0], track.origin[1], track.radis) const point = cPath.getPointAt(Number(step)) cPoint = [point.x, track.height, point.y] } return { cPoint: cPoint, imageUrl: imageUrl, cTarget: cTarget } } changeTrack(type){ if(type === 'next'){ // 切换下一个轨道 this.currentTrackIndex++ } if(type === 'prev'){ // 切换上一个轨道 this.currentTrackIndex-- } if(this.currentTrackIndex > this.config.tracks.length){ this.currentTrackIndex = this.config.tracks.length - 1 } if(this.currentTrackIndex < 0){ this.currentTrackIndex = 0 } if(this.config.tracks[this.currentTrackIndex]){ this.currentTrackData = this.config.tracks[this.currentTrackIndex] const cPath = new THREE.Path() cPath.absarc(this.currentTrackData.origin[0], this.currentTrackData.origin[1], this.currentTrackData.radis) this.currentTrackPath = cPath this.preDownloadImages() const fn = this.handler['trackChanged'] if(fn){ fn(this.currentTrackData.name) } this.renderFrameData() } } mouseMoveHandler = (ev)=>{ if(this.mouseDownStatus){ const diffX = ev.clientX - this.lastPosition.x const diffY = ev.clientY - this.lastPosition.y if(Math.abs(diffX) > Math.abs(diffY)){ // 水平方向移动,触发图像更新 if(Math.abs(diffX) > 10){ // 阈值 this.currentFrameStep += ev.clientX > this.lastPosition.x ? 1 : -1 this.currentFrameStep = this.currentFrameStep%this.currentTrackData.steps if(this.currentFrameStep < 0){ this.currentFrameStep = this.currentTrackData.steps + this.currentFrameStep } this.renderFrameData() this.lastPosition = { x: ev.clientX, y: ev.clientY } } }else{ // 竖直方向移动,触发轨道切换 if(Math.abs(diffY)>20){ this.changeTrack(ev.clientY > this.lastPosition.y ? 'next' : 'prev') this.lastPosition = { x: ev.clientX, y: ev.clientY } } } } } mouseDownHandler = (ev)=>{ this.mouseDownStatus = true this.lastPosition = { x: ev.clientX, y: ev.clientY } } mouseUpHandler = (ev)=>{ this.mouseDownStatus = false } }