FOR REALS account for offset applied by sites that use top:50%/transformY:-50% to center elements

This commit is contained in:
Tamius Han 2025-01-02 04:33:00 +01:00
parent d981e4c2c6
commit 9f10c0dcf4
2 changed files with 108 additions and 3 deletions

View File

@ -19,6 +19,7 @@ import Settings from '../Settings';
import { Ar } from '../../../common/interfaces/ArInterface';
import { RunLevel } from '../../enum/run-level.enum';
import * as _ from 'lodash';
import getElementStyles from '../../util/getElementStyles';
if(Debug.debug) {
console.log("Loading: Resizer.js");
@ -692,7 +693,6 @@ class Resizer {
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 computedStyles = getComputedStyle(this.video);
// correct any remaining element size discrepancies (applicable only to certain crop strategies!)
// NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy
@ -716,10 +716,16 @@ class Resizer {
// we only need to compensate if alignment is set to anything other than center center
// compensation is equal to half the difference between (zoomed) video size and player size.
const translate = {
x: -Math.round(+ (computedStyles.left.replace('px', ''))),
y: -Math.round(+ (computedStyles.top.replace('px', '')))
x: 0,
y: 0
};
const problemStats = getElementStyles(this.video, ['top', 'left', 'transform']);
if (problemStats.left?.css && problemStats.top?.css && problemStats.transform?.css?.includes(`translate(-${problemStats.left.css}, -${problemStats.top.css})`)) {
translate.x -= ~~problemStats.left.pxValue;
translate.y -= ~~problemStats.top.pxValue;
}
// NOTE: manual panning is probably broken now.
// TODO: FIXME:
// (argument could be made that manual panning was also broken before)
@ -889,6 +895,7 @@ class Resizer {
if (stretchFactors) {
styleArray.push(`transform: translate(${Math.round(translate.x)}px, ${Math.round(translate.y)}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor}) !important;`);
}
const styleString = `${this.buildStyleString(styleArray)}${extraStyleString || ''}`; // string returned by buildStyleString() should end with ; anyway
// build style string back

View File

@ -0,0 +1,98 @@
export type ProcessedElementStyles = {
[x: string]: {
css: string,
pxValue: number
}
};
/**
* Note that this function is written _very_ dangerously
* and includes absolutely no error handling
*/
function getPixelValue(value: string, element?: HTMLElement, prop?: string) {
if (value === undefined || value === null) {
return null;
}
if (value.endsWith('px')) {
// console.log('value ends in px:', value)
return parseFloat(value);
}
if (value.endsWith('%')) {
// console.log('value ends in %:', value)
// This allegedly doesn't work for certain types of properties, allegedly.
const parent = element?.parentElement;
if (parent && prop) {
const parentDimensions = parent.getBoundingClientRect();
return (parseFloat(value) * 0.01) * (prop.includes('height') || ['top', 'bottom'].includes(prop) ? parentDimensions.height : parentDimensions.width);
} else {
return null;
}
}
if (value.endsWith("vw")) {
const viewportWidth = window.innerWidth;
return (parseFloat(value) / 100) * viewportWidth;
}
if (value.endsWith("vh")) {
const viewportHeight = window.innerHeight;
return (parseFloat(value) / 100) * viewportHeight;
}
if (value.endsWith("rem")) {
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
return rootFontSize * parseFloat(value);
}
if (value.endsWith("em")) {
const fontSize = parseFloat(getComputedStyle(element).fontSize);
return fontSize * parseFloat(value);
}
};
export default function getElementStyles(element: HTMLElement, props: string[]): ProcessedElementStyles {
const stylesheets = document.styleSheets;
const computedStyles = getComputedStyle(element);
const stylesOut = {};
for (const stylesheet of stylesheets) {
// console.log('——————————————— processing stylesheet:', stylesheet);
try {
for (const rule of stylesheet?.cssRules) {
if (rule instanceof CSSStyleRule) {
if (element.matches(rule.selectorText)) {
// console.log('element matches rule:', rule);
for (const property in rule.style) {
if (!props.includes(property)) {
continue;
}
const cssValue = rule.style.getPropertyValue(property);
const actualValue = computedStyles.getPropertyValue(property);
const theory = getPixelValue(cssValue, element, property);
const practice = getPixelValue(actualValue, element, property);
if (theory === practice) {
stylesOut[property] = {
css: cssValue,
pxValue: theory
}
}
}
}
}
}
} catch (e) {
// Cross-origin styles amy cause problems
}
}
return stylesOut;
}