diff --git a/web/src/components/CameraImage.jsx b/web/src/components/CameraImage.jsx index d11054db9..98754e506 100644 --- a/web/src/components/CameraImage.jsx +++ b/web/src/components/CameraImage.jsx @@ -11,7 +11,15 @@ export default function CameraImage({ camera, onload, searchParams = '', stretch const [hasLoaded, setHasLoaded] = useState(false); const containerRef = useRef(null); const canvasRef = useRef(null); - const [{ width: availableWidth }] = useResizeObserver(containerRef); + const [{ width: containerWidth }] = useResizeObserver(containerRef); + + // Add scrollbar width (when visible) to the available observer width to eliminate screen juddering. + // https://github.com/blakeblackshear/frigate/issues/1657 + let scrollBarWidth = 0; + if (window.innerWidth && document.body.offsetWidth) { + scrollBarWidth = window.innerWidth - document.body.offsetWidth; + } + const availableWidth = scrollBarWidth ? containerWidth + scrollBarWidth : containerWidth; const { name } = config ? config.cameras[camera] : ''; const enabled = config ? config.cameras[camera].enabled : 'True'; @@ -22,7 +30,11 @@ export default function CameraImage({ camera, onload, searchParams = '', stretch const scaledHeight = Math.floor(availableWidth / aspectRatio); return stretch ? scaledHeight : Math.min(scaledHeight, height); }, [availableWidth, aspectRatio, height, stretch]); - const scaledWidth = useMemo(() => Math.ceil(scaledHeight * aspectRatio), [scaledHeight, aspectRatio]); + const scaledWidth = useMemo(() => Math.ceil(scaledHeight * aspectRatio - scrollBarWidth), [ + scaledHeight, + aspectRatio, + scrollBarWidth, + ]); const img = useMemo(() => new Image(), []); img.onload = useCallback(