WebController - User Interactions

This section contains all the methods used for the User interactions in the index.html web page. All the key-press functions and click functions are then available in this section.

onKeyDown

onKeyDown(event)

The onKeyDown method triggers a specific function depending on which key is pressed while the window is selected.

changeDelayOffset
Changes the video delay offset by changing the value.
toggleGenvidOverlay
Displays or hides the Genvid overlay.
toggleFullscreen
Toggles the fullscreen on and off.
genvidClient.videoPlayer.isPaused
Returns a boolean indicating if the video is paused.
genvidClient.videoPlayer.play
Plays the video.
genvidClient.videoPlayer.pause
Pauses the video.
genvidClient.videoPlayer.getMuted
Returns a boolean indicating if the video sound is muted.
genvidClient.videoPlayer.setMuted
Sets the sound to mute or unmute depending of the argument.
genvidClient.videoPlayer.setVolume
Sets the video volume to the sent argument.
genvidClient.videoPlayer.getVolume
Gets the current video volume.
onHelpActivation
Displays or hides the help overlay.
  onKeyDown(event) {
    // Seems to be the most cross-platform way to determine keys:
    // Ref: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
    let code = event.code || this.getKeyCode(event);
    switch (code) {
      case "Equal":
      case "NumpadAdd":
        this.changeDelayOffset(+1, event);
        break;
      case "Minus":
      case "NumpadSubtract":
        this.changeDelayOffset(-1, event);
        break;
      case "NumpadMultiply":
        this.changeDelayOffset(0, event);
        break;
      case "KeyG":
        this.toggleGenvidOverlay();
        break;
      case "KeyF":
        this.toggleFullScreen();
        break;
      case "Space":
        if (this.genvidClient.videoPlayer.isPaused()) {
          this.genvidClient.videoPlayer.play();
        } else {
          this.genvidClient.videoPlayer.pause();
        }
        event.preventDefault();
        break;
      case "KeyM":
        this.toggleMute();
        break;
      case "KeyZ":
        this.decreaseVolume();
        break;
      case "KeyX":
        this.increaseVolume();
        break;
      case "KeyH":
        this.onHelpActivation();
        break;
    }
  }

getKeyCode

getKeyCode(event)

The getKeyCode method gets a proper keyCode on different browsers.

  // Compatibility code for browsers (Safari) not having KeyboardEvent.code.
  getKeyCode(event) {
    if (event.keyCode) {
      if (65 <= event.keyCode && event.keyCode <= 90) {
        return "Key" + String.fromCharCode(event.keyCode);
      } else {
        switch (event.keyCode) {
          case 13:
            return "Enter";
          case 106:
            return "NumpadMultiply";
          case 107:
            return "NumpadAdd";
          case 109:
            return "NumpadSubtract";
          case 110:
            return "NumpadDecimal";
          case 111:
            return "NumpadDivide";
          case 187:
            return "Equal";
          case 188:
            return "Comma";
          case 189:
            return "Minus";
          case 190:
            return "Period";
          case 222:
            return "Backquote";
        }
      }
    }
  }

onResize

onResize()

The onResize method resizes the various overlays used in the web page for index.html. The overlays affected by this function are:

  • videoOverlay,
  • canvas3d,
  • volumeDisplay (used for sound modification)
  • the animated divs with the name of each object.

We get the size value of the video stream displayed in the index.html page and we adjust the other overlays according to this size. In this sample, resize is essential because we need the proper overlay size to perform the object selection properly. Also, we need it to display the WebGL circles at their appropriate locations to follow the objects in the video.

  // Allows to adjust the various overlays when resizing the windows - needed to see the PromptOverlay and Name moving div
  onResize() {
    const refElement = this.genvidClient.videoElem; // The element to match.
    const refElementSize = refElement
      ? genvidMath.vec2(refElement.clientWidth, refElement.clientHeight)
      : genvidMath.vec2(1280, 720);
    const refElementRatio = refElementSize.x / refElementSize.y;
    const videoRatio = this.genvidClient.videoAspectRatio;
    let pos;
    let size;
    if (videoRatio >= refElementRatio) {
      // Top+Bottom bars, fills width fully, shrinks height.
      const ey = refElementSize.x / videoRatio;
      const dy = refElementSize.y - ey;
      // Centers vertically.
      const y = dy * 0.5;
      pos = genvidMath.vec2(0, Math.round(y));
      size = genvidMath.vec2(refElementSize.x, Math.round(ey));
    } else {
      // Left+Right bars, fills height fully, shrinks width.
      const ex = refElementSize.y * videoRatio;
      const dx = refElementSize.x - ex;
      // Centers horizontally.
      const x = dx * 0.5;
      pos = genvidMath.vec2(Math.round(x), 0);
      size = genvidMath.vec2(Math.round(ex), refElementSize.y);
    }
    const style = this.videoOverlay.style;
    const curPos = genvidMath.vec2(parseInt(style.left), parseInt(style.top));
    const curSize = genvidMath.vec2(
      parseInt(style.width),
      parseInt(style.height)
    );

    if (
      !genvidMath.equal2D(curSize, size, 0.9) ||
      !genvidMath.equal2D(curPos, pos, 0.9)
    ) {
      this.videoOverlay.style.left = pos.x + "px";
      this.videoOverlay.style.width = size.x + "px";
      this.videoOverlay.style.top = pos.y + "px";
      this.videoOverlay.style.height = size.y + "px";
      this.canvas3d.width = size.x;
      this.canvas3d.height = size.y;
      this.renderer.setSize(size.x, size.y);
    }
  }

toggleFullScreen

toggleFullScreen()

The toggleFullScreen method activates or deactivates fullscreen on the video. We use the checkFullScreen function to verify fullscreen status and adjust to the proper condition.

If we’re already in fullscreen, we cancel fullscreen according to the proper web browser function and we also adjust the fullscreen button icon.

If we aren’t in fullscreen, we proceed to get the video_area element. Afterwards, we use the proper web browser function to activate fullscreen and update the fullscreen button icon.

  toggleFullScreen() {
    if (this.checkFullScreen()) {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
      } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
      }
      this.fullScreenIcon.classList.remove("fa-compress");
      this.fullScreenIcon.classList.add("fa-expand");
    } else {
      let element = document.querySelector("#video_area");
      if (element.requestFullscreen) {
        element.requestFullscreen();
      } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
      } else if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen();
      } else if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
      }
      this.fullScreenIcon.classList.remove("fa-expand");
      this.fullScreenIcon.classList.add("fa-compress");
    }
  }

toggleMute

toggleMute()

The toggleMute method is called whenever the video player is muted/unmuted.

Whenever this function is invoked, we can retrieve the mute state by calling getMuted(), and adjust the visibility of the audio, volume up icons, and the overlay message appropriately.

  // Depending on the status, mutes or unmutes the video player audio
  toggleMute() {
    const muteIcon = document.querySelector("#mute-button i");
    if (this.genvidClient.videoPlayer.getMuted()) {
      muteIcon.classList.remove("fa-volume-off");
      muteIcon.classList.add("fa-volume-up");
      this.genvidClient.videoPlayer.setMuted(false);
      this.volumeDisplay.style.visibility = "visible";
      this.volumeDisplay.textContent = "Volume is unmuted";
      this.volumeInfoDisplayCount = 0;
    } else {
      muteIcon.classList.remove("fa-volume-up");
      muteIcon.classList.add("fa-volume-off");
      this.genvidClient.videoPlayer.setMuted(true);
      this.volumeDisplay.style.visibility = "visible";
      this.volumeDisplay.textContent = "Volume is muted";
      this.volumeInfoDisplayCount = 0;
    }
  }

decreaseVolume increaseVolume

decreaseVolume() increaseVolume()

The decreaseVolume and increaseVolume methods use genvidClient.videoPlayer.setVolume and adjust the volumeDisplay overlay accordingly.

  decreaseVolume() {
    const newVolume = this.genvidClient.videoPlayer.getVolume() - 20;
    this.genvidClient.videoPlayer.setVolume(newVolume);
    this.volumeDisplay.style.visibility = "visible";
    this.volumeInfoDisplayCount = 0;
    this.volumeDisplay.textContent = `Volume: ${this.genvidClient.videoPlayer.getVolume()} %`;
  }

  increaseVolume() {
    const newVolume = this.genvidClient.videoPlayer.getVolume() + 20;
    this.genvidClient.videoPlayer.setVolume(newVolume);
    this.volumeDisplay.style.visibility = "visible";
    this.volumeInfoDisplayCount = 0;
    this.volumeDisplay.textContent = `Volume: ${this.genvidClient.videoPlayer.getVolume()} %`;
  }

showOverlay

showOverlay()

The showOverlay method displays the Genvid overlay.

  // Changes the style to display the Genvid overlay
  showOverlay() {
    this.genvidOverlay.style.display = "block";
  }

hideOverlay

hideOverlay()

The hideOverlay method hides the Genvid overlay.

  // Changes the style to hide the Genvid overlay
  hideOverlay() {
    this.genvidOverlay.style.display = "none";
  }

clickCube

clickCube(event)

The clickCube function returns true if the pickCube method determines that a cube was selected by clicking the WebGL overlay. If not, it returns false.

  // Method used when clicking on the WebGL overlay
  clickScene(event) {
    const rect = event.target.getBoundingClientRect();

    this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    this.raycaster.setFromCamera(this.mouse, this.camera);

    const pickedObjects = this.raycaster.intersectObjects(this.scene.children);

    if (pickedObjects.length) {
      this.selectCube(parseInt(pickedObjects[0].object.name));
    } else {
      this.selectCube(-1);
    }
  }

isSelected

isSelected(name)

The isSelected method tells us if an object is selected or not.

  // Verifies if the cube is selected
  isSelected(name) {
    return this.selectedCubeId === name;
  }

changeDelayOffset

changeDelayOffset(direction, event)

The changeDelayOffset method adds, reduces, or resets the delay offset of the video. If the argument direction is equal to 0, the delay offset of the video is reset. Otherwise, it is changed according to the value sent.

  // Function that changes the delay offset depending of the key pressed
  changeDelayOffset(direction, event) {
    if (direction !== 0) {
      let delayDelta = 100 * direction;
      if (event.altKey) delayDelta *= 10;
      if (event.shiftKey) delayDelta /= 3;
      this.genvidClient.delayOffset += delayDelta;
    } else {
      this.genvidClient.delayOffset = 0;
    }
  }

toggleGenvidOverlay

toggleGenvidOverlay()

The toggleGenvidOverlay method displays or hides the Genvid overlay. The Genvid overlay displays various stats about the stream.

Local
Local time of the machine displaying the webpage.
Est. Video
Estimated time when the video is received.
Stream received
Time of the last data reported by the Compose service.
Stream played
Current time of the generalized stream.
Latency
Estimated latency between the game and the spectators.
DelayOffset
Optional delay offset used for minute synchronization adjustments.
  // Displays or removes the Genvid Overlay
  toggleGenvidOverlay() {
    if (this.genvidOverlay.getAttribute("data-isHidden")) {
      this.genvidOverlay.setAttribute("data-isHidden", "");
      this.genvidOverlay.style.visibility = "visible";
      this.genvidOverlayButton.classList.remove("disabled");
    } else {
      this.genvidOverlay.setAttribute("data-isHidden", "true");
      this.genvidOverlay.style.visibility = "hidden";
      this.genvidOverlayButton.classList.add("disabled");
    }
  }

onHelpActivation

onHelpActivation()

The onHelpActivation method displays or hides the help overlay.

  // Displays or removes the help overlay
  onHelpActivation() {
    if (this.helpOverlay.style.visibility === "visible") {
      this.helpOverlay.style.visibility = "hidden";
    } else {
      this.helpOverlay.style.visibility = "visible";
    }
  }

onCheer

onCheer(cubeName)

The onCheer method sends and event to the client when a button is clicked to cheer a player. The event contains the name of the object.

  // Upon cheering a player
  onCheer(cubeName) {
    this.genvidClient.sendEventObject({
      cheer: cubeName,
    });
  }

onReset

onReset(cubeName)

The onReset method sends an event when a button is clicked to reset a player. The event contains the name of the object.

  // Resets the position of the cube
  onReset(cubeName) {
    this.genvidClient.sendEventObject({
      reset: cubeName,
    });
  }

onColorChange

onColorChange(cube, color)

The onColorChange method sends an event when a button is clicked on to change the color of a player. The event contains the name of the object and the color.

  // Method used when clicking on a color to change the color of a cube
  onColorChange(cube, color) {
    let evt = {
      key: ["changeColor", cube],
      value: color,
    };
    this.genvidClient.sendEvent([evt]);
  }

onSelect

onSelect(cubeName, selectionInterface)

The onSelect method changes the UI when the user clicks the player table located under the video stream. We reset the color for each table and apply the color to the selected table only, then we select the WebGL circle.

  // Selects the cube from the interface
  onSelect(cubeName) {
    for (let nameSelect of this.cubePanelDiv) {
      nameSelect.style.backgroundColor = "#181818";
    }

    if (cubeName) {
      let cubeDiv = document.querySelector(`#${cubeName} .cube`);
      if (cubeDiv) {
        cubeDiv.style.backgroundColor = "#32324e";
      }
    }

    this.selectedCubeId = cubeName;
  }