mirror of https://github.com/OpenVidu/openvidu.git
ov-components: Optimize layout handling with caching and resize improvements
parent
7c17e19cbb
commit
0cf5101931
|
|
@ -104,8 +104,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
private resizeTimeout: NodeJS.Timeout;
|
private resizeTimeout: NodeJS.Timeout;
|
||||||
|
private rafId: number | null = null;
|
||||||
private videoIsAtRight: boolean = false;
|
private videoIsAtRight: boolean = false;
|
||||||
private lastLayoutWidth: number = 0;
|
private lastLayoutWidth: number = 0;
|
||||||
|
private readonly SIGNIFICANT_RESIZE_THRESHOLD = 5; // pixels
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
|
|
@ -140,6 +142,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
this.localParticipant = undefined;
|
this.localParticipant = undefined;
|
||||||
this.remoteParticipants = [];
|
this.remoteParticipants = [];
|
||||||
|
if (this.rafId !== null) {
|
||||||
|
cancelAnimationFrame(this.rafId);
|
||||||
|
this.rafId = null;
|
||||||
|
}
|
||||||
this.resizeObserver?.disconnect();
|
this.resizeObserver?.disconnect();
|
||||||
this.layoutService.clear();
|
this.layoutService.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -217,11 +223,22 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
private listenToResizeLayout() {
|
private listenToResizeLayout() {
|
||||||
this.resizeObserver = new ResizeObserver((entries) => {
|
this.resizeObserver = new ResizeObserver((entries) => {
|
||||||
clearTimeout(this.resizeTimeout);
|
// Cancel any pending animation frame to avoid duplicate work
|
||||||
|
if (this.rafId !== null) {
|
||||||
|
cancelAnimationFrame(this.rafId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use requestAnimationFrame for better performance
|
||||||
|
this.rafId = requestAnimationFrame(() => {
|
||||||
|
const { width: parentWidth } = entries[0].contentRect;
|
||||||
|
|
||||||
|
// Only update if the change is significant (threshold-based)
|
||||||
|
if (Math.abs(this.lastLayoutWidth - parentWidth) < this.SIGNIFICANT_RESIZE_THRESHOLD) {
|
||||||
|
this.rafId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.resizeTimeout = setTimeout(() => {
|
|
||||||
if (this.localParticipant?.isMinimized) {
|
if (this.localParticipant?.isMinimized) {
|
||||||
const { width: parentWidth } = entries[0].contentRect;
|
|
||||||
if (this.panelService.isPanelOpened()) {
|
if (this.panelService.isPanelOpened()) {
|
||||||
if (this.lastLayoutWidth < parentWidth) {
|
if (this.lastLayoutWidth < parentWidth) {
|
||||||
// Layout is bigger than before. Maybe the settings panel(wider) has been transitioned to another panel.
|
// Layout is bigger than before. Maybe the settings panel(wider) has been transitioned to another panel.
|
||||||
|
|
@ -240,9 +257,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
this.moveStreamToRight(parentWidth);
|
this.moveStreamToRight(parentWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.lastLayoutWidth = parentWidth;
|
|
||||||
}
|
}
|
||||||
}, 100);
|
|
||||||
|
this.lastLayoutWidth = parentWidth;
|
||||||
|
this.rafId = null;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.resizeObserver.observe(this.layoutContainer.element.nativeElement);
|
this.resizeObserver.observe(this.layoutContainer.element.nativeElement);
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,88 @@ export interface ExtendedLayoutOptions extends OpenViduLayoutOptions {
|
||||||
containerHeight: number;
|
containerHeight: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface for layout area calculations
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface LayoutStrategy {
|
||||||
|
calculateAreas(
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
bigPercentage: number,
|
||||||
|
bigWidth: number,
|
||||||
|
bigHeight: number
|
||||||
|
): { big: LayoutArea; small: LayoutArea; offsetLeft: number; offsetTop: number };
|
||||||
|
|
||||||
|
determineBigFirst(bigFirst: BigFirstOption): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tall layout strategy: arrange small elements at bottom
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class TallLayoutStrategy implements LayoutStrategy {
|
||||||
|
calculateAreas(
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
bigPercentage: number,
|
||||||
|
bigWidth: number,
|
||||||
|
bigHeight: number
|
||||||
|
): { big: LayoutArea; small: LayoutArea; offsetLeft: number; offsetTop: number } {
|
||||||
|
const offsetTop = bigHeight;
|
||||||
|
return {
|
||||||
|
big: { top: 0, left: 0, width: bigWidth, height: bigHeight },
|
||||||
|
small: {
|
||||||
|
top: offsetTop,
|
||||||
|
left: 0,
|
||||||
|
width: containerWidth,
|
||||||
|
height: containerHeight - offsetTop
|
||||||
|
},
|
||||||
|
offsetLeft: 0,
|
||||||
|
offsetTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
determineBigFirst(bigFirst: BigFirstOption): boolean {
|
||||||
|
if (bigFirst === 'column') return false;
|
||||||
|
if (bigFirst === 'row') return true;
|
||||||
|
return !!bigFirst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wide layout strategy: arrange small elements on right
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class WideLayoutStrategy implements LayoutStrategy {
|
||||||
|
calculateAreas(
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
bigPercentage: number,
|
||||||
|
bigWidth: number,
|
||||||
|
bigHeight: number
|
||||||
|
): { big: LayoutArea; small: LayoutArea; offsetLeft: number; offsetTop: number } {
|
||||||
|
const offsetLeft = bigWidth;
|
||||||
|
return {
|
||||||
|
big: { top: 0, left: 0, width: bigWidth, height: bigHeight },
|
||||||
|
small: {
|
||||||
|
top: 0,
|
||||||
|
left: offsetLeft,
|
||||||
|
width: containerWidth - offsetLeft,
|
||||||
|
height: containerHeight
|
||||||
|
},
|
||||||
|
offsetLeft,
|
||||||
|
offsetTop: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
determineBigFirst(bigFirst: BigFirstOption): boolean {
|
||||||
|
if (bigFirst === 'column') return true;
|
||||||
|
if (bigFirst === 'row') return false;
|
||||||
|
return !!bigFirst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout configuration constants
|
* Layout configuration constants
|
||||||
*/
|
*/
|
||||||
|
|
@ -95,10 +177,10 @@ export const LAYOUT_CONSTANTS = {
|
||||||
DEFAULT_VIDEO_HEIGHT: 480,
|
DEFAULT_VIDEO_HEIGHT: 480,
|
||||||
DEFAULT_MAX_RATIO: 3 / 2,
|
DEFAULT_MAX_RATIO: 3 / 2,
|
||||||
DEFAULT_MIN_RATIO: 9 / 16,
|
DEFAULT_MIN_RATIO: 9 / 16,
|
||||||
DEFAULT_BIG_PERCENTAGE: 0.8,
|
DEFAULT_BIG_PERCENTAGE: 0.85,
|
||||||
UPDATE_TIMEOUT: 50,
|
UPDATE_TIMEOUT: 50,
|
||||||
ANIMATION_DURATION: '0.1s',
|
ANIMATION_DURATION: '0.15s',
|
||||||
ANIMATION_EASING: 'linear'
|
ANIMATION_EASING: 'ease-in-out'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -162,6 +244,8 @@ export class OpenViduLayout {
|
||||||
private layoutContainer!: HTMLElement;
|
private layoutContainer!: HTMLElement;
|
||||||
private opts!: OpenViduLayoutOptions;
|
private opts!: OpenViduLayoutOptions;
|
||||||
private dimensionsCache = new Map<string, BestDimensions>();
|
private dimensionsCache = new Map<string, BestDimensions>();
|
||||||
|
private layoutCache = new Map<string, { boxes: LayoutBox[]; areas: any }>();
|
||||||
|
private readonly CACHE_SIZE_LIMIT = 100;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the layout container
|
* Update the layout container
|
||||||
|
|
@ -253,6 +337,31 @@ export class OpenViduLayout {
|
||||||
*/
|
*/
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.dimensionsCache.clear();
|
this.dimensionsCache.clear();
|
||||||
|
this.layoutCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage cache size to prevent unlimited growth
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private manageCacheSize(cache: Map<any, any>): void {
|
||||||
|
if (cache.size > this.CACHE_SIZE_LIMIT) {
|
||||||
|
const firstKey = cache.keys().next().value;
|
||||||
|
if (firstKey !== undefined) {
|
||||||
|
cache.delete(firstKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate cache key for layout calculations
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private getLayoutCacheKey(opts: ExtendedLayoutOptions, elements: ElementDimensions[]): string {
|
||||||
|
const elementKey = elements
|
||||||
|
.map(e => `${e.width}x${e.height}x${e.big ? '1' : '0'}`)
|
||||||
|
.join('_');
|
||||||
|
return `${opts.containerWidth}x${opts.containerHeight}_${elementKey}_${opts.bigPercentage}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -330,11 +439,17 @@ export class OpenViduLayout {
|
||||||
if (animate) {
|
if (animate) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// animation added in css transition: all .1s linear;
|
// animation added in css transition: all .1s linear;
|
||||||
this.animateElement(elem, targetPosition);
|
elem.style.transition = `all ${LAYOUT_CONSTANTS.ANIMATION_DURATION} ${LAYOUT_CONSTANTS.ANIMATION_EASING}`;
|
||||||
|
Object.entries(targetPosition).forEach(([key, value]) => {
|
||||||
|
(elem.style as any)[key] = value;
|
||||||
|
});
|
||||||
this.fixAspectRatio(elem, width);
|
this.fixAspectRatio(elem, width);
|
||||||
}, 10);
|
}, 10);
|
||||||
} else {
|
} else {
|
||||||
this.setElementPosition(elem, targetPosition);
|
elem.style.transition = 'none';
|
||||||
|
Object.entries(targetPosition).forEach(([key, value]) => {
|
||||||
|
(elem.style as any)[key] = value;
|
||||||
|
});
|
||||||
if (!elem.classList.contains(LayoutClass.CLASS_NAME)) {
|
if (!elem.classList.contains(LayoutClass.CLASS_NAME)) {
|
||||||
elem.classList.add(LayoutClass.CLASS_NAME);
|
elem.classList.add(LayoutClass.CLASS_NAME);
|
||||||
}
|
}
|
||||||
|
|
@ -342,17 +457,6 @@ export class OpenViduLayout {
|
||||||
this.fixAspectRatio(elem, width);
|
this.fixAspectRatio(elem, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setElementPosition(elem: HTMLVideoElement, targetPosition: { [key: string]: string }) {
|
|
||||||
Object.keys(targetPosition).forEach((key) => {
|
|
||||||
(elem.style as any)[key] = targetPosition[key];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private animateElement(elem: HTMLVideoElement, targetPosition: { [key: string]: string }) {
|
|
||||||
elem.style.transition = `all ${LAYOUT_CONSTANTS.ANIMATION_DURATION} ${LAYOUT_CONSTANTS.ANIMATION_EASING}`;
|
|
||||||
this.setElementPosition(elem, targetPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
|
|
@ -447,6 +551,9 @@ export class OpenViduLayout {
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manage cache size before adding new entry
|
||||||
|
this.manageCacheSize(this.dimensionsCache);
|
||||||
let bestArea = 0;
|
let bestArea = 0;
|
||||||
let bestCols = 1;
|
let bestCols = 1;
|
||||||
let bestRows = 1;
|
let bestRows = 1;
|
||||||
|
|
@ -511,7 +618,173 @@ export class OpenViduLayout {
|
||||||
private getVideoRatio(element: ElementDimensions): number {
|
private getVideoRatio(element: ElementDimensions): number {
|
||||||
return element.height / element.width;
|
return element.height / element.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate big element dimensions with minimum percentage constraints
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private calculateBigDimensions(
|
||||||
|
bigOnes: ElementDimensions[],
|
||||||
|
bigWidth: number,
|
||||||
|
bigHeight: number,
|
||||||
|
bigFixedRatio: boolean,
|
||||||
|
bigMinRatio: number,
|
||||||
|
bigMaxRatio: number,
|
||||||
|
bigMaxWidth: number,
|
||||||
|
bigMaxHeight: number,
|
||||||
|
minBigPercentage: number,
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
minRatio: number,
|
||||||
|
maxRatio: number,
|
||||||
|
smallOnes: ElementDimensions[],
|
||||||
|
smallMaxWidth: number,
|
||||||
|
smallMaxHeight: number,
|
||||||
|
isTall: boolean
|
||||||
|
): number {
|
||||||
|
if (minBigPercentage <= 0) {
|
||||||
|
return isTall ? bigHeight : bigWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the best size for the big area
|
||||||
|
const bigDimensions = !bigFixedRatio
|
||||||
|
? this.getBestDimensions(bigMinRatio, bigMaxRatio, bigWidth, bigHeight, bigOnes.length, bigMaxWidth, bigMaxHeight)
|
||||||
|
: this.getBestDimensions(
|
||||||
|
bigOnes[0].height / bigOnes[0].width,
|
||||||
|
bigOnes[0].height / bigOnes[0].width,
|
||||||
|
bigWidth,
|
||||||
|
bigHeight,
|
||||||
|
bigOnes.length,
|
||||||
|
bigMaxWidth,
|
||||||
|
bigMaxHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
const minSize = isTall ? containerHeight * minBigPercentage : containerWidth * minBigPercentage;
|
||||||
|
const calculatedSize = isTall
|
||||||
|
? bigDimensions.targetHeight * bigDimensions.targetRows
|
||||||
|
: bigDimensions.targetWidth * bigDimensions.targetCols;
|
||||||
|
let adjustedSize = Math.max(minSize, Math.min(isTall ? bigHeight : bigWidth, calculatedSize));
|
||||||
|
|
||||||
|
// Don't awkwardly scale the small area bigger than we need to
|
||||||
|
const smallDimensions = isTall
|
||||||
|
? this.getBestDimensions(minRatio, maxRatio, containerWidth, containerHeight - adjustedSize, smallOnes.length, smallMaxWidth, smallMaxHeight)
|
||||||
|
: this.getBestDimensions(minRatio, maxRatio, containerWidth - adjustedSize, containerHeight, smallOnes.length, smallMaxWidth, smallMaxHeight);
|
||||||
|
|
||||||
|
const smallCalculatedSize = isTall
|
||||||
|
? smallDimensions.targetRows * smallDimensions.targetHeight
|
||||||
|
: smallDimensions.targetCols * smallDimensions.targetWidth;
|
||||||
|
|
||||||
|
adjustedSize = Math.max(adjustedSize, (isTall ? containerHeight : containerWidth) - smallCalculatedSize);
|
||||||
|
|
||||||
|
return adjustedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get layout strategy based on container and video ratios
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private getLayoutStrategy(availableRatio: number, videoRatio: number): LayoutStrategy {
|
||||||
|
return availableRatio > videoRatio ? new TallLayoutStrategy() : new WideLayoutStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate layout areas for big and small elements using Strategy Pattern
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private calculateLayoutAreas(
|
||||||
|
availableRatio: number,
|
||||||
|
videoRatio: number,
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
bigPercentage: number,
|
||||||
|
bigFirst: BigFirstOption,
|
||||||
|
bigOnes: ElementDimensions[],
|
||||||
|
smallOnes: ElementDimensions[],
|
||||||
|
opts: ExtendedLayoutOptions
|
||||||
|
): { big: LayoutArea | null; small: LayoutArea | null; bigFirst: boolean } {
|
||||||
|
const strategy = this.getLayoutStrategy(availableRatio, videoRatio);
|
||||||
|
const isTall = availableRatio > videoRatio;
|
||||||
|
|
||||||
|
// Calculate initial big dimensions
|
||||||
|
const initialBigWidth = isTall ? containerWidth : Math.floor(containerWidth * bigPercentage);
|
||||||
|
const initialBigHeight = isTall ? Math.floor(containerHeight * bigPercentage) : containerHeight;
|
||||||
|
|
||||||
|
// Calculate final big dimensions with constraints
|
||||||
|
const bigWidth = isTall ? initialBigWidth : this.calculateBigDimensions(
|
||||||
|
bigOnes,
|
||||||
|
initialBigWidth,
|
||||||
|
initialBigHeight,
|
||||||
|
opts.bigFixedRatio,
|
||||||
|
opts.bigMinRatio,
|
||||||
|
opts.bigMaxRatio,
|
||||||
|
opts.bigMaxWidth,
|
||||||
|
opts.bigMaxHeight,
|
||||||
|
opts.minBigPercentage,
|
||||||
|
containerWidth,
|
||||||
|
containerHeight,
|
||||||
|
opts.minRatio,
|
||||||
|
opts.maxRatio,
|
||||||
|
smallOnes,
|
||||||
|
opts.smallMaxWidth,
|
||||||
|
opts.smallMaxHeight,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
const bigHeight = isTall ? this.calculateBigDimensions(
|
||||||
|
bigOnes,
|
||||||
|
initialBigWidth,
|
||||||
|
initialBigHeight,
|
||||||
|
opts.bigFixedRatio,
|
||||||
|
opts.bigMinRatio,
|
||||||
|
opts.bigMaxRatio,
|
||||||
|
opts.bigMaxWidth,
|
||||||
|
opts.bigMaxHeight,
|
||||||
|
opts.minBigPercentage,
|
||||||
|
containerWidth,
|
||||||
|
containerHeight,
|
||||||
|
opts.minRatio,
|
||||||
|
opts.maxRatio,
|
||||||
|
smallOnes,
|
||||||
|
opts.smallMaxWidth,
|
||||||
|
opts.smallMaxHeight,
|
||||||
|
true
|
||||||
|
) : initialBigHeight;
|
||||||
|
|
||||||
|
// Use strategy to calculate areas
|
||||||
|
const { big, small, offsetLeft, offsetTop } = strategy.calculateAreas(
|
||||||
|
containerWidth,
|
||||||
|
containerHeight,
|
||||||
|
bigPercentage,
|
||||||
|
bigWidth,
|
||||||
|
bigHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
const showBigFirst = strategy.determineBigFirst(bigFirst);
|
||||||
|
|
||||||
|
if (showBigFirst) {
|
||||||
|
return { big, small, bigFirst: true };
|
||||||
|
} else {
|
||||||
|
// Swap positions for bigFirst=false
|
||||||
|
const bigOffsetLeft = containerWidth - offsetLeft;
|
||||||
|
const bigOffsetTop = containerHeight - offsetTop;
|
||||||
|
return {
|
||||||
|
big: { left: bigOffsetLeft, top: bigOffsetTop, width: bigWidth, height: bigHeight },
|
||||||
|
small: { top: 0, left: 0, width: containerWidth - offsetLeft, height: containerHeight - offsetTop },
|
||||||
|
bigFirst: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
private getLayout(opts: ExtendedLayoutOptions, elements: ElementDimensions[]) {
|
private getLayout(opts: ExtendedLayoutOptions, elements: ElementDimensions[]) {
|
||||||
|
// Check cache first
|
||||||
|
const cacheKey = this.getLayoutCacheKey(opts, elements);
|
||||||
|
const cached = this.layoutCache.get(cacheKey);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manage cache size before adding new entry
|
||||||
|
this.manageCacheSize(this.layoutCache);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
maxRatio = LAYOUT_CONSTANTS.DEFAULT_MAX_RATIO,
|
maxRatio = LAYOUT_CONSTANTS.DEFAULT_MAX_RATIO,
|
||||||
minRatio = LAYOUT_CONSTANTS.DEFAULT_MIN_RATIO,
|
minRatio = LAYOUT_CONSTANTS.DEFAULT_MIN_RATIO,
|
||||||
|
|
@ -536,17 +809,8 @@ export class OpenViduLayout {
|
||||||
scaleLastRow = true,
|
scaleLastRow = true,
|
||||||
bigScaleLastRow = true
|
bigScaleLastRow = true
|
||||||
} = opts;
|
} = opts;
|
||||||
const availableRatio = containerHeight / containerWidth;
|
// Separate big and small elements
|
||||||
let offsetLeft = 0;
|
|
||||||
let offsetTop = 0;
|
|
||||||
let bigOffsetTop = 0;
|
|
||||||
let bigOffsetLeft = 0;
|
|
||||||
const bigIndices: number[] = [];
|
const bigIndices: number[] = [];
|
||||||
let bigBoxes: LayoutBox[] = [];
|
|
||||||
let smallBoxes: LayoutBox[] = [];
|
|
||||||
let areas: { big: LayoutArea | null; small: LayoutArea | null } = { big: null, small: null };
|
|
||||||
|
|
||||||
// Move to Get Layout
|
|
||||||
const smallOnes = elements.filter((element) => !element.big);
|
const smallOnes = elements.filter((element) => !element.big);
|
||||||
const bigOnes = elements.filter((element, idx) => {
|
const bigOnes = elements.filter((element, idx) => {
|
||||||
if (element.big) {
|
if (element.big) {
|
||||||
|
|
@ -555,176 +819,38 @@ export class OpenViduLayout {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
//TODO: Habia un codigo personalizado que servía para
|
|
||||||
//TODO: tener videos grandes, pequeños y normales
|
|
||||||
//.filter((x) => !smallOnes.includes(x));
|
|
||||||
|
|
||||||
// const normalOnes: HTMLVideoElement[] = Array.prototype.filter
|
// Determine layout areas based on element distribution
|
||||||
// .call(
|
let areas: { big: LayoutArea | null; small: LayoutArea | null };
|
||||||
// this.layoutContainer.querySelectorAll(
|
let bigBoxes: LayoutBox[] = [];
|
||||||
// `#${id}>*:not(.${this.opts.bigClass})`
|
let smallBoxes: LayoutBox[] = [];
|
||||||
// ),
|
|
||||||
// () => this.filterDisplayNone
|
|
||||||
// )
|
|
||||||
// .filter((x) => !smallOnes.includes(x));
|
|
||||||
// this.attachElements(bigOnes, normalOnes, smallOnes);
|
|
||||||
if (bigOnes.length > 0 && smallOnes.length > 0) {
|
if (bigOnes.length > 0 && smallOnes.length > 0) {
|
||||||
let bigWidth;
|
// Mixed layout: calculate areas for both big and small elements
|
||||||
let bigHeight;
|
const availableRatio = containerHeight / containerWidth;
|
||||||
let showBigFirst = bigFirst;
|
const layoutAreas = this.calculateLayoutAreas(
|
||||||
|
availableRatio,
|
||||||
if (availableRatio > this.getVideoRatio(bigOnes[0])) {
|
this.getVideoRatio(bigOnes[0]),
|
||||||
// We are tall, going to take up the whole width and arrange small
|
containerWidth,
|
||||||
// guys at the bottom
|
containerHeight,
|
||||||
bigWidth = containerWidth;
|
bigPercentage,
|
||||||
bigHeight = Math.floor(containerHeight * bigPercentage);
|
bigFirst,
|
||||||
if (minBigPercentage > 0) {
|
bigOnes,
|
||||||
// Find the best size for the big area
|
smallOnes,
|
||||||
let bigDimensions;
|
opts
|
||||||
if (!bigFixedRatio) {
|
);
|
||||||
bigDimensions = this.getBestDimensions(
|
areas = { big: layoutAreas.big, small: layoutAreas.small };
|
||||||
bigMinRatio,
|
} else if (bigOnes.length > 0) {
|
||||||
bigMaxRatio,
|
// Only big elements: use full container
|
||||||
bigWidth,
|
areas = {
|
||||||
bigHeight,
|
big: { top: 0, left: 0, width: containerWidth, height: containerHeight },
|
||||||
bigOnes.length,
|
small: null
|
||||||
bigMaxWidth,
|
|
||||||
bigMaxHeight
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Use the ratio of the first video element we find to approximate
|
|
||||||
const ratio = bigOnes[0].height / bigOnes[0].width;
|
|
||||||
bigDimensions = this.getBestDimensions(
|
|
||||||
ratio,
|
|
||||||
ratio,
|
|
||||||
bigWidth,
|
|
||||||
bigHeight,
|
|
||||||
bigOnes.length,
|
|
||||||
bigMaxWidth,
|
|
||||||
bigMaxHeight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bigHeight = Math.max(
|
|
||||||
containerHeight * minBigPercentage,
|
|
||||||
Math.min(bigHeight, bigDimensions.targetHeight * bigDimensions.targetRows)
|
|
||||||
);
|
|
||||||
// Don't awkwardly scale the small area bigger than we need to and end up with floating
|
|
||||||
// videos in the middle
|
|
||||||
const smallDimensions = this.getBestDimensions(
|
|
||||||
minRatio,
|
|
||||||
maxRatio,
|
|
||||||
containerWidth,
|
|
||||||
containerHeight - bigHeight,
|
|
||||||
smallOnes.length,
|
|
||||||
smallMaxWidth,
|
|
||||||
smallMaxHeight
|
|
||||||
);
|
|
||||||
bigHeight = Math.max(bigHeight, containerHeight - smallDimensions.targetRows * smallDimensions.targetHeight);
|
|
||||||
}
|
|
||||||
offsetTop = bigHeight;
|
|
||||||
bigOffsetTop = containerHeight - offsetTop;
|
|
||||||
if (bigFirst === 'column') {
|
|
||||||
showBigFirst = false;
|
|
||||||
} else if (bigFirst === 'row') {
|
|
||||||
showBigFirst = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We are wide, going to take up the whole height and arrange the small
|
|
||||||
// guys on the right
|
|
||||||
bigHeight = containerHeight;
|
|
||||||
bigWidth = Math.floor(containerWidth * bigPercentage);
|
|
||||||
if (minBigPercentage > 0) {
|
|
||||||
// Find the best size for the big area
|
|
||||||
let bigDimensions;
|
|
||||||
if (!bigFixedRatio) {
|
|
||||||
bigDimensions = this.getBestDimensions(
|
|
||||||
bigMinRatio,
|
|
||||||
bigMaxRatio,
|
|
||||||
bigWidth,
|
|
||||||
bigHeight,
|
|
||||||
bigOnes.length,
|
|
||||||
bigMaxWidth,
|
|
||||||
bigMaxHeight
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Use the ratio of the first video element we find to approximate
|
|
||||||
const ratio = bigOnes[0].height / bigOnes[0].width;
|
|
||||||
bigDimensions = this.getBestDimensions(
|
|
||||||
ratio,
|
|
||||||
ratio,
|
|
||||||
bigWidth,
|
|
||||||
bigHeight,
|
|
||||||
bigOnes.length,
|
|
||||||
bigMaxWidth,
|
|
||||||
bigMaxHeight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bigWidth = Math.max(
|
|
||||||
containerWidth * minBigPercentage,
|
|
||||||
Math.min(bigWidth, bigDimensions.targetWidth * bigDimensions.targetCols)
|
|
||||||
);
|
|
||||||
// Don't awkwardly scale the small area bigger than we need to and end up with floating
|
|
||||||
// videos in the middle
|
|
||||||
const smallDimensions = this.getBestDimensions(
|
|
||||||
minRatio,
|
|
||||||
maxRatio,
|
|
||||||
containerWidth - bigWidth,
|
|
||||||
containerHeight,
|
|
||||||
smallOnes.length,
|
|
||||||
smallMaxWidth,
|
|
||||||
smallMaxHeight
|
|
||||||
);
|
|
||||||
bigWidth = Math.max(bigWidth, containerWidth - smallDimensions.targetCols * smallDimensions.targetWidth);
|
|
||||||
}
|
|
||||||
offsetLeft = bigWidth;
|
|
||||||
bigOffsetLeft = containerWidth - offsetLeft;
|
|
||||||
if (bigFirst === 'column') {
|
|
||||||
showBigFirst = true;
|
|
||||||
} else if (bigFirst === 'row') {
|
|
||||||
showBigFirst = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (showBigFirst) {
|
|
||||||
areas.big = {
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: bigWidth,
|
|
||||||
height: bigHeight
|
|
||||||
};
|
|
||||||
areas.small = {
|
|
||||||
top: offsetTop,
|
|
||||||
left: offsetLeft,
|
|
||||||
width: containerWidth - offsetLeft,
|
|
||||||
height: containerHeight - offsetTop
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
areas.big = {
|
|
||||||
left: bigOffsetLeft,
|
|
||||||
top: bigOffsetTop,
|
|
||||||
width: bigWidth,
|
|
||||||
height: bigHeight
|
|
||||||
};
|
|
||||||
areas.small = {
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: containerWidth - offsetLeft,
|
|
||||||
height: containerHeight - offsetTop
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (bigOnes.length > 0 && smallOnes.length === 0) {
|
|
||||||
// We only have one bigOne just center it
|
|
||||||
areas.big = {
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: containerWidth,
|
|
||||||
height: containerHeight
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
areas.small = {
|
// Only small elements: use full container
|
||||||
top: offsetTop,
|
areas = {
|
||||||
left: offsetLeft,
|
big: null,
|
||||||
width: containerWidth - offsetLeft,
|
small: { top: 0, left: 0, width: containerWidth, height: containerHeight }
|
||||||
height: containerHeight - offsetTop
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -778,7 +904,123 @@ export class OpenViduLayout {
|
||||||
smallBoxesIdx += 1;
|
smallBoxesIdx += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { boxes, areas };
|
|
||||||
|
const result = { boxes, areas };
|
||||||
|
// Cache the result for future use
|
||||||
|
this.layoutCache.set(cacheKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build layout rows from element ratios
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private buildLayoutRows(
|
||||||
|
ratios: number[],
|
||||||
|
dimensions: BestDimensions,
|
||||||
|
fixedRatio: boolean,
|
||||||
|
containerWidth: number,
|
||||||
|
maxHeight: number
|
||||||
|
): { rows: LayoutRow[]; totalRowHeight: number } {
|
||||||
|
const rows: LayoutRow[] = [];
|
||||||
|
let row: LayoutRow | undefined;
|
||||||
|
|
||||||
|
// Create rows and calculate their dimensions
|
||||||
|
for (let i = 0; i < ratios.length; i++) {
|
||||||
|
if (i % dimensions.targetCols === 0) {
|
||||||
|
row = { ratios: [], width: 0, height: 0 };
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
const ratio = ratios[i];
|
||||||
|
row.ratios.push(ratio);
|
||||||
|
const targetWidth = fixedRatio ? dimensions.targetHeight / ratio : dimensions.targetWidth;
|
||||||
|
row.width += targetWidth;
|
||||||
|
row.height = dimensions.targetHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust rows that exceed container width
|
||||||
|
let totalRowHeight = 0;
|
||||||
|
for (const r of rows) {
|
||||||
|
if (r.width > containerWidth) {
|
||||||
|
r.height = Math.floor(r.height * (containerWidth / r.width));
|
||||||
|
r.width = containerWidth;
|
||||||
|
}
|
||||||
|
totalRowHeight += r.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { rows, totalRowHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale rows to fill container height if needed
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private scaleRowsToFit(
|
||||||
|
rows: LayoutRow[],
|
||||||
|
totalRowHeight: number,
|
||||||
|
containerWidth: number,
|
||||||
|
containerHeight: number,
|
||||||
|
maxHeight: number
|
||||||
|
): number {
|
||||||
|
let remainingShortRows = rows.filter(r => r.width < containerWidth && r.height < maxHeight).length;
|
||||||
|
if (remainingShortRows === 0) return totalRowHeight;
|
||||||
|
|
||||||
|
let remainingHeightDiff = containerHeight - totalRowHeight;
|
||||||
|
let adjustedTotalHeight = 0;
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.width < containerWidth && remainingShortRows > 0) {
|
||||||
|
let extraHeight = remainingHeightDiff / remainingShortRows;
|
||||||
|
const maxExtraHeight = Math.floor(((containerWidth - row.width) / row.width) * row.height);
|
||||||
|
|
||||||
|
if (extraHeight > maxExtraHeight) {
|
||||||
|
extraHeight = maxExtraHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.width += Math.floor((extraHeight / row.height) * row.width);
|
||||||
|
row.height += extraHeight;
|
||||||
|
remainingHeightDiff -= extraHeight;
|
||||||
|
remainingShortRows -= 1;
|
||||||
|
}
|
||||||
|
adjustedTotalHeight += row.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustedTotalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate vertical offset based on alignment
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private calculateVerticalOffset(alignItems: LayoutAlignment, containerHeight: number, totalRowHeight: number): number {
|
||||||
|
switch (alignItems) {
|
||||||
|
case LayoutAlignment.START:
|
||||||
|
return 0;
|
||||||
|
case LayoutAlignment.END:
|
||||||
|
return containerHeight - totalRowHeight;
|
||||||
|
case LayoutAlignment.CENTER:
|
||||||
|
default:
|
||||||
|
return (containerHeight - totalRowHeight) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate horizontal offset based on alignment
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
private calculateHorizontalOffset(alignItems: LayoutAlignment, containerWidth: number, rowWidth: number): number {
|
||||||
|
switch (alignItems) {
|
||||||
|
case LayoutAlignment.START:
|
||||||
|
return 0;
|
||||||
|
case LayoutAlignment.END:
|
||||||
|
return containerWidth - rowWidth;
|
||||||
|
case LayoutAlignment.CENTER:
|
||||||
|
default:
|
||||||
|
return (containerWidth - rowWidth) / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLayoutAux(opts: Partial<OpenViduLayoutOptions & { containerWidth: number; containerHeight: number; offsetLeft: number; offsetTop: number }>, elements: ElementDimensions[]): LayoutBox[] {
|
private getLayoutAux(opts: Partial<OpenViduLayoutOptions & { containerWidth: number; containerHeight: number; offsetLeft: number; offsetTop: number }>, elements: ElementDimensions[]): LayoutBox[] {
|
||||||
|
|
@ -798,125 +1040,33 @@ export class OpenViduLayout {
|
||||||
const ratios = elements.map((element) => element.height / element.width);
|
const ratios = elements.map((element) => element.height / element.width);
|
||||||
const count = ratios.length;
|
const count = ratios.length;
|
||||||
|
|
||||||
let dimensions;
|
// Calculate target dimensions for elements
|
||||||
|
const targetRatio = fixedRatio && ratios.length > 0 ? ratios[0] : null;
|
||||||
|
const dimensions = targetRatio
|
||||||
|
? this.getBestDimensions(targetRatio, targetRatio, containerWidth, containerHeight, count, maxWidth, maxHeight)
|
||||||
|
: this.getBestDimensions(minRatio, maxRatio, containerWidth, containerHeight, count, maxWidth, maxHeight);
|
||||||
|
|
||||||
if (!fixedRatio) {
|
// Build and adjust rows
|
||||||
dimensions = this.getBestDimensions(minRatio, maxRatio, containerWidth, containerHeight, count, maxWidth, maxHeight);
|
const { rows, totalRowHeight } = this.buildLayoutRows(ratios, dimensions, fixedRatio, containerWidth, maxHeight);
|
||||||
} else {
|
const finalRowHeight = scaleLastRow && totalRowHeight < containerHeight
|
||||||
// Use the ratio of the first video element we find to approximate
|
? this.scaleRowsToFit(rows, totalRowHeight, containerWidth, containerHeight, maxHeight)
|
||||||
const ratio = ratios.length > 0 ? ratios[0] : LAYOUT_CONSTANTS.DEFAULT_MIN_RATIO;
|
: totalRowHeight;
|
||||||
dimensions = this.getBestDimensions(ratio, ratio, containerWidth, containerHeight, count, maxWidth, maxHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through each stream in the container and place it inside
|
// Calculate starting position
|
||||||
let x = 0;
|
let y = this.calculateVerticalOffset(alignItems, containerHeight, finalRowHeight);
|
||||||
let y = 0;
|
|
||||||
const rows: LayoutRow[] = [];
|
// Position elements in rows
|
||||||
let row: LayoutRow | undefined;
|
|
||||||
const boxes: LayoutBox[] = [];
|
const boxes: LayoutBox[] = [];
|
||||||
|
for (const row of rows) {
|
||||||
|
let x = this.calculateHorizontalOffset(alignItems, containerWidth, row.width);
|
||||||
|
|
||||||
// Iterate through the children and create an array with a new item for each row
|
for (const ratio of row.ratios) {
|
||||||
// and calculate the width of each row so that we know if we go over the size and need
|
|
||||||
// to adjust
|
|
||||||
for (let i = 0; i < ratios.length; i++) {
|
|
||||||
if (i % dimensions.targetCols === 0) {
|
|
||||||
// This is a new row
|
|
||||||
row = {
|
|
||||||
ratios: [],
|
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
};
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
const ratio = ratios[i];
|
|
||||||
if (row) {
|
|
||||||
row.ratios.push(ratio);
|
|
||||||
let targetWidth = dimensions.targetWidth;
|
let targetWidth = dimensions.targetWidth;
|
||||||
const targetHeight = dimensions.targetHeight;
|
const targetHeight = row.height;
|
||||||
// If we're using a fixedRatio then we need to set the correct ratio for this element
|
|
||||||
if (fixedRatio) {
|
|
||||||
targetWidth = targetHeight / ratio;
|
|
||||||
}
|
|
||||||
row.width += targetWidth;
|
|
||||||
row.height = targetHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Calculate total row height adjusting if we go too wide
|
|
||||||
let totalRowHeight = 0;
|
|
||||||
let remainingShortRows = 0;
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
row = rows[i];
|
|
||||||
if (row.width > containerWidth) {
|
|
||||||
// Went over on the width, need to adjust the height proportionally
|
|
||||||
row.height = Math.floor(row.height * (containerWidth / row.width));
|
|
||||||
row.width = containerWidth;
|
|
||||||
} else if (row.width < containerWidth && row.height < maxHeight) {
|
|
||||||
remainingShortRows += 1;
|
|
||||||
}
|
|
||||||
totalRowHeight += row.height;
|
|
||||||
}
|
|
||||||
if (scaleLastRow && totalRowHeight < containerHeight && remainingShortRows > 0) {
|
|
||||||
// We can grow some of the rows, we're not taking up the whole height
|
|
||||||
let remainingHeightDiff = containerHeight - totalRowHeight;
|
|
||||||
totalRowHeight = 0;
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
row = rows[i];
|
|
||||||
if (row.width < containerWidth) {
|
|
||||||
// Evenly distribute the extra height between the short rows
|
|
||||||
let extraHeight = remainingHeightDiff / remainingShortRows;
|
|
||||||
if (extraHeight / row.height > (containerWidth - row.width) / row.width) {
|
|
||||||
// We can't go that big or we'll go too wide
|
|
||||||
extraHeight = Math.floor(((containerWidth - row.width) / row.width) * row.height);
|
|
||||||
}
|
|
||||||
row.width += Math.floor((extraHeight / row.height) * row.width);
|
|
||||||
row.height += extraHeight;
|
|
||||||
remainingHeightDiff -= extraHeight;
|
|
||||||
remainingShortRows -= 1;
|
|
||||||
}
|
|
||||||
totalRowHeight += row.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// vertical centering
|
|
||||||
switch (alignItems) {
|
|
||||||
case 'start':
|
|
||||||
y = 0;
|
|
||||||
break;
|
|
||||||
case 'end':
|
|
||||||
y = containerHeight - totalRowHeight;
|
|
||||||
break;
|
|
||||||
case 'center':
|
|
||||||
default:
|
|
||||||
y = (containerHeight - totalRowHeight) / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Iterate through each row and place each child
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
row = rows[i];
|
|
||||||
let rowMarginLeft;
|
|
||||||
switch (alignItems) {
|
|
||||||
case 'start':
|
|
||||||
rowMarginLeft = 0;
|
|
||||||
break;
|
|
||||||
case 'end':
|
|
||||||
rowMarginLeft = containerWidth - row.width;
|
|
||||||
break;
|
|
||||||
case 'center':
|
|
||||||
default:
|
|
||||||
rowMarginLeft = (containerWidth - row.width) / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
x = rowMarginLeft;
|
|
||||||
let targetHeight = row.height;
|
|
||||||
for (let j = 0; j < row.ratios.length; j++) {
|
|
||||||
const ratio = row.ratios[j];
|
|
||||||
|
|
||||||
let targetWidth = dimensions.targetWidth;
|
|
||||||
targetHeight = row.height;
|
|
||||||
// If we're using a fixedRatio then we need to set the correct ratio for this element
|
|
||||||
if (fixedRatio) {
|
if (fixedRatio) {
|
||||||
targetWidth = Math.floor(targetHeight / ratio);
|
targetWidth = Math.floor(targetHeight / ratio);
|
||||||
} else if (targetHeight / targetWidth !== dimensions.targetHeight / dimensions.targetWidth) {
|
} else if (targetHeight / targetWidth !== dimensions.targetHeight / dimensions.targetWidth) {
|
||||||
// We grew this row, we need to adjust the width to account for the increase in height
|
|
||||||
targetWidth = Math.floor((dimensions.targetWidth / dimensions.targetHeight) * targetHeight);
|
targetWidth = Math.floor((dimensions.targetWidth / dimensions.targetHeight) * targetHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -928,7 +1078,7 @@ export class OpenViduLayout {
|
||||||
});
|
});
|
||||||
x += targetWidth;
|
x += targetWidth;
|
||||||
}
|
}
|
||||||
y += targetHeight;
|
y += row.height;
|
||||||
}
|
}
|
||||||
return boxes;
|
return boxes;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ export class LayoutService {
|
||||||
protected getOptions(): OpenViduLayoutOptions {
|
protected getOptions(): OpenViduLayoutOptions {
|
||||||
const ratios = this.getResponsiveRatios();
|
const ratios = this.getResponsiveRatios();
|
||||||
const percentages = this.getResponsivePercentages();
|
const percentages = this.getResponsivePercentages();
|
||||||
|
const isMobile = this.viewportSrv.isMobile();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
maxRatio: ratios.maxRatio,
|
maxRatio: ratios.maxRatio,
|
||||||
|
|
@ -94,7 +95,8 @@ export class LayoutService {
|
||||||
bigMaxRatio: ratios.bigMaxRatio,
|
bigMaxRatio: ratios.bigMaxRatio,
|
||||||
bigMinRatio: ratios.bigMinRatio,
|
bigMinRatio: ratios.bigMinRatio,
|
||||||
bigFirst: true,
|
bigFirst: true,
|
||||||
animate: true,
|
// Disable animations on mobile for better performance
|
||||||
|
animate: !isMobile,
|
||||||
alignItems: LayoutAlignment.CENTER,
|
alignItems: LayoutAlignment.CENTER,
|
||||||
bigAlignItems: LayoutAlignment.CENTER,
|
bigAlignItems: LayoutAlignment.CENTER,
|
||||||
smallAlignItems: LayoutAlignment.CENTER,
|
smallAlignItems: LayoutAlignment.CENTER,
|
||||||
|
|
@ -105,7 +107,7 @@ export class LayoutService {
|
||||||
bigMaxWidth: Infinity,
|
bigMaxWidth: Infinity,
|
||||||
bigMaxHeight: Infinity,
|
bigMaxHeight: Infinity,
|
||||||
scaleLastRow: true,
|
scaleLastRow: true,
|
||||||
bigScaleLastRow: true
|
bigScaleLastRow: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue