Camera now show up when someone is moving and hides 5 seconds after we stop moving.

Also, added an animation to show/hide the webcam.
This commit is contained in:
David Négrier 2021-05-20 18:05:03 +02:00
parent 8af8ccd54b
commit d32df13f1b
6 changed files with 146 additions and 10 deletions

View File

@ -73,7 +73,6 @@
<img id="microphone-close" src="resources/logos/microphone-close.svg">
</div>
</div>
</div>
</div>
<div id="cowebsite" class="cowebsite hidden">
@ -108,7 +107,7 @@
</div>
</div>
<div class="audioplayer">
<label id="label-audioplayer_decrease_while_talking" for="audiooplayer_decrease_while_talking" title="decrease background volume by 50% when entering conversations">
<label id="label-audioplayer_decrease_while_talking" for="audioplayer_decrease_while_talking" title="decrease background volume by 50% when entering conversations">
reduce in conversations
<input type="checkbox" id="audioplayer_decrease_while_talking" checked />
</label>

View File

@ -2,6 +2,7 @@ import {PlayerAnimationDirections} from "./Animation";
import type {GameScene} from "../Game/GameScene";
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
import {Character} from "../Entity/Character";
import {userMovingStore} from "../../Stores/GameStore";
import {RadialMenu, RadialMenuClickEvent, RadialMenuItem} from "../Components/RadialMenu";
export const hasMovedEventName = "hasMoved";
@ -86,6 +87,7 @@ export class Player extends Character {
this.previousDirection = direction;
}
this.wasMoving = moving;
userMovingStore.set(moving);
}
public isMoving(): boolean {
@ -99,7 +101,7 @@ export class Player extends Character {
this.openEmoteMenu(emotes);
}
}
isClickable(): boolean {
return true;
}
@ -113,13 +115,13 @@ export class Player extends Character {
this.playEmote(item.name);
});
}
closeEmoteMenu(): void {
if (!this.emoteMenu) return;
this.emoteMenu.destroy();
this.emoteMenu = null;
}
destroy() {
this.scene.events.removeListener('postupdate', this.updateListener);
super.destroy();

View File

@ -0,0 +1,3 @@
import { derived, writable, Writable } from "svelte/store";
export const userMovingStore = writable(false);

View File

@ -2,6 +2,8 @@ import {derived, get, Readable, readable, writable, Writable} from "svelte/store
import {peerStore} from "./PeerStore";
import {localUserStore} from "../Connexion/LocalUserStore";
import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap";
import {userMovingStore} from "./GameStore";
import {HtmlUtils} from "../WebRtc/HtmlUtils";
/**
* A store that contains the camera state requested by the user (on or off).
@ -110,6 +112,108 @@ function createPrivacyShutdownStore() {
export const privacyShutdownStore = createPrivacyShutdownStore();
/**
* A store containing whether the webcam was enabled in the last 10 seconds
*/
const enabledWebCam10secondsAgoStore = readable(false, function start(set) {
let timeout: NodeJS.Timeout|null = null;
const unsubscribe = requestedCameraState.subscribe((enabled) => {
if (enabled === true) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
set(false);
}, 10000);
set(true);
} else {
set(false);
}
})
return function stop() {
unsubscribe();
};
});
/**
* A store containing whether the webcam was enabled in the last 5 seconds
*/
const userMoved5SecondsAgoStore = readable(false, function start(set) {
let timeout: NodeJS.Timeout|null = null;
const unsubscribe = userMovingStore.subscribe((moving) => {
if (moving === true) {
if (timeout) {
clearTimeout(timeout);
}
set(true);
} else {
timeout = setTimeout(() => {
set(false);
}, 5000);
}
})
return function stop() {
unsubscribe();
};
});
/**
* A store containing whether the current page is visible or not.
*/
const mouseInBottomRight = readable(false, function start(set) {
let lastInBottomRight = false;
const gameDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('game');
const detectInBottomRight = (event: MouseEvent) => {
// TODO: not relative to window but to canvas!!!
// use phaser?
const rect = gameDiv.getBoundingClientRect();
const inBottomRight = event.x - rect.left > rect.width * 3 / 4 && event.y - rect.top > rect.height * 3 / 4;
if (inBottomRight !== lastInBottomRight) {
lastInBottomRight = inBottomRight;
set(inBottomRight);
}
};
document.addEventListener('mousemove', detectInBottomRight);
return function stop() {
document.removeEventListener('mousemove', detectInBottomRight);
}
/*const mouseEnter = () => {
set(true);
console.log('enter')
}
const mouseLeave = () => set(false);
const bottomLeftZone = HtmlUtils.getElementByIdOrFail('bottom-left-zone');
bottomLeftZone.addEventListener('mouseenter', mouseEnter);
bottomLeftZone.addEventListener('mouseleave', mouseLeave);
return function stop() {
bottomLeftZone.removeEventListener('mouseenter', mouseEnter);
bottomLeftZone.removeEventListener('mouseleave', mouseLeave);
};*/
});
/**
* A store that contains "true" if the webcam should be stopped for energy efficiency reason - i.e. we are not moving and not in a conversation.
*/
export const cameraEnergySavingStore = derived([userMoved5SecondsAgoStore, peerStore, enabledWebCam10secondsAgoStore, mouseInBottomRight], ([$userMoved5SecondsAgoStore,$peerStore, $enabledWebCam10secondsAgoStore, $mouseInBottomRight]) => {
// TODO: enable when mouse is hovering near the webcam
// TODO: add animation to show/hide webcam
return !$mouseInBottomRight && !$userMoved5SecondsAgoStore && $peerStore.size === 0 && !$enabledWebCam10secondsAgoStore;
});
/**
* A store that contains video constraints.
*/
@ -192,6 +296,7 @@ export const mediaStreamConstraintsStore = derived(
videoConstraintStore,
audioConstraintStore,
privacyShutdownStore,
cameraEnergySavingStore,
], (
[
$requestedCameraState,
@ -201,6 +306,7 @@ export const mediaStreamConstraintsStore = derived(
$videoConstraintStore,
$audioConstraintStore,
$privacyShutdownStore,
$cameraEnergySavingStore,
], set
) => {
@ -236,6 +342,12 @@ export const mediaStreamConstraintsStore = derived(
currentVideoConstraint = false;
}
// Disable webcam for energy reasons (the user is not moving and we are talking to noone)
if ($cameraEnergySavingStore === true) {
currentVideoConstraint = false;
currentAudioConstraint = false;
}
// Let's make the changes only if the new value is different from the old one.
if (previousComputedVideoConstraint != currentVideoConstraint || previousComputedAudioConstraint != currentAudioConstraint) {
previousComputedVideoConstraint = currentVideoConstraint;
@ -415,3 +527,4 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStreamStore) => {
return $localStreamStore.constraints;
});

View File

@ -142,15 +142,15 @@ export class MediaManager {
}
if (result.constraints.video !== false) {
this.enableCameraStyle();
HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.remove('hide');
} else {
this.disableCameraStyle();
}
HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.add('hide');
}/*
if (result.constraints.audio !== false) {
this.enableMicrophoneStyle();
} else {
this.disableMicrophoneStyle();
}
}*/
this.localStream = result.stream;
this.myCamVideo.srcObject = this.localStream;
@ -158,6 +158,21 @@ export class MediaManager {
// TODO: migrate all listeners to the store directly.
this.triggerUpdatedLocalStreamCallbacks(result.stream);
});
requestedCameraState.subscribe((enabled) => {
if (enabled) {
this.enableCameraStyle();
} else {
this.disableCameraStyle();
}
});
requestedMicrophoneState.subscribe((enabled) => {
if (enabled) {
this.enableMicrophoneStyle();
} else {
this.disableMicrophoneStyle();
}
});
}
public updateScene(){

View File

@ -143,6 +143,11 @@ body .message-info.warning{
bottom: 30px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
}
#div-myCamVideo.hide {
right: -20vw;
}
video#myCamVideo{
@ -216,7 +221,6 @@ video#myCamVideo{
border-radius: 48px;
transform: translateY(20px);
transition-timing-function: ease-in-out;
margin-bottom: 20px;
margin: 0 4%;
}
.btn-cam-action div.disabled {