From d114dc25062ab411856597803616f8a995210da6 Mon Sep 17 00:00:00 2001 From: jojohoch <joachim.hoch@iqb.hu-berlin.de> Date: Tue, 26 Oct 2021 19:29:42 +0200 Subject: [PATCH] [player] Apply audio and video appearance properties --- docs/release-notes-player.txt | 3 + .../element-components/audio.component.ts | 7 +- .../control-bar/control-bar.component.css | 33 ++++- .../control-bar/control-bar.component.html | 102 ++++++++++----- .../control-bar/control-bar.component.ts | 123 ++++++++++++++---- .../element-components/video.component.ts | 12 +- 6 files changed, 211 insertions(+), 69 deletions(-) diff --git a/docs/release-notes-player.txt b/docs/release-notes-player.txt index 7486e120c..681f6d1a4 100644 --- a/docs/release-notes-player.txt +++ b/docs/release-notes-player.txt @@ -1,5 +1,8 @@ Player ====== +1.2.1 +- Apply audio and video appearance properties to player + 1.2.0: - Add custom control bar for audio and video elements - Change background color and font size of virtual keyboard keys diff --git a/projects/common/element-components/audio.component.ts b/projects/common/element-components/audio.component.ts index 3fd6e1e1c..8c9e10fbe 100644 --- a/projects/common/element-components/audio.component.ts +++ b/projects/common/element-components/audio.component.ts @@ -8,10 +8,11 @@ import { AudioElement } from '../models/audio-element'; <div [style.width.%]="100" [style.height.%]="100"> <audio #player - [src]="elementModel.src | safeResourceUrl" - [style.width.%]="100"> + [style.width.%]="100" + [src]="elementModel.src | safeResourceUrl"> </audio> - <app-control-bar [player]="player"> + <app-control-bar [player]="player" + [elementModel]="elementModel"> </app-control-bar> </div> ` diff --git a/projects/common/element-components/control-bar/control-bar.component.css b/projects/common/element-components/control-bar/control-bar.component.css index 4e297248e..3e8681bc7 100644 --- a/projects/common/element-components/control-bar/control-bar.component.css +++ b/projects/common/element-components/control-bar/control-bar.component.css @@ -4,7 +4,6 @@ flex-direction: row; padding-right: 10px; overflow: hidden; - margin-top: -4px } .control-button { @@ -12,15 +11,25 @@ padding: 0 5px; } -.control-button:hover { +.enabled-control:hover{ color: #006064; } .time { + cursor: pointer; white-space: nowrap; padding: 17px 5px 0 5px; } +.runs { + white-space: nowrap; + padding: 17px 5px 0 5px; +} + +.time:hover { + color: #006064; +} + .active-control{ color: #006064; } @@ -28,21 +37,31 @@ .duration{ flex-grow: 5; min-width: 20%; - margin-top: 2px; + margin-top: 3px; } .volume{ flex-grow: 2; min-width: 10%; - margin-top: 2px; + margin-top: 3px; +} + +.status-bar{ + padding: 10px; + color: #f44336; + font-size: 75%; +} + +.hint-border{ + border: 2px #f44336 solid; } -::ng-deep .mat-accent .mat-slider-thumb { +::ng-deep app-control-bar .mat-accent .mat-slider-thumb { background-color: #006064; } -::ng-deep .mat-accent .mat-slider-thumb-label { +::ng-deep app-control-bar .mat-accent .mat-slider-thumb-label { background-color: #006064; } -::ng-deep .mat-accent .mat-slider-track-fill { +::ng-deep app-control-bar .mat-accent .mat-slider-track-fill { background-color: #006064; } diff --git a/projects/common/element-components/control-bar/control-bar.component.html b/projects/common/element-components/control-bar/control-bar.component.html index 89035ce3b..8531eda08 100644 --- a/projects/common/element-components/control-bar/control-bar.component.html +++ b/projects/common/element-components/control-bar/control-bar.component.html @@ -1,34 +1,72 @@ -<div class="control-bar"> - <button *ngIf="!playing" mat-button - class="control-button" - (click)="play()"> - <mat-icon>play_arrow</mat-icon> - </button> - <button *ngIf="playing" mat-button - class="control-button" - (click)="pause()"> - <mat-icon>pause</mat-icon> - </button> - <mat-slider class="duration" - min="0" - [max]="player.duration" - step="1" - (input)="onTimeChange($event)" - [value]="player.currentTime"> - </mat-slider> - <div class="time mat-typography">{{currentTime | playerTimeFormat}} / {{duration | playerTimeFormat }}</div> - <button mat-button - class="control-button" - (click)="toggleVolume()"> - <mat-icon *ngIf="!player.muted">volume_up</mat-icon> - <mat-icon *ngIf="player.muted">volume_off</mat-icon> - </button> - <mat-slider class="volume" - min="0" - [max]="1" - step="0.01" - (input)="onVolumeChange($event)" - [value]="player.volume"> - </mat-slider> +<div class="control-bar" + [class.hint-border]="showHint"> + <ng-container *ngIf="elementModel.startControl"> + <button *ngIf="!playing || !elementModel.pauseControl" + mat-button + class="control-button" + [class.enabled-control]="!disabled" + [class.active-control]="playing" + [disabled]="disabled" + (click)="play($event)"> + <mat-icon>play_arrow</mat-icon> + </button> + </ng-container> + <ng-container *ngIf="elementModel.pauseControl"> + <button *ngIf="playing" + mat-button + class="control-button" + [class.enabled-control]="!disabled" + [class.active-control]="pausing" + [disabled]="disabled" + (click)="pause($event)"> + <mat-icon>pause</mat-icon> + </button> + </ng-container> + <ng-container *ngIf="elementModel.maxRuns"> + <div class="runs mat-typography"> + {{ runCounter + 1 }} / {{ elementModel.maxRuns }} + </div> + </ng-container> + <ng-container *ngIf="elementModel.progressBar"> + <mat-slider class="duration" + min="0" + step="1" + [max]="player.duration" + [value]="player.currentTime" + [disabled]="disabled || !elementModel.interactiveProgressbar" + (input)="onTimeChange($event)"> + </mat-slider> + </ng-container> + <ng-container *ngIf="elementModel.showRestTime"> + <div *ngIf="!restTimeMode" + class="time mat-typography" + (click)="toggleTime($event)"> + {{currentTime | playerTimeFormat}} / {{duration | playerTimeFormat }} + </div> + <div *ngIf="restTimeMode" + class="time mat-typography" + (click)="toggleTime($event)"> + -{{currentRestTime | playerTimeFormat}} + </div> + </ng-container> + <ng-container *ngIf="elementModel.volumeControl"> + <button mat-button + class="control-button enabled-control" + (click)="toggleVolume($event)"> + <mat-icon *ngIf="!player.muted">volume_up</mat-icon> + <mat-icon *ngIf="player.muted">volume_off</mat-icon> + </button> + <mat-slider class="volume" + min="0" + step="0.01" + [max]="1" + [value]="player.volume" + (input)="onVolumeChange($event)"> + </mat-slider> + </ng-container> +</div> +<div *ngIf="showHint || !isAspectPlayer" + class="status-bar mat-typography"> + {{elementModel.hintLabel}} </div> diff --git a/projects/common/element-components/control-bar/control-bar.component.ts b/projects/common/element-components/control-bar/control-bar.component.ts index df24fabd4..d9ba05c02 100644 --- a/projects/common/element-components/control-bar/control-bar.component.ts +++ b/projects/common/element-components/control-bar/control-bar.component.ts @@ -2,6 +2,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { MatSliderChange } from '@angular/material/slider'; +import { AudioElement } from '../../models/audio-element'; +import { VideoElement } from '../../models/video-element'; @Component({ selector: 'app-control-bar', @@ -10,41 +12,105 @@ import { MatSliderChange } from '@angular/material/slider'; }) export class ControlBarComponent implements OnInit { @Input() player!: HTMLVideoElement | HTMLAudioElement; + @Input() elementModel!: AudioElement | VideoElement; duration!: number; currentTime!: number; + currentRestTime!: number; + started!: boolean; playing!: boolean; pausing!: boolean; runCounter!: number; lastVolume!: number; + restTimeMode: boolean = true; + showHint!: boolean; + disabled!: boolean; + isAspectPlayer!: boolean; + + // TODO: + // uninterruptible: boolean; // false kein Blättern; starten eines anderen Videos; .... + // hideOtherPages: boolean; // false (Solange nicht vollständig gespielt, sind alle anderen Seiten verborgen) + // activeAfterID: string; // '' (andere Audio-id; Audio ist deaktiviert, solange anderes nicht vollständig abgespielt) + // minRuns: number; // 1 ngOnInit(): void { + this.checkEnvironment(); // Firefox has problems to get the duration - this.player.ondurationchange = () => this.getDuration(); - this.player.onloadedmetadata = () => this.getDuration(); - this.player.onloadeddata = () => this.getDuration(); - this.player.onprogress = () => this.getDuration(); - this.player.oncanplay = () => this.getDuration(); - this.player.oncanplaythrough = () => this.getDuration(); - - this.player.ontimeupdate = () => { this.currentTime = this.player.currentTime / 60; }; - this.player.onpause = () => { this.playing = false; this.pausing = true; }; - this.player.onended = () => { this.runCounter += 1; }; // playing, pausing? - this.player.onvolumechange = () => { this.player.muted = !this.player.volume; }; + this.player.ondurationchange = () => this.initTimerValues(); + this.player.ontimeupdate = () => { + this.currentTime = this.player.currentTime / 60; + this.currentRestTime = this.player.duration ? (this.player.duration - this.player.currentTime) / 60 : 0; + }; + this.player.onpause = () => { + this.playing = false; + this.pausing = true; + }; + this.player.onplaying = () => { + this.playing = true; + this.pausing = false; + this.started = true; + this.showHint = false; + }; + this.player.onended = () => { + if (!this.checkStatus(this.runCounter + 1)) { + this.runCounter += 1; + if (this.elementModel.loop) { + this._play(); + } + } + }; + this.player.onvolumechange = () => { + this.player.muted = !this.player.volume; + }; this.lastVolume = this.player.volume; + this.runCounter = 0; + this.currentTime = 0; + if (this.isAspectPlayer) { + this.initAutostart(); + this.initHint(); + } + } + + private checkEnvironment() { + this.isAspectPlayer = !!this.player.closest('player-aspect'); } - play(): void { + private checkStatus(runCounter: number): boolean { + this.disabled = !this.elementModel.maxRuns ? false : this.elementModel.maxRuns <= runCounter; + return this.disabled; + } + + private initAutostart(): void { + if (this.elementModel.autostart && !this.started) { + setTimeout(() => { + this._play(); + }, this.elementModel.autostartDelay); + } + } + + private initHint(): void { + if (this.elementModel.hintLabel && !this.started) { + setTimeout(() => { + this.showHint = true; + }, this.elementModel.hintLabelDelay); + } + } + + private _play(): void { + this.player.play().then(() => {}, // eslint-disable-next-line no-console - this.player.play().then(() => { this.playing = true; this.pausing = false; }, () => console.error('error')); + () => console.error('error')); } - pause(): void { - this.player.pause(); + play(event: MouseEvent): void { + this._play(); + event.stopPropagation(); + event.preventDefault(); } - stop(): void { + pause(event: MouseEvent): void { this.player.pause(); - this.player.currentTime = 0; + event.stopPropagation(); + event.preventDefault(); } onTimeChange(event: MatSliderChange): void { @@ -55,20 +121,31 @@ export class ControlBarComponent implements OnInit { this.player.volume = event.value ? event.value : 0; } - toggleVolume(): void { + toggleTime(event: MouseEvent): void { + this.restTimeMode = !this.restTimeMode; + event.stopPropagation(); + event.preventDefault(); + } + + toggleVolume(event: MouseEvent): void { if (this.player.volume) { this.lastVolume = this.player.volume; this.player.volume = 0; } else { this.player.volume = this.lastVolume; } + event.stopPropagation(); + event.preventDefault(); } - private getDuration(): void { - if (this.player.duration !== Infinity && this.player.duration && !this.duration) { - this.duration = this.player.duration / 60; - } else { - this.duration = 0; + private initTimerValues(): void { + if (!this.duration) { + if ((this.player.duration !== Infinity) && this.player.duration) { + this.duration = this.player.duration / 60; + this.currentRestTime = (this.player.duration - this.player.currentTime) / 60; + } else { + this.duration = 0; + } } } } diff --git a/projects/common/element-components/video.component.ts b/projects/common/element-components/video.component.ts index 945a8549f..07bb1656e 100644 --- a/projects/common/element-components/video.component.ts +++ b/projects/common/element-components/video.component.ts @@ -8,13 +8,17 @@ import { VideoElement } from '../models/video-element'; <div [style.object-fit]="'contain'" [style.height.%]="100" [style.width.%]="100"> - <video #player [src]="elementModel.src | safeResourceUrl" - [style.width.%]="100"> + <video #player + [style.width.%]="100" + [src]="elementModel.src | safeResourceUrl"> </video> - <app-control-bar [player]="player"> + <app-control-bar class="correct-position" + [player]="player" + [elementModel]="elementModel"> </app-control-bar> </div> - ` + `, + styles: ['.correct-position{ display: block; margin-top: -4px; }'] }) export class VideoComponent extends ElementComponent { elementModel!: VideoElement; -- GitLab