Welcome animation when a workspace is set up (#22622)
## Desktop https://github.com/user-attachments/assets/87999ff5-9257-4fed-90e0-781322b1db98 ## Mobile https://github.com/user-attachments/assets/0d115cb6-7bca-4229-8fde-0f188e862b00 Greets the user the moment they finish onboarding: a halftone Twenty mark (brand purple) assembles from scattered particles, shimmers, then bursts outward to reveal the freshly loaded workspace behind it. Plays once, ~3s, with a reduced-motion fallback. `PageChangeEffect` sets a transient `isWelcomeAnimationVisibleState` atom at the onboarding to workspace redirect seam (gated by `shouldShowWelcomeAnimationOnNavigate`); `WelcomeOverlay`, mounted in `WorkspaceAppProviders`, plays it and clears it. Transient by design, so it never replays on reload. ## Why a canvas rendered on another thread The overlay plays at the exact moment the workspace mounts behind it, which is one of the heaviest main-thread stretches in the app (metadata, providers, first data fetch). Anything animating on the main thread competes with that work: - framer-motion and plain main-thread `requestAnimationFrame` visibly stuttered and froze during the mount, because long tasks starve rAF. - CSS keyframes stay smooth (they run on the compositor), but can't drive a ~2900-particle halftone with per-particle physics (staggered assemble, moving shimmer band, directional burst). So the halftone runs as a Canvas 2D particle system inside a Web Worker, drawing to an `OffscreenCanvas` transferred from the main thread. The whole render loop lives off the main thread, so app-mount jank can't reach it. A main-thread path is kept as a fallback for browsers without `OffscreenCanvas` / `transferControlToOffscreen`, and Safari workers (which lack rAF) fall back to a fixed-interval loop. The renderer is a pure, DOM-free module so it bundles cleanly into the worker; dot colors come from CSS vars read on the main thread and passed in. Also fires for every onboarding completion path and skips the billing `PlanRequired` detour. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22622?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
||||
import { OnboardingStatus } from '~/generated-metadata/graphql';
|
||||
|
||||
type ShouldShowWelcomeAnimationOnNavigateArgs = {
|
||||
onboardingStatus: OnboardingStatus | null | undefined;
|
||||
isOnOnboardingPage: boolean;
|
||||
isNavigatingToOnboardingOrAuthPath: boolean;
|
||||
};
|
||||
|
||||
export const shouldShowWelcomeAnimationOnNavigate = ({
|
||||
onboardingStatus,
|
||||
isOnOnboardingPage,
|
||||
isNavigatingToOnboardingOrAuthPath,
|
||||
}: ShouldShowWelcomeAnimationOnNavigateArgs): boolean =>
|
||||
onboardingStatus === OnboardingStatus.COMPLETED &&
|
||||
isOnOnboardingPage &&
|
||||
!isNavigatingToOnboardingOrAuthPath;
|
||||
Reference in New Issue
Block a user