Adding CoWebsiteManager + first working version of flex video

This commit is contained in:
David Négrier 2020-08-13 18:21:48 +02:00
parent 83fe024c45
commit 9f6c6e0ce1
6 changed files with 326 additions and 51 deletions

52
front/dist/index.html vendored
View File

@ -39,7 +39,53 @@
<title>WorkAdventure</title> <title>WorkAdventure</title>
</head> </head>
<body id="body" style="margin: 0"> <body id="body" style="margin: 0">
<div id="webRtc" class="webrtc"> <div class="main-container">
<div id="game" class="game" style="/*background: red;*/">
<div id="game-overlay" class="game-overlay" style="/*background: violet*/;">
<div id="main-section" class="main-section">
<!--<div style="background: lightpink;">a</div>
<div style="background: lightpink;">a</div> -->
</div>
<aside id="sidebar" class="sidebar">
<!--<div style="background: lightgreen;">a</div>
<div style="background: green;">b</div>
<div style="background: darkgreen;">c</div>
<div style="background: darkgreen;">d</div>-->
</aside>
<div id="chat-mode" class="chat-mode three-col" style="display: none;">
<!--<div style="background: lightgreen;">a</div>
<div style="background: green;">b</div>
<div style="background: darkgreen;">c</div>
<div style="background: darkolivegreen;">d</div>
<div style="background: darkolivegreen;">d</div>
<div style="background: darkgreen;">c</div>
<div style="background: green;">b</div>
<div style="background: lightgreen;">last elem for game</div>-->
</div>
<div id="activeCam" class="activeCam">
<div id="div-myCamVideo" class="video-container">
<video id="myCamVideo" autoplay muted></video>
</div>
<div class="btn-cam-action">
<div class="btn-micro">
<img id="microphone" src="resources/logos/microphone.svg">
<img id="microphone-close" src="resources/logos/microphone-close.svg">
</div>
<div class="btn-video">
<img id="cinema" src="resources/logos/cinema.svg">
<img id="cinema-close" src="resources/logos/cinema-close.svg">
</div>
</div>
</div>
</div>
</div>
<div id="cowebsite" class="cowebsite"></div>
</div>
<!--
<div id="webRtc" class="webrtc">
<div id="activeCam" class="activeCam"> <div id="activeCam" class="activeCam">
<div id="div-myCamVideo" class="video-container"> <div id="div-myCamVideo" class="video-container">
<video id="myCamVideo" autoplay muted></video> <video id="myCamVideo" autoplay muted></video>
@ -54,11 +100,9 @@
<img id="cinema" src="resources/logos/cinema.svg"> <img id="cinema" src="resources/logos/cinema.svg">
<img id="cinema-close" src="resources/logos/cinema-close.svg"> <img id="cinema-close" src="resources/logos/cinema-close.svg">
</div> </div>
<!--<div class="btn-call">
<img src="resources/logos/phone.svg">
</div>-->
</div> </div>
</div> </div>
-->
<div id="webRtcSetup" class="webrtcsetup"> <div id="webRtcSetup" class="webrtcsetup">
<img id="webRtcSetupNoVideo" class="background-img" src="resources/logos/cinema-close.svg"> <img id="webRtcSetupNoVideo" class="background-img" src="resources/logos/cinema-close.svg">
<video id="myCamVideoSetup" autoplay muted></video> <video id="myCamVideoSetup" autoplay muted></video>

View File

@ -27,7 +27,7 @@ video{
-webkit-transform: scaleX(-1); -webkit-transform: scaleX(-1);
transform: scaleX(-1); transform: scaleX(-1);
} }
.webrtc{ /*.webrtc{
display: none; display: none;
position: absolute; position: absolute;
right: 0px; right: 0px;
@ -36,21 +36,22 @@ video{
} }
.webrtc.active{ .webrtc.active{
display: block; display: block;
} }*/
.webrtc, .activeCam{} /*.webrtc, .activeCam{}*/
.activeCam .video-container{ /*.activeCam*/ .video-container{
position: absolute; position: relative;
height: 25%; /*height: 25%;
top: 10px; top: 10px;
margin: 5px; margin: 5px;
right: -100px; right: -100px;
transition: all 0.2s ease; transition: all 0.2s ease;*/
border-color: black; /*border-color: black;
border-style: solid; border-style: solid;
border-width: 0.2px; border-width: 0.2px;*/
background-color: #00000099;
} }
.activeCam .video-container i{ /*.activeCam*/ .video-container i{
position: absolute; position: absolute;
width: 100px; width: 100px;
height: 65px; height: 65px;
@ -63,10 +64,10 @@ video{
font-size: 28px; font-size: 28px;
color: white; color: white;
} }
.activeCam .video-container img.active{ /*.activeCam*/ .video-container img.active{
display: block; display: block;
} }
.activeCam .video-container img{ /*.activeCam*/ .video-container img{
position: absolute; position: absolute;
display: none; display: none;
width: 15px; width: 15px;
@ -78,34 +79,28 @@ video{
padding: 10px; padding: 10px;
z-index: 2; z-index: 2;
} }
.activeCam .video-container video{ /*.activeCam*/ .video-container video{
height: 100%; height: 100%;
} }
.webrtc:hover .activeCam .video-container{ /*.webrtc:hover .activeCam .video-container{
right: 10px; right: 10px;
} }*/
.activeCam .video-container#div-myCamVideo{ /*.activeCam*/ .video-container#div-myCamVideo{
border: none; border: none;
} }
.activeCam .video-container video#myCamVideo{ /*.activeCam*/
width: 200px;
height: 113px; #div-myCamVideo {
position: fixed;
right: 0;
bottom: 0;
} }
/*CSS size for 2 - 3 elements*/ video#myCamVideo{
.activeCam .video-container:nth-child(1){ width: 15vw;
/*this is for camera of user*/ /*width: 200px;*/
top: 75%; /*height: 113px;*/
}
.activeCam .video-container:nth-child(2){
top: 0%;
}
.activeCam .video-container:nth-child(3){
top: 25%;
}
.activeCam .video-container:nth-child(4) {
top: 50%;
} }
/*btn animation*/ /*btn animation*/
@ -122,7 +117,7 @@ video{
transition-timing-function: ease-in-out; transition-timing-function: ease-in-out;
bottom: 20px; bottom: 20px;
} }
.webrtc:hover .btn-cam-action div{ #activeCam:hover .btn-cam-action div{
transform: translateY(0); transform: translateY(0);
} }
.btn-cam-action div:hover{ .btn-cam-action div:hover{
@ -237,3 +232,138 @@ video{
.webrtcsetup.active{ .webrtcsetup.active{
display: block; display: block;
} }
/* New layout */
body {
margin: 0;
height: 100vh;
width: 100vw;
}
.main-container {
height: 100vh;
width: 100vw;
display: flex;
align-items: stretch;
}
@media (min-aspect-ratio: 1/1) {
.main-container {
flex-direction: row
}
.game-overlay {
flex-direction: row
}
.sidebar {
flex-direction: column
}
}
@media (max-aspect-ratio: 1/1) {
.main-container {
flex-direction: column
}
.game-overlay {
flex-direction: column
}
.sidebar {
flex-direction: row
}
}
.game {
flex-basis: 100%;
position: relative; /* Position relative is needed for the game-overlay. */
}
/* A potentially shared website could appear in an iframe in the cowebsite space. */
.cowebsite {
flex-basis: 100%;
transition: flex-basis 0.5s;
}
/*.cowebsite:hover {
flex-basis: 100%;
}*/
.cowebsite iframe {
width: 100%;
height: 100%;
}
.game-overlay {
display: none;
position: absolute;
width: 100%;
height: 100%;
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
}
.game-overlay.active {
display: flex;
}
.game-overlay video {
width: 100%
}
.main-section {
flex: 0 0 75%;
display: flex;
justify-content: start;
/*align-items: flex-start;*/
flex-wrap: wrap;
}
.main-section div {
margin: 5%;
flex-basis: 90%;
/*flex-shrink: 2;*/
}
.sidebar {
flex: 0 0 25%;
display: flex;
}
.sidebar > div {
height: 15%;
margin: 5%;
}
.chat-mode {
display: flex;
width: 100%;
flex-wrap: wrap;
padding: 1%;
}
.chat-mode div {
margin: 1%;
}
.chat-mode.one-col div {
flex-basis: 98%;
}
.chat-mode.two-col div {
flex-basis: 48%;
}
.chat-mode.three-col div {
flex-basis: 31.333333%;
}
.chat-mode.four-col div {
flex-basis: 23%;
}
.chat-mode div:last-child {
flex-grow: 5;
}

View File

@ -0,0 +1,56 @@
import {HtmlUtils} from "./HtmlUtils";
export type CoWebsiteStateChangedCallback = () => void;
export class CoWebsiteManager {
private static observers = new Array<CoWebsiteStateChangedCallback>();
public static loadCoWebsite(url: string): void {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite");
cowebsiteDiv.innerHTML = '';
const iframe = document.createElement('iframe');
iframe.id = 'cowebsite-iframe';
iframe.src = url;
cowebsiteDiv.appendChild(iframe);
CoWebsiteManager.fire();
}
public static closeCoWebsite(): void {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite");
cowebsiteDiv.innerHTML = '';
CoWebsiteManager.fire();
}
public static getGameSize(): {width: number, height: number} {
const iframe = document.getElementById('cowebsite-iframe');
if (iframe === null) {
return {
width: window.innerWidth,
height: window.innerHeight
}
}
if (window.innerWidth >= window.innerHeight) {
return {
width: window.innerWidth / 2,
height: window.innerHeight
}
} else {
return {
width: window.innerWidth,
height: window.innerHeight / 2
}
}
}
public static onStateChange(observer: CoWebsiteStateChangedCallback) {
CoWebsiteManager.observers.push(observer);
}
private static fire(): void {
for (const callback of CoWebsiteManager.observers) {
callback();
}
}
}

View File

@ -18,7 +18,7 @@ export enum DivImportance {
* This class is in charge of the video-conference layout. * This class is in charge of the video-conference layout.
* It receives positioning requests for videos and does its best to place them on the screen depending on the active layout mode. * It receives positioning requests for videos and does its best to place them on the screen depending on the active layout mode.
*/ */
export class LayoutManager { class LayoutManager {
private mode: LayoutMode = LayoutMode.Presentation; private mode: LayoutMode = LayoutMode.Presentation;
private importantDivs: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>(); private importantDivs: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
@ -26,7 +26,7 @@ export class LayoutManager {
public add(importance: DivImportance, userId: string, html: string): void { public add(importance: DivImportance, userId: string, html: string): void {
const div = document.createElement('div'); const div = document.createElement('div');
div.append(html); div.innerHTML = html;
div.id = "user-"+userId; div.id = "user-"+userId;
if (importance === DivImportance.Important) { if (importance === DivImportance.Important) {
@ -65,6 +65,7 @@ export class LayoutManager {
* Removes the DIV matching userId. * Removes the DIV matching userId.
*/ */
public remove(userId: string): void { public remove(userId: string): void {
console.log('Removing video for userID '+userId+'.');
let div = this.importantDivs.get(userId); let div = this.importantDivs.get(userId);
if (div !== undefined) { if (div !== undefined) {
div.remove(); div.remove();
@ -81,7 +82,8 @@ export class LayoutManager {
return; return;
} }
throw new Error('Could not find user ID "'+userId+'"'); console.log('Cannot remove userID '+userId+'. Already removed?');
//throw new Error('Could not find user ID "'+userId+'"');
} }
private adjustVideoChatClass(): void { private adjustVideoChatClass(): void {
@ -104,6 +106,16 @@ export class LayoutManager {
private switchLayoutMode(layoutMode: LayoutMode) { private switchLayoutMode(layoutMode: LayoutMode) {
this.mode = layoutMode; this.mode = layoutMode;
if (layoutMode === LayoutMode.Presentation) {
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('sidebar').style.display = 'block';
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-section').style.display = 'block';
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('chat-mode').style.display = 'none';
} else {
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('sidebar').style.display = 'none';
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-section').style.display = 'none';
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('chat-mode').style.display = 'block';
}
for (let div of this.importantDivs.values()) { for (let div of this.importantDivs.values()) {
this.positionDiv(div, DivImportance.Important); this.positionDiv(div, DivImportance.Important);
} }
@ -112,3 +124,7 @@ export class LayoutManager {
} }
} }
} }
const layoutManager = new LayoutManager();
export { layoutManager };

View File

@ -1,3 +1,5 @@
import {DivImportance, layoutManager} from "./LayoutManager";
const videoConstraint: boolean|MediaTrackConstraints = { const videoConstraint: boolean|MediaTrackConstraints = {
width: { ideal: 1280 }, width: { ideal: 1280 },
height: { ideal: 720 }, height: { ideal: 720 },
@ -73,8 +75,8 @@ export class MediaManager {
} }
activeVisio(){ activeVisio(){
const webRtc = this.getElementByIdOrFail('webRtc'); const gameOverlay = this.getElementByIdOrFail('game-overlay');
webRtc.classList.add('active'); gameOverlay.classList.add('active');
} }
enabledCamera() { enabledCamera() {
@ -184,10 +186,11 @@ export class MediaManager {
*/ */
addActiveVideo(userId : string, userName: string = ""){ addActiveVideo(userId : string, userName: string = ""){
this.webrtcInAudio.play(); this.webrtcInAudio.play();
const elementRemoteVideo = this.getElementByIdOrFail("activeCam");
//const elementRemoteVideo = this.getElementByIdOrFail("activeCam");
userName = userName.toUpperCase(); userName = userName.toUpperCase();
const color = this.getColorByString(userName); const color = this.getColorByString(userName);
elementRemoteVideo.insertAdjacentHTML('beforeend', ` /*elementRemoteVideo.insertAdjacentHTML('beforeend', `
<div id="div-${userId}" class="video-container" style="border-color: ${color};"> <div id="div-${userId}" class="video-container" style="border-color: ${color};">
<div class="connecting-spinner"></div> <div class="connecting-spinner"></div>
<div class="rtc-error" style="display: none"></div> <div class="rtc-error" style="display: none"></div>
@ -195,7 +198,20 @@ export class MediaManager {
<img id="microphone-${userId}" src="resources/logos/microphone-close.svg"> <img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
<video id="${userId}" autoplay></video> <video id="${userId}" autoplay></video>
</div> </div>
`); `);*/
const html = `
<div id="div-${userId}" class="video-container">
<div class="connecting-spinner"></div>
<div class="rtc-error" style="display: none"></div>
<i style="background-color: ${color};">${userName}</i>
<img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
<video id="${userId}" autoplay></video>
</div>
`;
layoutManager.add(DivImportance.Normal, userId, html);
this.remoteVideo.set(userId, this.getElementByIdOrFail<HTMLVideoElement>(userId)); this.remoteVideo.set(userId, this.getElementByIdOrFail<HTMLVideoElement>(userId));
} }
@ -274,11 +290,12 @@ export class MediaManager {
* @param userId * @param userId
*/ */
removeActiveVideo(userId : string){ removeActiveVideo(userId : string){
const element = document.getElementById(`div-${userId}`); /*const element = document.getElementById(`div-${userId}`);
if(!element){ if(!element){
return; return;
} }
element.remove(); element.remove();*/
layoutManager.remove(userId);
this.remoteVideo.delete(userId); this.remoteVideo.delete(userId);
} }

View File

@ -4,16 +4,21 @@ import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable";
import {cypressAsserter} from "./Cypress/CypressAsserter"; import {cypressAsserter} from "./Cypress/CypressAsserter";
import {LoginScene} from "./Phaser/Login/LoginScene"; import {LoginScene} from "./Phaser/Login/LoginScene";
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
import {gameManager} from "./Phaser/Game/GameManager";
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene"; import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
import {HtmlUtils} from "./WebRtc/HtmlUtils";
import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager";
//CoWebsiteManager.loadCoWebsite('https://thecodingmachine.com');
const {width, height} = CoWebsiteManager.getGameSize();
const config: GameConfig = { const config: GameConfig = {
title: "WorkAdventure", title: "WorkAdventure",
width: window.innerWidth / RESOLUTION, width: width / RESOLUTION,
height: window.innerHeight / RESOLUTION, height: height / RESOLUTION,
parent: "game", parent: "game",
scene: [LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene], scene: [LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene],
zoom: RESOLUTION, zoom: RESOLUTION,
@ -30,5 +35,12 @@ cypressAsserter.gameStarted();
const game = new Phaser.Game(config); const game = new Phaser.Game(config);
window.addEventListener('resize', function (event) { window.addEventListener('resize', function (event) {
game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION); const {width, height} = CoWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
});
CoWebsiteManager.onStateChange(() => {
const {width, height} = CoWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
}); });