fix vertical alignment

This commit is contained in:
Tamius Han 2024-12-30 03:05:47 +01:00
parent 25222c7310
commit cd89cca984
2 changed files with 65 additions and 58 deletions

View File

@ -278,7 +278,7 @@ class Resizer {
} }
if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(ar.type)) { if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(ar.type)) {
console.log('run level is UI only because aspect ratio type is', ar.type) // console.log('run level is UI only because aspect ratio type is', ar.type)
this.eventBus.send('set-run-level', RunLevel.UIOnly); this.eventBus.send('set-run-level', RunLevel.UIOnly);
} else { } else {
this.eventBus.send('set-run-level', RunLevel.CustomCSSActive); this.eventBus.send('set-run-level', RunLevel.CustomCSSActive);
@ -307,7 +307,7 @@ class Resizer {
return; return;
} }
let stretchFactors: {xFactor: number, yFactor: number, arCorrectionFactor?: number, ratio?: number} | any; let stretchFactors: VideoDimensions | any;
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to // reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
// AspectRatioType.Fixed when zooming, so let's keep that in mind // AspectRatioType.Fixed when zooming, so let's keep that in mind
@ -419,10 +419,10 @@ class Resizer {
); );
} }
this.applyScaling(stretchFactors); this.applyScaling(stretchFactors as VideoDimensions);
} }
applyScaling(stretchFactors, options?: {noAnnounce?: boolean}) { applyScaling(stretchFactors: VideoDimensions, options?: {noAnnounce?: boolean, ar?: Ar}) {
this.stretcher.chromeBugMitigation(stretchFactors); this.stretcher.chromeBugMitigation(stretchFactors);
// let the UI know // let the UI know
@ -430,7 +430,7 @@ class Resizer {
this.videoData.eventBus.send('announce-zoom', {x: stretchFactors.xFactor, y: stretchFactors.yFactor}); this.videoData.eventBus.send('announce-zoom', {x: stretchFactors.xFactor, y: stretchFactors.yFactor});
} }
let translate = this.computeOffsets(stretchFactors); let translate = this.computeOffsets(stretchFactors, options?.ar);
this.applyCss(stretchFactors, translate); this.applyCss(stretchFactors, translate);
} }
@ -457,7 +457,7 @@ class Resizer {
if(!this.videoData.player || !this.videoData.player.element) { if(!this.videoData.player || !this.videoData.player.element) {
return; return;
} }
// dont allow weird floats // don't allow weird floats
this.videoAlignment.x = VideoAlignmentType.Center; this.videoAlignment.x = VideoAlignmentType.Center;
// because non-fixed aspect ratios reset panning: // because non-fixed aspect ratios reset panning:
@ -501,14 +501,14 @@ class Resizer {
setVideoAlignment(videoAlignmentX: VideoAlignmentType, videoAlignmentY?: VideoAlignmentType) { setVideoAlignment(videoAlignmentX: VideoAlignmentType, videoAlignmentY?: VideoAlignmentType) {
// if aspect ratio is unset or initial, CSS fixes are inactive by design. // if aspect ratio is unset or initial, CSS fixes are inactive by design.
// because of that, we need to set a manual aspect ratio first. // because of that, we need to set a manual aspect ratio first.
console.log('last aspect ratio:', this.lastAr);
if (!this.lastAr) { if (!this.lastAr?.ratio) {
console.warn('[Resizer.js::setVideoAlignment] Aspect ratio not set. This is illegal. This function will do nothing.');
this.setAr({ this.setAr({
type: AspectRatioType.Fixed, type: AspectRatioType.AutomaticUpdate,
ratio: this.getFileAr() ratio: this.getFileAr()
}); });
} }
if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(this.lastAr.type)) { if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(this.lastAr.type)) {
if (this.lastAr.ratio) { if (this.lastAr.ratio) {
this.lastAr.type = AspectRatioType.Fixed; this.lastAr.type = AspectRatioType.Fixed;
@ -659,36 +659,6 @@ class Resizer {
} }
} }
computeCroppedAreas(stretchFactors) {
// PSA: offsetWidth and offsetHeight DO NOT INCLUDE
// ZOOM APPLIED THROUGH THE MAGIC OF CSS TRANSFORMS
const sourceWidth = this.videoData.video.offsetWidth;
const sourceHeight = this.videoData.video.offsetHeight;
// this is the size of the video AFTER zooming was applied but does
// not account for cropping. It may be bigger than the player in
// both dimensions. It may be smaller than player in both dimensions
const postZoomWidth = sourceWidth * stretchFactors.xFactor;
const postZoomHeight = sourceHeight * stretchFactors.yFactor;
// this is the size of the video after crop is applied
const displayedWidth = Math.min(this.videoData.player.dimensions.width, postZoomWidth);
const displayedHeight = Math.min(this.videoData.player.dimensions.height, postZoomHeight);
// these two are cropped areas. Negative values mean additional
// letterboxing or pillarboxing. We assume center alignment for
// the time being - we will correct that later if need be
const croppedX = (postZoomWidth - displayedWidth) * 0.5;
const croppedY = (postZoomHeight - displayedHeight) * 0.5;
return {
sourceVideoDimensions: {width: sourceWidth, height: sourceHeight},
postZoomVideoDimensions: {width: postZoomWidth, height: postZoomHeight},
displayedVideoDimensions: {width: displayedWidth, height: displayedHeight},
crop: {left: croppedX, top: croppedY},
};
}
/** /**
* Sometimes, sites (e.g. new reddit) will guarantee that video fits width of its container * Sometimes, sites (e.g. new reddit) will guarantee that video fits width of its container
* and let the browser figure out the height through the magic of height:auto. This is bad, * and let the browser figure out the height through the magic of height:auto. This is bad,
@ -716,14 +686,12 @@ class Resizer {
} }
private _computeOffsetsRecursionGuard: boolean = false; private _computeOffsetsRecursionGuard: boolean = false;
computeOffsets(stretchFactors: VideoDimensions){ computeOffsets(stretchFactors: VideoDimensions, ar?: Ar){
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.videoAlignment); this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.videoAlignment);
const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions(); const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions();
const {postZoomVideoDimensions, displayedVideoDimensions, crop} = this.computeCroppedAreas(stretchFactors); // correct any remaining element size discrepancies (applicable only to certain crop strategies!)
// correct any remaining element size discrepencies (applicable only to certain crop strategies!)
// NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy // NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy
// (but we'll wait for bug reports before doing so). // (but we'll wait for bug reports before doing so).
// We also don't compensate for height:auto if height is provided via element style // We also don't compensate for height:auto if height is provided via element style
@ -732,7 +700,11 @@ class Resizer {
stretchFactors.cropStrategy === CropStrategy.CropLetterbox stretchFactors.cropStrategy === CropStrategy.CropLetterbox
&& (!stretchFactors.styleHeightCompensationFactor || stretchFactors.styleHeightCompensationFactor === 1) && (!stretchFactors.styleHeightCompensationFactor || stretchFactors.styleHeightCompensationFactor === 1)
) { ) {
autoHeightCompensationFactor = this.computeAutoHeightCompensationFactor(realVideoWidth, realVideoHeight, this.videoData.player.dimensions.width, this.videoData.player.dimensions.height, 'height'); autoHeightCompensationFactor = this.computeAutoHeightCompensationFactor(
realVideoWidth, realVideoHeight,
this.videoData.player.dimensions.width, this.videoData.player.dimensions.height,
'height'
);
stretchFactors.xFactor *= autoHeightCompensationFactor; stretchFactors.xFactor *= autoHeightCompensationFactor;
stretchFactors.yFactor *= autoHeightCompensationFactor; stretchFactors.yFactor *= autoHeightCompensationFactor;
} }
@ -759,17 +731,21 @@ class Resizer {
} }
} else { } else {
// correct horizontal alignment according to the settings // correct horizontal alignment according to the settings
if (this.videoAlignment.x == VideoAlignmentType.Left) { if (!stretchFactors.preventAlignment?.x) {
translate.x += alignXOffset; if (this.videoAlignment.x == VideoAlignmentType.Left) {
} else if (this.videoAlignment.x == VideoAlignmentType.Right) { translate.x += stretchFactors?.relativeCropLimits?.left ? (this.videoData.player.dimensions.width * stretchFactors.relativeCropLimits.left): alignXOffset;
translate.x -= alignXOffset } else if (this.videoAlignment.x == VideoAlignmentType.Right) {
translate.x -= stretchFactors?.relativeCropLimits?.left ? (this.videoData.player.dimensions.width * stretchFactors.relativeCropLimits.left): alignXOffset
}
} }
// correct vertical alignment according to the settings // correct vertical alignment according to the settings
if (this.videoAlignment.y == VideoAlignmentType.Top) { if (!stretchFactors.preventAlignment?.y) {
translate.y += alignYOffset; if (this.videoAlignment.y == VideoAlignmentType.Top) {
} else if (this.videoAlignment.y == VideoAlignmentType.Bottom) { translate.y += stretchFactors?.relativeCropLimits?.top ? (this.videoData.player.dimensions.height * stretchFactors?.relativeCropLimits?.top): alignYOffset;
translate.y -= alignYOffset; } else if (this.videoAlignment.y == VideoAlignmentType.Bottom) {
translate.y -= stretchFactors?.relativeCropLimits?.top ? (this.videoData.player.dimensions.height * stretchFactors?.relativeCropLimits?.top): alignYOffset;
}
} }
} }

View File

@ -29,6 +29,14 @@ export type VideoDimensions = {
styleHeightCompensationFactor?: number; styleHeightCompensationFactor?: number;
actualWidth?: number; actualWidth?: number;
actualHeight?: number; actualHeight?: number;
relativeCropLimits?: {
top: number;
left: number;
},
preventAlignment?: {
x: boolean,
y: boolean
}
} }
// does video size calculations for zooming/cropping // does video size calculations for zooming/cropping
@ -92,7 +100,7 @@ class Scaler {
return null; return null;
} }
calculateCrop(ar: {type: AspectRatioType, ratio?: number}) { calculateCrop(ar: {type: AspectRatioType, ratio?: number}): VideoDimensions | {error: string, [x: string]: any} {
/** /**
* STEP 1: NORMALIZE ASPECT RATIO * STEP 1: NORMALIZE ASPECT RATIO
* *
@ -142,7 +150,16 @@ class Scaler {
} }
if (ar.type === AspectRatioType.Reset){ if (ar.type === AspectRatioType.Reset){
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor}; return {
xFactor: arCorrectionFactor,
yFactor: arCorrectionFactor,
arCorrectionFactor: arCorrectionFactor,
relativeCropLimits: {
top: 0,
left: 0,
}
};
} }
// handle fuckie-wuckies // handle fuckie-wuckies
@ -175,7 +192,11 @@ class Scaler {
actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
arCorrectionFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor,
styleHeightCompensationFactor: heightCompensationFactor styleHeightCompensationFactor: heightCompensationFactor,
relativeCropLimits: {
top: 0,
left: 0
}
} }
this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr) this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr)
@ -223,14 +244,24 @@ class Scaler {
} }
} }
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
// correct the scale factor // correct the scale factor
if (videoDimensions.arCorrectionFactor) { if (videoDimensions.arCorrectionFactor) {
videoDimensions.xFactor *= videoDimensions.arCorrectionFactor; videoDimensions.xFactor *= videoDimensions.arCorrectionFactor;
videoDimensions.yFactor *= videoDimensions.arCorrectionFactor; videoDimensions.yFactor *= videoDimensions.arCorrectionFactor;
} }
// Add crop limits — needed for vertical alignment in order to
const letterboxRatio = (1 - (playerAr / ar));
videoDimensions.relativeCropLimits = {
top: ar > streamAr ? ( ar > playerAr ? (letterboxRatio * -0.5) : 0) : 0,
left: ar < streamAr ? ( ar < playerAr ? (-0.5 / letterboxRatio) : 0) : 0,
}
videoDimensions.preventAlignment = {
x: ar > playerAr, // video is wider than player, so it's full width already
y: ar < playerAr, // video is narrower than player, so it's full height already
}
return videoDimensions; return videoDimensions;
} }
} }