diff --git a/sdk/demo/demo.html b/sdk/demo/demo.html index 2d209afd98d19f94779c65d013f8306513e6d104..98b347a24d717a3a6b7f8cba9883a73ec54bb140 100644 --- a/sdk/demo/demo.html +++ b/sdk/demo/demo.html @@ -69,7 +69,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. line-height: 40px; text-align: center; width: 100%; - color: #fff; + color: #3AC295; } #keyboard-dialog { border: 1px solid lightblue; @@ -121,6 +121,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. } .control-btn { display: none; + user-select: none; font-size: 14px; line-height: 40px; text-align: center; @@ -137,7 +138,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. .button-modal{ display: none; width: 255px; - height: 155px; + height: 200px; position: absolute; z-index: 999; top: 50%; @@ -146,9 +147,9 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. border-radius: 5px; transform: translate(-50%, -50%); } - .button-network,.button-exit{ + .button-network,.button-exit, .fullscreen-btn{ width: 100%; - height: 25%; + height: 38px; margin-bottom: 10px; } .radio-group { @@ -156,7 +157,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. display: flex; justify-content: space-around; align-items: center; - height: 35%; + height: 55px; } input[type="radio"] { transform: scale(1.5); @@ -254,6 +255,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. + @@ -582,7 +584,9 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. const buttonModalEle = document.getElementById('buttonModal'); const playContainer = document.querySelector('article'); const playerElement = playContainer.shadowRoot.querySelector('#playCanvas') || playContainer.shadowRoot.querySelector('#phoenixVideo'); + const fullscreenBtn = document.getElementById('toggleFullscreen'); ctrlEle.style.display = 'block'; + ctrlEle.addEventListener('click', (event) => { if(buttonModalEle.style.display !== 'block') { buttonModalEle.style.display = 'block'; @@ -592,12 +596,14 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. }); if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) { + fullscreenBtn.style.display = 'block'; playerElement.addEventListener('touchend', (event) => { if(!buttonModalEle.contains(event.target) && !ctrlEle.contains(event.target)) { buttonModalEle.style.display = 'none'; } }); } else { + fullscreenBtn.style.display = 'none'; document.addEventListener('click', (event) => { if(!buttonModalEle.contains(event.target) && !ctrlEle.contains(event.target)) { buttonModalEle.style.display = 'none'; diff --git a/sdk/enter.html b/sdk/enter.html index c4f84de5e99b85a51d94aaeaa40c1ff87c82fc3d..ad022c13f75f4e9841c8cc5aec7c709af49f6ecd 100644 --- a/sdk/enter.html +++ b/sdk/enter.html @@ -14,7 +14,7 @@ .pc-size { width: 500px; - height: 300px; + overflow: hidden; } .mobile-size { @@ -27,10 +27,11 @@ display: flex; flex-direction: column; align-items: center; + padding: 10px; } .input-item { - margin-top: 30px; + margin-top: 20px; width: 100%; height: 35px; text-align: center; @@ -65,7 +66,6 @@ .enter-btn { cursor: pointer; - margin-top: 20px; width: 50%; height: 35px; background: #fff; @@ -200,10 +200,10 @@ let localHref = window.location.href + 'index'; if (window.location.href.indexOf('enter.html') > -1) { - localHref = window.location.href.replace('enter.html', 'index.html'); - if (localHref === window.location.href) { - localHref = window.location.href.replace('enter.html', 'demo.html'); - } + localHref = window.location.href.replace('enter.html', 'demo.html'); + if (localHref === window.location.href) { + localHref = window.location.href.replace('enter.html', 'demo.html'); + } } window.location.href = localHref; diff --git a/sdk/index.html b/sdk/index.html index fadf55a35875a47823f70aadba8dce4afde97d0f..f4dbd34c6cdce44c4fc6b1899f00f1bfbe03e201 100644 --- a/sdk/index.html +++ b/sdk/index.html @@ -69,7 +69,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. line-height: 40px; text-align: center; width: 100%; - color: #fff; + color: #3AC295; } #keyboard-dialog { border: 1px solid lightblue; @@ -121,6 +121,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. } .control-btn { display: none; + user-select: none; font-size: 14px; line-height: 40px; text-align: center; @@ -137,7 +138,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. .button-modal{ display: none; width: 255px; - height: 155px; + height: 200px; position: absolute; z-index: 999; top: 50%; @@ -146,9 +147,9 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. border-radius: 5px; transform: translate(-50%, -50%); } - .button-network,.button-exit{ + .button-network,.button-exit, .fullscreen-btn{ width: 100%; - height: 25%; + height: 38px; margin-bottom: 10px; } .radio-group { @@ -156,7 +157,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. display: flex; justify-content: space-around; align-items: center; - height: 35%; + height: 55px; } input[type="radio"] { transform: scale(1.5); @@ -254,6 +255,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. + @@ -581,7 +583,9 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. const buttonModalEle = document.getElementById('buttonModal'); const playContainer = document.querySelector('article'); const playerElement = playContainer.shadowRoot.querySelector('#playCanvas') || playContainer.shadowRoot.querySelector('#phoenixVideo'); + const fullscreenBtn = document.getElementById('toggleFullscreen'); ctrlEle.style.display = 'block'; + ctrlEle.addEventListener('click', (event) => { if(buttonModalEle.style.display !== 'block') { buttonModalEle.style.display = 'block'; @@ -591,12 +595,14 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. }); if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) { + fullscreenBtn.style.display = 'block'; playerElement.addEventListener('touchend', (event) => { if(!buttonModalEle.contains(event.target) && !ctrlEle.contains(event.target)) { buttonModalEle.style.display = 'none'; } }); } else { + fullscreenBtn.style.display = 'none'; document.addEventListener('click', (event) => { if(!buttonModalEle.contains(event.target) && !ctrlEle.contains(event.target)) { buttonModalEle.style.display = 'none'; diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 7156971ffa904d2487e3f3afe4a1e2df8d7eefb9..173c01d6db0c1cbe8db9f7615cd32e8f247594c4 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,6 +1,6 @@ { "name": "CloudAppSdk_H5", - "version": "21.4.0", + "version": "23.6.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2358,6 +2358,20 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmmirror.com/babel-jest/-/babel-jest-26.6.3.tgz", @@ -3509,6 +3523,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "css-select": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", @@ -4812,6 +4831,11 @@ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", diff --git a/sdk/src/AppController.js b/sdk/src/AppController.js index bfa360e834f693297b8da0b0559c5a33d6946b92..ceab1448698a6e24ee6105683500fc0fb8fafc35 100644 --- a/sdk/src/AppController.js +++ b/sdk/src/AppController.js @@ -36,6 +36,7 @@ import { DEFAULT_DEFINITION, FRAME_TYPE_MAP } from './config/commonConfig'; +import FullScreen from './Fullscreen'; /*global __IS_DEBUG__*/ if (__IS_DEBUG__) { @@ -161,6 +162,15 @@ class AppController { }; this.appState = null; this.socketHasOpenned = false; // 标识socket是否已open过,用于重连时判断发送connect还是reconnect cmd + + // 初始化全屏类 + this.fullscreen = new FullScreen({ + containerId: this.options.containerId, + autoRotate: this.options.autoRotate, + isMobile: this.options.isMobile + }); + this.fullscreen.init(); + this.triggerFullscreen(); } getVolume() { @@ -339,24 +349,23 @@ class AppController { inputId: this.keyboardInput && this.keyboardInput.inputId || '' }); this.touchHandler.start(); + if(this.touchHandler.displayBox.width && this.touchHandler.displayBox.height) { const ctrlEle = document.getElementById('controlBtn'); const containerEle = document.getElementById('container'); - const left = (containerEle.offsetWidth - this.touchHandler.displayBox.width)/2 + 20; - const top = (containerEle.offsetHeight - this.touchHandler.displayBox.height)/2 + 125; - if (this.options.isMobile) { - ctrlEle.style.top = `20%`; - ctrlEle.style.left = `5%`; - } else { - ctrlEle.style.top = `${top}px`; - ctrlEle.style.left = `${left}px`; - } + const left = (containerEle.offsetWidth - this.touchHandler.displayBox.width)/2 + ctrlEle.clientWidth/2; + const top = (containerEle.offsetHeight - this.touchHandler.displayBox.height)/2 + ctrlEle.clientHeight * 2.5; + ctrlEle.style.top = `${top}px`; + ctrlEle.style.left = `${left}px`; + ctrlEle.style.userSelect = 'none'; } this.directionHandler = new DirectionHandler({ containerId: this.options.containerId, isMobile: this.options.isMobile, - playerContainerId: this.playerContainerId + playerContainerId: this.playerContainerId, + touchHandler: this.touchHandler }); + this.directionHandler.init(); this.listenPlayerSizeChange(); this.autoRotation = new AutoRotation(this.options.containerId, @@ -447,7 +456,7 @@ class AppController { } // 增加播放状态条件限制,避免destroy后播放报错 - if (this.options.supportAudio && this.playing) { + if (this.options.supportAudio && this.playing && this.audioPlayer) { const frame = this.frameParser.shiftPackage('Audio'); frame && this.audioPlayer.feed(frame); } @@ -473,6 +482,7 @@ class AppController { let pkgBody = orientationPkg & 0xFF; const orientation = PROTOCOL_CONFIG.ORIENTATION[pkgBody]; Logger.debug('Orientation change:' + orientation); + this.pkgBody = pkgBody; this.orientation = orientation; this.updateResolutionAndTouch(); } @@ -491,12 +501,13 @@ class AppController { updateResolutionAndTouch() { if (this.player && this.orientation) { // resize之前更新canvas大小 - if (!this.options.isMobile) { + if (this.options.isMobile) { + this.directionHandler.updateMobileCanvasSize(this.orientation, this.isMSE); + } else { this.directionHandler.updateCanvasSize(this.orientation, this.isMSE); } this.touchHandler.updateOrientation(this.orientation); - this.autoRotation && this.autoRotation.updateOrientation(this.orientation); - this.directionHandler.update(this.orientation, this.touchHandler.displayBox); + this.autoRotation && this.autoRotation.updateOrientation(this.orientation, this.touchHandler.displayBox, this.directionHandler); } } @@ -667,6 +678,9 @@ class AppController { this.updateResolution(); // 按照参数设置清晰度 this.setResolution(DEFAULT_DEFINITION); + + // 进入全屏模式 + this.triggerFullscreen(); break; case codeConfig.INVALID_OPERATION: case codeConfig.RECONNECT_FAILED: @@ -1100,9 +1114,6 @@ class AppController { } disconnect(callbackFn) { - // 停止传感器 - this.deviceHardwareHandler.handleSensorDisable(this.sensorMsgType.TYPE_ACCELEROMETER); - this.deviceHardwareHandler.handleSensorDisable(this.sensorMsgType.TYPE_GYROSCOPE); // client自动断连场景不重连 this.reconnection.can = false; this.stopCloudPhone(); @@ -1357,6 +1368,24 @@ class AppController { && this.socketWorkerState.socket !== WEBSOCKET_READY_STATE.CLOSED; } + // fullscreenElementId: 全屏元素ID。不能在同一个dom上旋转和全屏,所以若自动旋转则需另提供全屏的dom id,如containerId的父级dom + fullscreenToggle(fullscreenElementId = 'fullscreen-container') { + this.fullscreen.fullscreenToggle(fullscreenElementId, this.playerContainerId, this.isMSE); + } + + isFullscreen() { + return this.fullscreen.isFullscreen(); + } + + triggerFullscreen() { + const fullscreenBtn = document.getElementById('toggleFullscreen'); + if (fullscreenBtn) { + fullscreenBtn.addEventListener('click', () => { + this.fullscreenToggle(); + }); + } + } + /** * 销毁 * @param {boolean}} reserveSocketWorker 是否需要销毁socket,exit场景,延迟close socket,close前需保留socket @@ -1370,12 +1399,18 @@ class AppController { this.touchHandler && this.touchHandler.destroy(); this.autoRotation && this.autoRotation.destroy(); this.keyboardInput && this.keyboardInput.destroy(); + this.fullscreen && this.fullscreen.destroy(); + // 停止传感器 + this.deviceHardwareHandler && this.deviceHardwareHandler.handleSensorDisable(this.sensorMsgType.TYPE_ACCELEROMETER); + this.deviceHardwareHandler && this.deviceHardwareHandler.handleSensorDisable(this.sensorMsgType.TYPE_GYROSCOPE); this.jmuxer = null; this.avc = null; this.audioPlayer = null; this.touchHandler = null; this.autoRotation = null; this.keyboardInput = null; + this.fullscreen = null; + this.deviceHardwareHandler = null; if (this.player && this.player.parentNode) { if (this.videoResizeCallback) { diff --git a/sdk/src/AutoRotation.js b/sdk/src/AutoRotation.js index 11f9291370b7d3941c03bf27811f4ea07f2d3ce5..23ad77252393c55bd70c2e84aec12c87e167d137 100644 --- a/sdk/src/AutoRotation.js +++ b/sdk/src/AutoRotation.js @@ -13,6 +13,8 @@ // limitations under the License. import Util from './Util'; +import {DEVICE_ORIENTATION_ANGLES, CLOUD_PHONE_RATIO} from './config/commonConfig'; +import PROTOCOL_CONFIG from './config/protocolConfig'; const ORIENTATION_DEGRESS = { 'PORTRAIT': 0, @@ -56,8 +58,10 @@ export default class AutoRotation { }); } - updateOrientation(orientation) { + updateOrientation(orientation, displayBox, directionHandler) { this.orientation = orientation; + this.directionHandler = directionHandler; + this.displayBox = displayBox; this.rotatePlayer(); } @@ -71,58 +75,212 @@ export default class AutoRotation { } const rect = container.parentNode && container.parentNode.getClientRects()[0]; + let style = ''; + let rotateDegrees = 0; + let playerWidth = 0; + let playerHeight = 0; if (rect) { if (this.isMobile) { - width = Math.max(width - Math.max(rect.left, 0), 0); - height = Math.max(height - Math.max(rect.top, 0), 0); + const deviceOrientation = this.getDeviceOrientation(); + if ([DEVICE_ORIENTATION_ANGLES.PORTRAIT,DEVICE_ORIENTATION_ANGLES.REVERSE_PORTRAIT].includes(deviceOrientation)) { + if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[0]) { + style = ` + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + transform: none; + width: ${width}px; + height: ${height}px; + position: relative; + top: 0px; + left: 0px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[16]) { + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + width: ${width}px; + height: ${height}px; + position: relative; + top: 0px; + left: 0px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[8]) { + playerWidth = width; + playerHeight = playerWidth * CLOUD_PHONE_RATIO; + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: left; + -moz-transform-origin: left; + -ms-transform-origin: left; + transform-origin: left; + width: ${playerHeight}px; + height: ${playerWidth}px; + position: relative; + top: ${(height - playerHeight)/2}px; + left: ${playerWidth/2}px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[24]) { + playerWidth = width; + playerHeight = playerWidth * CLOUD_PHONE_RATIO; + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: top; + -moz-transform-origin: top; + -ms-transform-origin: top; + transform-origin: top; + width: ${playerHeight}px; + height: ${playerWidth}px; + position: relative; + top: ${(height - playerHeight)/2 + playerHeight/2}px; + left: ${playerWidth - playerHeight/2}px; + `; + } + } else if([DEVICE_ORIENTATION_ANGLES.LANDSCAPE, DEVICE_ORIENTATION_ANGLES.REVERSE_LANDSCAPE].includes(deviceOrientation)) { + if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[0]) { + playerWidth = height * CLOUD_PHONE_RATIO; + playerHeight = height; + style = ` + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + transform: none; + width: ${playerWidth}px; + height: ${playerHeight}px; + position: relative; + top: 0px; + left: ${(width - playerWidth)/2}px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[16]) { + playerWidth = height * CLOUD_PHONE_RATIO; + playerHeight = height; + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + width: ${playerWidth}px; + height: ${playerHeight}px; + position: relative; + top: 0px; + left: ${(width - playerWidth)/2}px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[8]) { + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: left; + -moz-transform-origin: left; + -ms-transform-origin: left; + transform-origin: left; + width: ${height}px; + height: ${width}px; + position: relative; + top: ${height - width/2}px; + left: ${width/2}px; + `; + } else if (this.orientation === PROTOCOL_CONFIG.ORIENTATION[24]) { + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: top; + -moz-transform-origin: top; + -ms-transform-origin: top; + transform-origin: top; + width: ${height}px; + height: ${width}px; + position: relative; + top: ${height/2}px; + left: ${width - height/2}px; + `; + } + } } else { width = Math.max(width - Math.max(rect.left * 2, 0), 0); height = Math.min(height - Math.max(rect.top * 2, 0), rect.height); + + // 根据本地视口方向和云手机方向判断播放器dom的旋转方向,将播放器旋转至视口方向。顺时针为正数,逆时针为负数 + if (this.orientation === 'PORTRAIT') { + style = ` + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + transform: none; + width: ${width}px; + height: ${height}px; + position: relative; + top: 0px; + left: 0px; + `; + } else if (this.orientation === 'REVERSE_PORTRAIT') { + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: '50% 50%'; + -moz-transform-origin: '50% 50%'; + -ms-transform-origin: '50% 50%'; + transform-origin: '50% 50%'; + width: ${width}px; + height: ${height}px; + position: relative; + top: 0px; + left: 0px; + `; + } else { + // 云手机横屏时顺时针旋转,竖屏时逆时针旋转 + rotateDegrees = ORIENTATION_DEGRESS[this.orientation]; + style = ` + -webkit-transform: rotate(${rotateDegrees}deg); + -moz-transform: rotate(${rotateDegrees}deg); + -ms-transform: rotate(${rotateDegrees}deg); + transform: rotate(${rotateDegrees}deg); + -webkit-transform-origin: '50% 50%'; + -moz-transform-origin: '50% 50%'; + -ms-transform-origin: '50% 50%'; + transform-origin: '50% 50%'; + width: ${height}px; + height: ${width}px; + position: relative; + top: ${(height - width) / 2}px; + left: ${0 - (height - width) / 2}px; + `; + } } } - let style = ''; - let rotateDegrees = 0; - // 根据本地视口方向和云手机方向判断播放器dom的旋转方向,将播放器旋转至视口方向。顺时针为正数,逆时针为负数 - if ((this.isMobile && ORIENTATION_DEGRESS[this.orientation] === -90) - || (Math.abs(this.viewportOrientation - ORIENTATION_DEGRESS[this.orientation]) % 180) === 0) { - style = ` - -webkit-transform: none; - -moz-transform: none; - -ms-transform: none; - transform: none; - width: ${width}px; - height: ${height}px; - position: relative; - top: 0px; - left: 0px; - `; - } else { - // 云手机横屏时顺时针旋转,竖屏时逆时针旋转 - rotateDegrees = !this.isMobile ? ORIENTATION_DEGRESS[this.orientation] : Math.abs(ORIENTATION_DEGRESS[this.orientation]) === 90 ? 90 : -90; - style = ` - -webkit-transform: rotate(${rotateDegrees}deg); - -moz-transform: rotate(${rotateDegrees}deg); - -ms-transform: rotate(${rotateDegrees}deg); - transform: rotate(${rotateDegrees}deg); - -webkit-transform-origin: '50% 50%'; - -moz-transform-origin: '50% 50%'; - -ms-transform-origin: '50% 50%'; - transform-origin: '50% 50%'; - width: ${height}px; - height: ${width}px; - position: relative; - top: ${(height - width) / 2}px; - left: ${0 - (height - width) / 2}px; - `; - } - container.style.cssText = this.originStyle + style; // 旋转后通过getClientRects()立即获取宽高可能会获取到旋转前的宽高 this.callbackAfterRotate && setTimeout(() => { this.callbackAfterRotate(rotateDegrees); }, 300); + + if (this.directionHandler) { + const keyboardEle = document.getElementById('keyboardInputContent'); + const currentDisplay = keyboardEle.style.display; + this.directionHandler.update(this.orientation, this.displayBox, currentDisplay); + } } destroy() { @@ -136,4 +294,8 @@ export default class AutoRotation { }); } } + + getDeviceOrientation() { + return window.screen.orientation.angle; + } } diff --git a/sdk/src/CPHCloudApp.js b/sdk/src/CPHCloudApp.js index 36792968880173eec7cd48302642b2aab2e54df5..1a6f0881809228e410476d327fbdedead6a60a00 100644 --- a/sdk/src/CPHCloudApp.js +++ b/sdk/src/CPHCloudApp.js @@ -14,7 +14,6 @@ import AppController from './AppController'; import PROTOCOL_CONFIG from './config/protocolConfig'; -import FullScreen from './Fullscreen'; import Logger from './Logger'; import NoDebugger from './NoDebugger'; @@ -106,19 +105,11 @@ class CPHCloudApp { const clientWidth = document.documentElement.clientWidth; const clientHeight = document.documentElement.clientHeight; fullscreenContainer.style.width = `${clientWidth}px`; - // 需要给操作按钮留出空间 fullscreenContainer.style.height = `${clientHeight}px`; } this.appController = CPHCloudApp.setAppController.bind(this)(); this.appController.start(); - this.fullscreen = new FullScreen({ - containerId: this.options.containerId, - autoRotate: this.appController.options.autoRotate, - isMSE: this.appController.isMSE, - isMobile: isMobile - }); - this.fullscreen.init(); window.addEventListener('unload', () => { this.exit(); }); @@ -324,9 +315,7 @@ class CPHCloudApp { exit() { this.appController.exit(); - this.fullscreen.destroy(); this.appController = null; - this.fullscreen = null; } pause() { @@ -341,15 +330,6 @@ class CPHCloudApp { this.appController.reconnect(); } - // fullscreenElementId: 全屏元素ID。不能在同一个dom上旋转和全屏,所以若自动旋转则需另提供全屏的dom id,如containerId的父级dom - fullscreenToggle(fullscreenElementId) { - this.fullscreen.fullscreenToggle(fullscreenElementId, this.appController.playerContainerId); - } - - isFullscreen() { - return this.fullscreen.isFullscreen(); - } - on(eventName, callback) { if (eventName === 'logReceived') { Logger.onLogReceived(callback); diff --git a/sdk/src/CloudApp.js b/sdk/src/CloudApp.js index a9f9d7c6ff2650e2f5065ea6305fcb8145e81cc1..013a24bb172fc1943c385cd0665a1e446fa76e9a 100644 --- a/sdk/src/CloudApp.js +++ b/sdk/src/CloudApp.js @@ -1,3 +1,16 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. import RTCCloudApp from "./rtc/RTCCloudApp"; import CPHCloudApp from "./CPHCloudApp"; import AppController from "./AppController"; diff --git a/sdk/src/DirectionHandler.js b/sdk/src/DirectionHandler.js index fa0663cac2cfb3e094a5c97d152e2f1c833c2908..a5f6e6c547e44c99168462e851bc1b9b6248666d 100644 --- a/sdk/src/DirectionHandler.js +++ b/sdk/src/DirectionHandler.js @@ -13,162 +13,603 @@ // limitations under the License. import PROTOCOL_CONFIG from './config/protocolConfig'; -import {CLOUD_PHONE_RATIO} from './config/commonConfig'; +import {CLOUD_PHONE_RATIO, DEVICE_ORIENTATION_ANGLES} from './config/commonConfig'; +import Util from './Util'; + +const normalCssText = ` + display: none; + width: 300px; + height: 200px; + position: absolute; + z-index: 999; + top: 50%; + left: 50%; + background-color: #fff; + border: 1px solid #dfe1e6; + border-radius: 5px; + transform: translate(-50%, -50%); +`; +const DOM_ORIENTATION_ANGLES = { + 'PORTRAIT': 0, + 'REVERSE_PORTRAIT': 180, + 'LANDSCAPE': -90, + 'REVERSE_LANDSCAPE': 90 +}; class DirectionHandler { constructor(options) { this.options = {...options}; + this.util = new Util(); + this.orientation = null; + this.displayBox = null; + this.touchHandler = this.options.touchHandler; + + this.containerEle = document.getElementById(this.options.containerId); + this.netWorkInfo = document.getElementById('network-info'); + this.messageModal = document.getElementById('messageModal'); + this.exitModal = document.getElementById('exitModal'); + this.buttonModal = document.getElementById('buttonModal'); + this.ctrlEle = document.getElementById('controlBtn'); + this.keyboardInputContent = document.getElementById('keyboardInputContent'); } - update(orientation, displayBox) { - const containerEle = document.getElementById(this.options.containerId); - const keyboardInputContent = document.getElementById('keyboardInputContent'); - const netWorkInfo = document.getElementById('network-info'); - const messageModal = document.getElementById('messageModal'); - const exitModal = document.getElementById('exitModal'); - const buttonModal = document.getElementById('buttonModal'); - const ctrlEle = document.getElementById('controlBtn'); + init() { + this.util.bind(window, 'orientationchange', this.patchChange.bind(this)); + } - if (orientation === PROTOCOL_CONFIG.ORIENTATION[0]) { - const normalCssText = ` - display: none; - width: 300px; - height: 200px; - position: absolute; - z-index: 999; - top: 50%; - left: 50%; - background-color: #fff; - border: 1px solid #dfe1e6; - border-radius: 5px; - transform: translate(-50%, -50%); - `; - messageModal.style.cssText = normalCssText; - exitModal.style.cssText = normalCssText; - keyboardInputContent.style.cssText = ` - display: none; - position: absolute; - bottom: 5px; - left: 0px; - height: 40px; - width: 100%; + portraitPc() { + switch (this.orientation) { + case PROTOCOL_CONFIG.ORIENTATION[0]: + let ctrlEleLeft = (this.containerEle.clientWidth - this.displayBox.width)/2 + this.ctrlEle.clientWidth/2; + let ctrlEleTop = (this.containerEle.clientHeight - this.displayBox.height)/2 + this.netWorkInfo.clientHeight * 2.5; + this.portraitAndportrait(ctrlEleLeft, ctrlEleTop); + this.keyboardInputContent.style.cssText = ` + display: ${this.currentDisplay ? this.currentDisplay : 'none'}; + position: absolute; + bottom: 5px; + left: 0px; + height: 40px; + width: 100%; `; - netWorkInfo.style.cssText = ` - pointer-events: none; - position: absolute; - top: 0px; - height: 40px; - line-height: 40px; - text-align: center; - width: 100%; - color: #fff; - `; - buttonModal.style.cssText = ` - display: none; - width: 255px; - height: 155px; - position: absolute; - z-index: 999; - top: 50%; - left: 50%; - background-color: rgba(0, 0, 0, 0.4); - border-radius: 5px; - transform: translate(-50%, -50%); - `; + break; + case PROTOCOL_CONFIG.ORIENTATION[16]: + const keyboardInputLeft = this.containerEle.clientWidth; + const keyboardInputTop = 0; + this.portraitAndReversePortrait(keyboardInputLeft, keyboardInputTop, PROTOCOL_CONFIG.ORIENTATION[16]); + break; + case PROTOCOL_CONFIG.ORIENTATION[8]: + this.portraitAndReverseLandscapePc(PROTOCOL_CONFIG.ORIENTATION[8]); + break; + case PROTOCOL_CONFIG.ORIENTATION[24]: + this.portraitAndLandscapePc(PROTOCOL_CONFIG.ORIENTATION[24]); + break; + default: + break; + } + } - if(displayBox.width && displayBox.height) { - const left = (containerEle.offsetWidth - displayBox.width)/2 + 20; - const top = (containerEle.offsetHeight - displayBox.height)/2 + 125; - if (this.options.isMobile) { - ctrlEle.style.top = `20%`; - ctrlEle.style.left = `5%`; - } else { - ctrlEle.style.top = `${top}px`; - ctrlEle.style.left = `${left}px`; - } - - ctrlEle.style.transform = 'none'; - } - } else { - // 横屏时 - const transformCssText = ` - display: none; - width: 300px; - height: 200px; - position: absolute; - z-index: 999; - top: 50%; - left: 50%; - background-color: #fff; - border: 1px solid #dfe1e6; - border-radius: 5px; - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform: rotate(90deg); - transform-origin: top; - `; - messageModal.style.cssText = transformCssText; - exitModal.style.cssText = transformCssText; - keyboardInputContent.style.cssText = ` - display: none; - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform-origin: left; - transform: rotate(90deg); - position: absolute; - top: -20px; - left: 20px; - height: 40px; - width: ${containerEle.clientHeight}px;`; - netWorkInfo.style.cssText = ` - transform-origin: left; - transform: rotate(90deg); - pointer-events: none; - position: absolute; - left: ${containerEle.clientWidth - 40}px; - height: 40px; - line-height: 40px; - text-align: center; - width: ${containerEle.clientHeight}px; - top: -40px; - color: #fff; - `; - buttonModal.style.cssText = ` - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform-origin: top; - transform: rotate(90deg); - display: none; - width: 255px; - height: 155px; - position: absolute; - z-index: 999; - top: 50%; - left: 50%; - background-color: rgba(0, 0, 0, 0.4); - border-radius: 5px; + // 手机竖屏场景,根据流方向适配 + portraitMobile() { + switch (this.orientation) { + case PROTOCOL_CONFIG.ORIENTATION[0]: + let ctrlEleLeft = this.ctrlEle.clientWidth/2; + let ctrlEleTop = this.netWorkInfo.clientHeight * 2.5; + this.portraitAndportrait(ctrlEleLeft, ctrlEleTop); + this.keyboardInputContent.style.cssText = ` + display: ${this.currentDisplay ? this.currentDisplay : 'none'}; + position: absolute; + top: 50%; + left: 0px; + height: 40px; + width: 100%; + `; + break; + case PROTOCOL_CONFIG.ORIENTATION[16]: + const keyboardInputLeft = this.containerEle.clientWidth; + const keyboardInputTop = this.containerEle.clientHeight/2 - this.keyboardInputContent.clientHeight; + this.portraitAndReversePortrait(keyboardInputLeft, keyboardInputTop, PROTOCOL_CONFIG.ORIENTATION[16]); + break; + case PROTOCOL_CONFIG.ORIENTATION[8]: + this.portraitAndReverseLandscapeMobile(PROTOCOL_CONFIG.ORIENTATION[8]); + break; + case PROTOCOL_CONFIG.ORIENTATION[24]: + this.portraitAndLandscapeMobile(PROTOCOL_CONFIG.ORIENTATION[24]); + break; + default: + break; + } + } + + setDomElementPosition(positionParams) { + const transformCssText = ` + display: none; + width: 300px; + height: 200px; + position: absolute; + z-index: 999; + top: ${positionParams.commonTop}px; + left: ${positionParams.commonLeft}px; + background-color: #fff; + border: 1px solid #dfe1e6; + border-radius: 5px; + -webkit-transform: rotate(${positionParams.transformAngle}deg); + -moz-transform: rotate(${positionParams.transformAngle}deg); + -ms-transform: rotate(${positionParams.transformAngle}deg); + -o-transform: rotate(${positionParams.transformAngle}deg); + transform: rotate(${positionParams.transformAngle}deg); + transform-origin: left top; + `; + this.messageModal.style.cssText = transformCssText; + this.exitModal.style.cssText = transformCssText; + this.netWorkInfo.style.cssText = ` + transform-origin: left; + -webkit-transform: rotate(${positionParams.transformAngle}deg); + -moz-transform: rotate(${positionParams.transformAngle}deg); + -ms-transform: rotate(${positionParams.transformAngle}deg); + -o-transform: rotate(${positionParams.transformAngle}deg); + transform: rotate(${positionParams.transformAngle}deg); + pointer-events: none; + position: absolute; + left: ${positionParams.netWorkInfoLeft}px; + top: ${positionParams.netWorkInfoTop}px; + height: 40px; + line-height: 40px; + text-align: center; + width: ${positionParams.netWorkInfoWidth}px; + color: #3AC295; + `; + this.buttonModal.style.cssText = ` + -webkit-transform: rotate(${positionParams.transformAngle}deg); + -moz-transform: rotate(${positionParams.transformAngle}deg); + -ms-transform: rotate(${positionParams.transformAngle}deg); + -o-transform: rotate(${positionParams.transformAngle}deg); + transform-origin: left top; + transform: rotate(${positionParams.transformAngle}deg); + display: none; + width: 255px; + height: 200px; + position: absolute; + z-index: 999; + top: ${positionParams.buttonModalTop}px; + left: ${positionParams.buttonModalLeft}px; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 5px; + `; + this.keyboardInputContent.style.cssText = ` + display: ${this.currentDisplay ? this.currentDisplay : 'none'}; + -webkit-transform: rotate(${positionParams.transformAngle}deg); + -moz-transform: rotate(${positionParams.transformAngle}deg); + -ms-transform: rotate(${positionParams.transformAngle}deg); + -o-transform: rotate(${positionParams.transformAngle}deg); + transform-origin: left; + transform: rotate(${positionParams.transformAngle}deg); + position: absolute; + top: ${positionParams.keyboardInputTop}px; + left: ${positionParams.keyboardInputLeft}px; + height: 40px; + width: ${positionParams.keyboardInputWidth}px; + `; + + this.ctrlEle.style.top = `${positionParams.ctrlEleTop}px`; + this.ctrlEle.style.left = `${positionParams.ctrlEleLeft}px`; + this.ctrlEle.style.transform = `rotate(${positionParams.transformAngle}deg)`; + this.ctrlEle.style.transformOrigin = 'top'; + } + + // 设备不旋转,流不旋转 + portraitAndportrait(ctrlEleLeft, ctrlEleTop) { + this.messageModal.style.cssText = normalCssText; + this.exitModal.style.cssText = normalCssText; + this.netWorkInfo.style.cssText = ` + pointer-events: none; + position: absolute; + top: 0px; + height: 40px; + line-height: 40px; + text-align: center; + width: 100%; + color: #3AC295; + `; + this.buttonModal.style.cssText = ` + display: none; + width: 255px; + height: 200px; + position: absolute; + z-index: 999; + top: 50%; + left: 50%; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 5px; + transform: translate(-50%, -50%); + `; + + this.ctrlEle.style.top = `${ctrlEleTop}px`; + this.ctrlEle.style.left = `${ctrlEleLeft}px`; + this.ctrlEle.style.transform = 'none'; + } + + // 设备不旋转,流旋转180 + portraitAndReversePortrait(keyboardInputLeft, keyboardInputTop, orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const netWorkInfoLeft = (this.containerEle.clientWidth - this.netWorkInfo.clientWidth)/2 + this.netWorkInfo.clientWidth; + const netWorkInfoTop = this.containerEle.clientHeight - this.netWorkInfo.clientHeight; + const commonLeft = (this.containerEle.clientWidth - this.displayBox.width)/2 + Number(window.getComputedStyle(this.messageModal).width.split('px')[0]) + (this.displayBox.width - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2; + const commonTop =this.containerEle.clientHeight/2 + Number(window.getComputedStyle(this.messageModal).height.split('px')[0])/2; + const buttonModalLeft = (this.containerEle.clientWidth - this.displayBox.width)/2 + Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]) + (this.displayBox.width - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2; + const buttonModalTop = this.containerEle.clientHeight/2 + Number(window.getComputedStyle(this.messageModal).height.split('px')[0])/2; + const ctrlEleTop = this.containerEle.clientHeight - this.netWorkInfo.clientHeight * 2.5; + const ctrlEleLeft = (this.containerEle.clientWidth - this.displayBox.width)/2 + this.displayBox.width - this.ctrlEle.clientWidth * 1.5; + const netWorkInfoWidth = this.containerEle.clientWidth; + const keyboardInputWidth = this.containerEle.clientWidth; + + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 设备不旋转,流旋转-90 + portraitAndReverseLandscapePc(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const netWorkInfoLeft = this.containerEle.clientWidth - this.netWorkInfo.clientHeight; + const netWorkInfoTop = -this.netWorkInfo.clientHeight/2; + const commonLeft = this.containerEle.clientWidth - Number(window.getComputedStyle(this.messageModal).height.split('px')[0]); + const commonTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2; + const buttonModalLeft = this.containerEle.clientWidth - Number(window.getComputedStyle(this.buttonModal).height.split('px')[0]); + const buttonModalTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2; + const ctrlEleTop = (this.containerEle.clientHeight - this.displayBox.width)/2 + this.netWorkInfo.clientHeight; + const ctrlEleLeft = this.containerEle.clientWidth - this.ctrlEle.clientHeight * 2.5; + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputTop = -Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = this.containerEle.clientHeight; + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 设备不旋转,流旋转-90 + portraitAndReverseLandscapeMobile(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const windowHeight = document.documentElement.clientHeight; + const netWorkInfoLeft = (windowHeight - this.containerEle.clientWidth)/2 + this.containerEle.clientWidth - this.netWorkInfo.clientHeight; + const netWorkInfoTop = -this.netWorkInfo.clientHeight/2; + const commonLeft = this.containerEle.clientWidth; + const commonTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2; + const buttonModalLeft = this.containerEle.clientWidth; + const buttonModalTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2; + const ctrlEleTop = this.ctrlEle.clientHeight; + const ctrlEleLeft = (windowHeight - this.containerEle.clientWidth)/2 + this.containerEle.clientWidth - this.netWorkInfo.clientHeight * 3.5; + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = this.containerEle.clientWidth/2 - this.keyboardInputContent.clientHeight; + const keyboardInputTop = -Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = this.containerEle.clientHeight; + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 设备不旋转,流旋转90 + portraitAndLandscapePc(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const netWorkInfoLeft = this.netWorkInfo.clientHeight; + const netWorkInfoTop = this.containerEle.clientHeight - this.netWorkInfo.clientHeight/2; + const commonLeft = (this.displayBox.height - Number(window.getComputedStyle(this.messageModal).height.split('px')[0]))/2; + const commonTop = `${(this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.messageModal).width.split('px')[0])}`; + const buttonModalTop = `${(this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.buttonModal).width.split('px')[0])}`; + const buttonModalLeft = (this.displayBox.height - Number(window.getComputedStyle(this.buttonModal).height.split('px')[0]))/2; + const ctrlEleTop = (this.containerEle.clientHeight - this.displayBox.width)/2 + this.displayBox.width - this.ctrlEle.clientHeight; + const ctrlEleLeft = this.netWorkInfo.clientHeight * 1.5; + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = this.containerEle.clientWidth - Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputTop = this.containerEle.clientHeight - Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = this.containerEle.clientHeight; + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 设备不旋转,流旋转90 + portraitAndLandscapeMobile(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const windowHeight = document.documentElement.clientHeight; + const netWorkInfoLeft = -((windowHeight - this.containerEle.clientWidth)/2 - this.netWorkInfo.clientHeight/2); + const netWorkInfoTop = this.containerEle.clientHeight - this.netWorkInfo.clientHeight/2; + const buttonModalTop = `${(this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.buttonModal).width.split('px')[0])}`; + const buttonModalLeft = 0; + const commonTop = `${(this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.messageModal).width.split('px')[0])}`; + const commonLeft = 0; + const ctrlEleTop = this.containerEle.clientHeight - this.ctrlEle.clientHeight; + const ctrlEleLeft =-((windowHeight - this.containerEle.clientWidth)/2 - this.netWorkInfo.clientHeight * 2); + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = this.containerEle.clientWidth/2 - this.keyboardInputContent.clientHeight; + const keyboardInputTop = this.containerEle.clientHeight - Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = this.containerEle.clientHeight; + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 手机横屏场景,根据流方向适配 + landscapeMobile() { + switch (this.orientation) { + case PROTOCOL_CONFIG.ORIENTATION[0]: + this.landscapeAndPortrait(); + break; + case PROTOCOL_CONFIG.ORIENTATION[8]: + this.landscapeAndReserveLandscape(PROTOCOL_CONFIG.ORIENTATION[8]); + break; + case PROTOCOL_CONFIG.ORIENTATION[24]: + this.landscapeAndLandscape(PROTOCOL_CONFIG.ORIENTATION[24]); + break; + default: + break; + } + } + + // 设备横屏,流旋转0 + landscapeAndPortrait() { + const clientWidth = document.documentElement.clientWidth; + this.messageModal.style.cssText = normalCssText; + this.exitModal.style.cssText = normalCssText; + this.netWorkInfo.style.cssText = ` + pointer-events: none; + position: absolute; + top: 0px; + left: ${-(document.documentElement.clientWidth - this.containerEle.clientWidth)/2}px; + height: 40px; + line-height: 40px; + text-align: center; + width: ${document.documentElement.clientWidth}px; + color: #3AC295; + `; + this.buttonModal.style.cssText = ` + display: none; + width: 255px; + height: 200px; + position: absolute; + z-index: 999; + top: 50%; + left: 50%; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 5px; + transform: translate(-50%, -50%); + `; + this.keyboardInputContent.style.cssText = ` + display: ${this.currentDisplay ? this.currentDisplay : 'none'}; + position: absolute; + top: 0px; + left: ${-(clientWidth - this.containerEle.clientWidth)/2}px; + height: 40px; + width: ${clientWidth}px; `; - if(displayBox.width && displayBox.height) { - const top = (containerEle.offsetHeight - displayBox.width)/2 + 40; - const left = (containerEle.offsetWidth - displayBox.height)/2 + containerEle.offsetWidth - 50; - if (this.options.isMobile) { - ctrlEle.style.top = `5%`; - ctrlEle.style.left = `80%`; - } else { - ctrlEle.style.top = `${top}px`; - ctrlEle.style.left = `${left}px`; - } - ctrlEle.style.transform = 'rotate(90deg)'; - ctrlEle.style.transformOrigin = 'top'; + const left = this.ctrlEle.clientWidth/2; + const top = this.netWorkInfo.clientHeight * 2.5; + this.ctrlEle.style.top = `${top}px`; + this.ctrlEle.style.left = `${left}px`; + this.ctrlEle.style.transform = 'none'; + } + + // 设备横屏,流旋转-90 + landscapeAndReserveLandscape(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const windowWidth = document.documentElement.clientWidth; + const windowHeight = document.documentElement.clientHeight; + const netWorkInfoLeft = (windowHeight - this.containerEle.clientWidth)/2 + this.containerEle.clientWidth - this.netWorkInfo.clientHeight/2; + const netWorkInfoTop = -this.netWorkInfo.clientHeight/2; + const ctrlEleTop = this.ctrlEle.clientHeight; + const ctrlEleLeft = (windowHeight - this.containerEle.clientWidth)/2 + this.containerEle.clientWidth - this.netWorkInfo.clientHeight * 2.5; + const commonLeft = (this.containerEle.clientWidth - Number(window.getComputedStyle(this.messageModal).height.split('px')[0]))/2 + Number(window.getComputedStyle(this.messageModal).height.split('px')[0]); + const commonTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2; + const buttonModalTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2; + const buttonModalLeft = (this.containerEle.clientWidth - Number(window.getComputedStyle(this.buttonModal).height.split('px')[0]))/2 + Number(window.getComputedStyle(this.buttonModal).height.split('px')[0]); + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = (windowHeight - this.containerEle.clientWidth)/2 + this.containerEle.clientWidth - Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0]/2); + const keyboardInputTop = -Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = windowWidth; + + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + // 设备横屏,流旋转90 + landscapeAndLandscape(orientationStr) { + const transformAngle = DOM_ORIENTATION_ANGLES[orientationStr]; + const windowWidth = document.documentElement.clientWidth; + const windowHeight = document.documentElement.clientHeight; + const netWorkInfoLeft = -((windowHeight - this.containerEle.clientWidth)/2 - this.netWorkInfo.clientHeight/2); + const netWorkInfoTop = this.containerEle.clientHeight - this.netWorkInfo.clientHeight/2; + const ctrlEleTop = this.containerEle.clientHeight - this.ctrlEle.clientHeight; + const ctrlEleLeft =-((windowHeight - this.containerEle.clientWidth)/2 - this.ctrlEle.clientWidth); + const commonLeft = (this.containerEle.clientWidth - Number(window.getComputedStyle(this.messageModal).height.split('px')[0]))/2; + const commonTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.messageModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.messageModal).width.split('px')[0]); + const buttonModalTop = (this.containerEle.clientHeight - Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]))/2 + Number(window.getComputedStyle(this.buttonModal).width.split('px')[0]); + const buttonModalLeft = (this.containerEle.clientWidth - Number(window.getComputedStyle(this.buttonModal).height.split('px')[0]))/2; + const netWorkInfoWidth = this.containerEle.clientHeight; + const keyboardInputLeft = Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputTop = windowWidth - Number(window.getComputedStyle(this.keyboardInputContent).height.split('px')[0])/2; + const keyboardInputWidth = windowWidth; + + const positionParams = { + transformAngle, + netWorkInfoLeft, + netWorkInfoTop, + commonLeft, + commonTop, + buttonModalLeft, + buttonModalTop, + ctrlEleTop, + ctrlEleLeft, + netWorkInfoWidth, + keyboardInputLeft, + keyboardInputTop, + keyboardInputWidth + }; + + this.setDomElementPosition(positionParams); + } + + update(orientation, displayBox, currentDisplay) { + this.orientation = orientation; + this.displayBox = displayBox; + this.currentDisplay = currentDisplay; + this.change(); + } + + change() { + if (!this.orientation || !this.displayBox) { + return; + } + + // 旋转前需要先取消旋转,避免叠加旋转 + this.cancelTransform(); + if (this.options.isMobile) { + const deviceOrientation = window.screen.orientation.angle; + if ([DEVICE_ORIENTATION_ANGLES.PORTRAIT,DEVICE_ORIENTATION_ANGLES.REVERSE_PORTRAIT].includes(deviceOrientation)) { + this.portraitMobile(); + } else if ([DEVICE_ORIENTATION_ANGLES.LANDSCAPE, DEVICE_ORIENTATION_ANGLES.REVERSE_LANDSCAPE].includes(deviceOrientation)) { + this.landscapeMobile(); } + } else { + this.portraitPc(); } + + } + + patchChange() { + this.displayBox = this.touchHandler.displayBox; + setTimeout(() => { + this.change(); + // 更新mobile时canvas画面 + this.changeMobileCanvas(); + }, 100); + } + + cancelTransform() { + this.messageModal.style.cssText = normalCssText; + this.exitModal.style.cssText = normalCssText; + this.netWorkInfo.style.cssText = ` + pointer-events: none; + position: absolute; + top: 0px; + height: 40px; + line-height: 40px; + text-align: center; + width: 100%; + color: #3AC295; + `; + this.buttonModal.style.cssText = ` + display: none; + width: 255px; + height: 200px; + position: absolute; + z-index: 999; + top: 50%; + left: 50%; + background-color: rgba(0, 0, 0, 0.4); + border-radius: 5px; + transform: translate(-50%, -50%); + `; + + this.keyboardInputContent.style.cssText = ` + display: none; + position: absolute; + bottom: 5px; + left: 0px; + height: 40px; + width: 100%; + `; + + const left = (this.containerEle.clientWidth - this.displayBox.width)/2 + this.ctrlEle.clientWidth/2; + const top = (this.containerEle.clientHeight - this.displayBox.height)/2 + this.netWorkInfo.clientHeight * 2.5; + this.ctrlEle.style.top = `${top}px`; + this.ctrlEle.style.left = `${left}px`; + this.ctrlEle.style.transform = 'none'; } updateCanvasSize(orientation, isMSE) { @@ -207,6 +648,53 @@ class DirectionHandler { canvasElement.width = playerWidth; canvasElement.height = playerHeight; } + + updateMobileCanvasSize(orientation, isMSE) { + this.orientation = orientation; + this.isMSE = isMSE; + + this.changeMobileCanvas(); + } + + changeMobileCanvas() { + if (!this.orientation || this.isMSE) { + return; + } + + const playContainerEle = document.getElementById(`${this.options.playerContainerId}`); + playContainerEle.style.display = 'inline-block'; + let canvasElement = playContainerEle.shadowRoot.querySelector('#playCanvas'); + let playerHeight = null; + let playerWidth = null; + + setTimeout(() => { + let width = document.documentElement.clientWidth; + let height = document.documentElement.clientHeight; + const deviceOrientation = window.screen.orientation.angle; + if ([DEVICE_ORIENTATION_ANGLES.PORTRAIT,DEVICE_ORIENTATION_ANGLES.REVERSE_PORTRAIT].includes(deviceOrientation)) { + if ([PROTOCOL_CONFIG.ORIENTATION[0], PROTOCOL_CONFIG.ORIENTATION[16]].includes(this.orientation)) { + playerWidth = width; + playerHeight = height; + } else if ([PROTOCOL_CONFIG.ORIENTATION[8], PROTOCOL_CONFIG.ORIENTATION[24]].includes(this.orientation)) { + const newWidth = width; + const newHeight = newWidth * CLOUD_PHONE_RATIO; + playerWidth = newHeight; + playerHeight = newWidth; + } + } else if([DEVICE_ORIENTATION_ANGLES.LANDSCAPE, DEVICE_ORIENTATION_ANGLES.REVERSE_LANDSCAPE].includes(deviceOrientation)) { + if ([PROTOCOL_CONFIG.ORIENTATION[0], PROTOCOL_CONFIG.ORIENTATION[16]].includes(this.orientation)) { + playerWidth = height * CLOUD_PHONE_RATIO; + playerHeight = height; + } else if ([PROTOCOL_CONFIG.ORIENTATION[8], PROTOCOL_CONFIG.ORIENTATION[24]].includes(this.orientation)) { + playerWidth = height; + playerHeight = width; + } + } + + canvasElement.width = playerWidth; + canvasElement.height = playerHeight; + }, 200); + } } export default DirectionHandler; \ No newline at end of file diff --git a/sdk/src/Fullscreen.js b/sdk/src/Fullscreen.js index aafe433d3afe7531ccb7763cdb9ddb74f522f1d9..198395dd67936fd781f4da90e5ea0902eef1ab27 100644 --- a/sdk/src/Fullscreen.js +++ b/sdk/src/Fullscreen.js @@ -75,15 +75,18 @@ export default class FullScreen { || document.webkitFullscreenElement); Logger.debug('inFullscreenStatus = ', this.inFullscreenStatus); - this.updateContainerSize(); - // 按下F11键进入全屏或退出全屏,不会触发resize事件,强制触发,否则触控点错位 - window.dispatchEvent && window.dispatchEvent(new Event('resize')); + window.dispatchEvent && window.dispatchEvent(new Event('resize')); + + setTimeout(() => { + this.updateContainerSize(); + }, 120); } - fullscreenToggle(fullscreenElementId, playerContainerId) { + fullscreenToggle(fullscreenElementId, playerContainerId, isMSE) { this.playerContainerId = playerContainerId; this.fullscreenElementId = fullscreenElementId; + this.isMSE = isMSE; if (!fullscreenElementId && this.options.autoRotate) { Logger.debug('Can not use fullscreenToggle API success, because fullscreenElementId is undefined but auto_rotate is true'); @@ -114,15 +117,18 @@ export default class FullScreen { let canvasElement = playContainer.shadowRoot.querySelector('#playCanvas'); let playerWidth = clientHeight * CLOUD_PHONE_RATIO; let playerHeight = clientHeight; + if (this.options.isMobile) { + playerWidth = clientWidth; + playerHeight = clientHeight; + } + canvasElement.width = playerWidth; canvasElement.height = playerHeight; } updateContainerSize() { - // 根据fullscreen-container更新container的大小 - const fullscreenEle = document.getElementById(`${this.fullscreenElementId}`); - const fullWidth = fullscreenEle.clientWidth; - const fullHeight = fullscreenEle.clientHeight; + const fullWidth = window.innerWidth; + const fullHeight = window.innerHeight; const containerEle = document.getElementById('container'); containerEle.style.cssText = ` width: ${fullWidth}; @@ -130,8 +136,15 @@ export default class FullScreen { `; // canvas无法使用百分比设置大小,故全屏和取消全屏需要手动设置宽高 - if (!this.options.isMSE) { + if (!this.isMSE) { this.updateCanvasSize(fullWidth, fullHeight); } + + const fullscreenBtn = document.getElementById('toggleFullscreen'); + if (this.inFullscreenStatus) { + fullscreenBtn.innerHTML = '取消全屏'; + } else { + fullscreenBtn.innerHTML = '全屏'; + } } } diff --git a/sdk/src/TouchHandler.js b/sdk/src/TouchHandler.js index c07eea6cbbfc5c467d1ddc116177025de89f920e..545ba3559e7f4829b3a93afa49c8620bd5b1c4b0 100644 --- a/sdk/src/TouchHandler.js +++ b/sdk/src/TouchHandler.js @@ -14,10 +14,13 @@ import PROTOCOL_CONFIG from './config/protocolConfig'; import Util from './Util'; +import {DEVICE_ORIENTATION_ANGLES} from './config/commonConfig'; // 屏幕刷新频率一般为60fps const MOVE_THROTTLE_DELAY = 16.6; const ORIENTATION_PORTRAIT = 'PORTRAIT'; +const ORIENTATION_REVERSE_PORTRAIT = 'REVERSE_PORTRAIT'; +const ORIENTATION_LANDSCAPE = 'LANDSCAPE'; const ORIENTATION_REVERSE_LANDSCAPE = 'REVERSE_LANDSCAPE'; const KEYBOARD_ID_PREFIX = 'phoenix_keyboard_'; const ORIENTATION_DEGRESS = { @@ -28,10 +31,16 @@ const ORIENTATION_DEGRESS = { }; const ORIENTATION_ORIGIN = { 'PORTRAIT': 0, - 'LANDSCAPE': 1, + 'LANDSCAPE': 3, 'REVERSE_LANDSCAPE': 3, 'REVERSE_PORTRAIT': 2 }; +const ORIENTATION_ORIGIN_MOBILE = { + 'PORTRAIT': 0, + 'LANDSCAPE': 3, + 'REVERSE_LANDSCAPE': 1, + 'REVERSE_PORTRAIT': 2 +}; const KEYBOARD_MODE = { 'KEYBOARD_MAP': 'KEYBOARD_MAP', 'KEYBOARD_INPUT': 'KEYBOARD_INPUT' @@ -286,7 +295,7 @@ export default class TouchHandler { isSameOrientation() { if (!this.isMobile) { - return this.orientation !== ORIENTATION_REVERSE_LANDSCAPE; + return !['LANDSCAPE', ORIENTATION_REVERSE_LANDSCAPE].includes(this.orientation); } // 若云手机画面横屏,当前认为云手机方向为逆时针旋转90度。 @@ -447,14 +456,20 @@ export default class TouchHandler { if (touch.x > this.displayBox.width || touch.x < 0 || touch.y < 0 || touch.y > this.displayBox.height) { return; } - let oritenation = this.isMobile && this.orientation === ORIENTATION_REVERSE_LANDSCAPE ? 0 : ORIENTATION_ORIGIN[this.orientation]; + + let finalOrientation; + if (this.isMobile) { + finalOrientation = ORIENTATION_ORIGIN_MOBILE[this.orientation]; + } else { + finalOrientation = ORIENTATION_ORIGIN[this.orientation]; + } const msg = { ...touch, action: PROTOCOL_CONFIG.ACTIONS_TYPE[action], id: touch.vId, pressure: 129, time: -1, - orientation: oritenation, + orientation: finalOrientation, height: this.displayBox.height, width: this.displayBox.width }; diff --git a/sdk/src/config/commonConfig.js b/sdk/src/config/commonConfig.js index 4542682a24a64adfed1e624869805eb675d172af..ad96910ad1c5a9b4a18abb5f079c331d26f8210c 100644 --- a/sdk/src/config/commonConfig.js +++ b/sdk/src/config/commonConfig.js @@ -43,4 +43,11 @@ export const MIC_START_RECORD_RSP_LEN = MEDIA_MSG_HEADER_COUNT + 4; // pc场景摄像头最大高度 export const PC_CAMERA_MAX_HEIGHT = 720; // 默认画质 -export const DEFAULT_DEFINITION = 'HD'; \ No newline at end of file +export const DEFAULT_DEFINITION = 'HD'; +// 设备旋转角度 +export const DEVICE_ORIENTATION_ANGLES = { + 'PORTRAIT': 0, + 'REVERSE_PORTRAIT': 180, + 'LANDSCAPE': 90, + 'REVERSE_LANDSCAPE': 270 +}; diff --git a/sdk/src/config/protocolConfig.js b/sdk/src/config/protocolConfig.js index 05b1e781ad1f4354941815b8858d78380516e242..e344a77d3f27bfc2ae0d05c00908b627c05cc8e9 100644 --- a/sdk/src/config/protocolConfig.js +++ b/sdk/src/config/protocolConfig.js @@ -103,7 +103,9 @@ const PROTOCOL_CONFIG = { ORIENTATION: { 0: 'PORTRAIT', 1: 'LANDSCAPE', // 从竖屏逆时针旋转90度 - 8: 'REVERSE_LANDSCAPE' + 8: 'REVERSE_LANDSCAPE', + 24: 'LANDSCAPE', + 16: 'REVERSE_PORTRAIT' }, RESOLUTIONS: { DISPLAY_480P: {