fix: stabilize portfolio scrolling and loading
Remove skeleton swaps and deferred layout estimates, restore reveal state, and use native proximity scrolling for project cards. Also make the Docker build npm registry configurable.
This commit is contained in:
@@ -269,7 +269,7 @@ const createAboutScene: SceneFactory = ({ pointer, theme, reduceMotion }) => {
|
||||
};
|
||||
};
|
||||
|
||||
export function AboutRiverScene({ onReady }: { onReady?: () => void }) {
|
||||
export function AboutRiverScene() {
|
||||
const { theme } = useTheme();
|
||||
return <ThreeCanvas className={styles.aboutScene} createScene={createAboutScene} theme={theme} onReady={onReady} />;
|
||||
return <ThreeCanvas className={styles.aboutScene} createScene={createAboutScene} theme={theme} />;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ const AboutRiverScene = lazy(() =>
|
||||
export function DeferredAboutRiverScene() {
|
||||
const hostRef = useRef<HTMLDivElement>(null);
|
||||
const [shouldLoad, setShouldLoad] = useState(false);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const host = hostRef.current;
|
||||
@@ -28,11 +27,10 @@ export function DeferredAboutRiverScene() {
|
||||
}, [shouldLoad]);
|
||||
|
||||
return (
|
||||
<div ref={hostRef} className={styles.aboutSceneBoundary} aria-busy={!isReady} data-looping-motion>
|
||||
{!isReady && <span className={styles.sceneSkeleton} aria-hidden="true" />}
|
||||
<div ref={hostRef} className={styles.aboutSceneBoundary}>
|
||||
{shouldLoad && (
|
||||
<Suspense fallback={null}>
|
||||
<AboutRiverScene onReady={() => setIsReady(true)} />
|
||||
<AboutRiverScene />
|
||||
</Suspense>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -51,22 +51,6 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sceneSkeleton {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(100deg, transparent 24%, color-mix(in srgb, var(--bg-raised) 68%, transparent) 50%, transparent 76%),
|
||||
var(--bg-page);
|
||||
background-size: 200% 100%, auto;
|
||||
animation: sceneSkeletonShimmer 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes sceneSkeletonShimmer {
|
||||
to {
|
||||
background-position: -200% 0, 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutScene::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -114,9 +98,3 @@
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.sceneSkeleton {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ interface ThreeCanvasProps {
|
||||
className: string;
|
||||
createScene: SceneFactory;
|
||||
theme: SceneTheme;
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
function disposeObject(object: THREE.Object3D) {
|
||||
@@ -50,13 +49,8 @@ function disposeObject(object: THREE.Object3D) {
|
||||
});
|
||||
}
|
||||
|
||||
export function ThreeCanvas({ className, createScene, theme, onReady }: ThreeCanvasProps) {
|
||||
export function ThreeCanvas({ className, createScene, theme }: ThreeCanvasProps) {
|
||||
const hostRef = useRef<HTMLDivElement>(null);
|
||||
const onReadyRef = useRef(onReady);
|
||||
|
||||
useEffect(() => {
|
||||
onReadyRef.current = onReady;
|
||||
}, [onReady]);
|
||||
|
||||
useEffect(() => {
|
||||
const host = hostRef.current;
|
||||
@@ -70,7 +64,6 @@ export function ThreeCanvas({ className, createScene, theme, onReady }: ThreeCan
|
||||
let frame = 0;
|
||||
let visible = true;
|
||||
let pageVisible = !document.hidden;
|
||||
let hasRendered = false;
|
||||
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
@@ -81,10 +74,6 @@ export function ThreeCanvas({ className, createScene, theme, onReady }: ThreeCan
|
||||
|
||||
const render = () => {
|
||||
renderer.render(controller.scene, controller.camera);
|
||||
if (!hasRendered) {
|
||||
hasRendered = true;
|
||||
onReadyRef.current?.();
|
||||
}
|
||||
};
|
||||
const resize = () => {
|
||||
const bounds = host.getBoundingClientRect();
|
||||
|
||||
Reference in New Issue
Block a user