To optimize UX and page-loads I have three different UI states, blank, with background, and animated. By default the page loads without any backgrounds or animation.
The effect I want to achieve is that the background image aligns with the animation of Vanta.js.
For reference, here are two screenshots on my extra wide monitor (I removed the content for privacy reasons and to make the image better visible):
As you can see the horizons don't align. This is because I took the background picture as screenshot of the animation on a smaller screen with a different size.
Vanta.js doesn't seem to center the horizon instead it depends on the dimensions of the viewport.
Is there anything I can do to always have the horizon at the same line regardless of screen size?
To see it in real life, go to https://state-less.cloud/ and hit the magic wand icon in the top right. Once for the background and a second time for the animation.
Depending on your screen size you will see it jumps up (or down) when you enable the animation.
The code I use for rendering the Vanta.js background is the following:
export const SunnyBlueClouds = {
type: 'CLOUD',
skyColor: 0x2096d7,
cloudColor: 0xc5c8fa,
sunColor: 0xdc7412,
sunlightColor: 0xe17833,
speed: 1,
};
type VantaBackgroudProps = {
enabled?: boolean;
light: any;
dark: any;
bg?: boolean;
};
export const VantaBackground: FunctionComponent<
PropsWithChildren<VantaBackgroudProps>
> = ({
enabled = false,
children,
light = SunnyBlueClouds,
dark = DarkFog,
bg,
}) => {
const instance = useRef<any>();
const theme = useTheme();
const { type, ...rest } = theme.palette.mode === 'light' ? light : dark;
const sharedProps = {
el: '#bg',
mouseControls: false,
touchControls: false,
gyroControls: false,
};
/** Destroy the background on unmount */
useEffect(() => {
return () => {
if (instance.current && instance.current.destroy) {
instance.current.destroy();
instance.current = null;
}
};
}, []);
const render = useMemo(
() => () => {
const fn = window.VANTA[type] || window.VANTA.CLOUDS;
if (enabled && !instance.current) {
instance.current = fn({
...sharedProps,
...rest,
});
} else if (!enabled && instance.current && instance.current.destroy) {
instance.current.destroy();
instance.current = null;
}
},
[enabled, dark, light, type]
);
useEffect(() => {
if (!window.VANTA) return;
render();
return () => {
if (instance.current && instance.current.destroy)
instance.current.destroy();
instance.current = null;
};
}, [enabled, dark, light, type]);
useEffect(() => {
document.getElementById('vanta')?.addEventListener('load', render);
return () => {
document.getElementById('vanta')?.removeEventListener('load', render);
};
});
useEffect(() => {
if (instance.current && instance.current.updateUniforms) {
Object.assign(instance.current.options, {
...rest,
...sharedProps,
});
instance.current.updateUniforms();
}
}, [sharedProps, rest]);
return (
<div
id="bg"
className={clsx(theme.palette.mode, 'fh', {
bg,
animated: enabled,
})}
style={{ overflow: 'hidden' }}
>
{children}
</div>
);
};