ov-components: enhance layout service with responsive viewport handling and layout options adjustment

master
Carlos Santos 2025-09-22 20:06:25 +02:00
parent bd74184799
commit 1cef3c17a4
1 changed files with 163 additions and 19 deletions

View File

@ -1,8 +1,9 @@
import { Injectable } from '@angular/core';
import { Injectable, effect } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { LayoutAlignment, LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model';
import { ILogger } from '../../models/logger.model';
import { LoggerService } from '../logger/logger.service';
import { ViewportService } from '../viewport/viewport.service';
/**
* @internal
@ -16,14 +17,19 @@ export class LayoutService {
captionsTogglingObs: Observable<boolean>;
protected layoutWidth: BehaviorSubject<number> = new BehaviorSubject(0);
protected openviduLayout: OpenViduLayout | undefined;
protected openviduLayoutOptions: OpenViduLayoutOptions;
protected openviduLayoutOptions!: OpenViduLayoutOptions;
protected captionsToggling: BehaviorSubject<boolean> = new BehaviorSubject(false);
protected log: ILogger;
constructor(protected loggerSrv: LoggerService) {
constructor(
protected loggerSrv: LoggerService,
protected viewportSrv: ViewportService
) {
this.layoutWidthObs = this.layoutWidth.asObservable();
this.captionsTogglingObs = this.captionsToggling.asObservable();
this.log = this.loggerSrv.get('LayoutService');
this.openviduLayoutOptions = this.getOptions();
this.setupViewportListener();
}
initialize(container: HTMLElement) {
@ -43,6 +49,7 @@ export class LayoutService {
update(timeout: number | undefined = undefined) {
const updateAux = () => {
if (this.openviduLayout && this.layoutContainer) {
this.openviduLayoutOptions = this.getOptions();
this.openviduLayout.updateLayout(this.layoutContainer, this.openviduLayoutOptions);
this.sendLayoutWidthEvent();
}
@ -54,6 +61,10 @@ export class LayoutService {
}
}
updateResponsive() {
this.updateLayoutOptions();
}
getLayout() {
return this.openviduLayout;
}
@ -62,27 +73,33 @@ export class LayoutService {
this.openviduLayout = undefined;
}
/**
* Get layout options adjusted to the current viewport
* @returns Layout options adjusted to the current viewport
*/
protected getOptions(): OpenViduLayoutOptions {
const options = {
maxRatio: 3 / 2, // The narrowest ratio that will be used (default 2x3)
minRatio: 9 / 16, // The widest ratio that will be used (default 16x9)
fixedRatio: false /* If this is true then the aspect ratio of the video is maintained
and minRatio and maxRatio are ignored (default false) */,
bigClass: LayoutClass.BIG_ELEMENT, // The class to add to elements that should be sized bigger
const ratios = this.getResponsiveRatios();
const percentages = this.getResponsivePercentages();
return {
maxRatio: ratios.maxRatio,
minRatio: ratios.minRatio,
fixedRatio: false,
bigClass: LayoutClass.BIG_ELEMENT,
smallClass: LayoutClass.SMALL_ELEMENT,
ignoredClass: LayoutClass.IGNORED_ELEMENT,
bigPercentage: 0.8, // The maximum percentage of space the big ones should take up
minBigPercentage: 0, // If this is set then it will scale down the big space if there is left over whitespace down to this minimum size
bigFixedRatio: false, // fixedRatio for the big ones
bigMaxRatio: 9 / 16, // The narrowest ratio to use for the big elements (default 2x3)
bigMinRatio: 9 / 16, // The widest ratio to use for the big elements (default 16x9)
bigFirst: true, // Whether to place the big one in the top left (true) or bottom right
animate: true, // Whether you want to animate the transitions. Deprecated property, to disable it remove the transaction property on OV_publisher css class
bigPercentage: percentages.bigPercentage,
minBigPercentage: percentages.minBigPercentage,
bigFixedRatio: false,
bigMaxRatio: ratios.bigMaxRatio,
bigMinRatio: ratios.bigMinRatio,
bigFirst: true,
animate: true,
alignItems: LayoutAlignment.CENTER,
bigAlignItems: LayoutAlignment.CENTER,
smallAlignItems: LayoutAlignment.CENTER,
maxWidth: Infinity, // The maximum width of the elements
maxHeight: Infinity, // The maximum height of the elements
maxWidth: Infinity,
maxHeight: Infinity,
smallMaxWidth: Infinity,
smallMaxHeight: Infinity,
bigMaxWidth: Infinity,
@ -90,7 +107,134 @@ export class LayoutService {
scaleLastRow: true,
bigScaleLastRow: true
};
return options;
}
protected getResponsiveRatios() {
const isMobile = this.viewportSrv.isMobile();
const isTablet = this.viewportSrv.isTablet();
const isPortrait = this.viewportSrv.isPortrait();
if (isMobile && isPortrait) {
return {
maxRatio: 5 / 4,
minRatio: 4 / 5,
bigMaxRatio: 5 / 4,
bigMinRatio: 3 / 4
};
}
if (isMobile) {
return {
maxRatio: 16 / 9,
minRatio: 3 / 4,
bigMaxRatio: 16 / 9,
bigMinRatio: 4 / 3
};
}
if (isTablet && isPortrait) {
return {
maxRatio: 4 / 3,
minRatio: 3 / 5,
bigMaxRatio: 4 / 3,
bigMinRatio: 9 / 16
};
}
if (isTablet) {
return {
maxRatio: 16 / 9,
minRatio: 2 / 3,
bigMaxRatio: 16 / 9,
bigMinRatio: 9 / 16
};
}
return {
maxRatio: 16 / 9,
minRatio: 9 / 16,
bigMaxRatio: 16 / 9,
bigMinRatio: 9 / 16
};
}
protected getResponsivePercentages() {
const isMobile = this.viewportSrv.isMobile();
const isTablet = this.viewportSrv.isTablet();
const isPortrait = this.viewportSrv.isPortrait();
if (isMobile && isPortrait) {
return {
bigPercentage: 0.85,
minBigPercentage: 0.7
};
}
if (isMobile) {
return {
bigPercentage: 0.82,
minBigPercentage: 0.65
};
}
if (isTablet && isPortrait) {
return {
bigPercentage: 0.83,
minBigPercentage: 0.6
};
}
if (isTablet) {
return {
bigPercentage: 0.81,
minBigPercentage: 0.55
};
}
return {
bigPercentage: 0.8,
minBigPercentage: 0.5
};
}
protected setupViewportListener(): void {
effect(() => {
const viewportInfo = this.viewportSrv.viewportInfo();
const isMobile = this.viewportSrv.isMobile();
const orientation = this.viewportSrv.orientation();
this.updateLayoutOptions();
});
}
protected updateLayoutOptions(): void {
const newOptions = this.getOptions();
if (this.hasSignificantChanges(this.openviduLayoutOptions, newOptions)) {
this.openviduLayoutOptions = newOptions;
if (this.openviduLayout && this.layoutContainer) {
this.openviduLayout.updateLayout(this.layoutContainer, this.openviduLayoutOptions);
this.sendLayoutWidthEvent();
}
}
}
protected hasSignificantChanges(oldOptions: OpenViduLayoutOptions, newOptions: OpenViduLayoutOptions): boolean {
if (!oldOptions) return true;
const significantProps: (keyof OpenViduLayoutOptions)[] = [
'maxRatio',
'minRatio',
'bigMaxRatio',
'bigMinRatio',
'bigPercentage',
'alignItems',
'bigAlignItems'
];
return significantProps.some(
(prop) => Math.abs((oldOptions[prop] as number) - (newOptions[prop] as number)) > 0.01 || oldOptions[prop] !== newOptions[prop]
);
}
protected sendLayoutWidthEvent() {