Files
twenty/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneMainThreadController.ts
T
Raphaël Bosi ada7eb1d88 Restore the Figma halftone shapes and calm the welcome animation (#23495)
https://github.com/user-attachments/assets/0c06d190-977e-4ad6-8a02-251f341ee310



The welcome overlay settled into circles instead of the halftone from
Figma: the densify step that took the dot set from 681 to 2897 emitted
points, so every dash had zero length. All 681 dashes in the Figma
export (`public/images/onboarding/welcome-halftone.svg`) share a
constant length / stroke width ratio of 1.452, so the shape is restored
by deriving the length from the stroke width, with no data regeneration.
Dashes now stretch into shape while they are still flying in, rather
than popping once they have landed.

The rest of the pass makes the animation quieter. The shine sweep is
gone, along with the highlight colour that only fed it. Particles
approach from much closer on a gentler ease, the idle drift is roughly
halved, and the exit is a soft outward drift instead of a burst that
threw everything off screen. The white pill behind the title and the
person chip's surface are removed too, so the title reads directly
against the halftone.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23495?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. -->
2026-07-29 16:14:46 +00:00

71 lines
2.1 KiB
TypeScript

import { isDefined } from 'twenty-shared/utils';
import { WELCOME_HALFTONE_DASHES } from '@/onboarding/components/WelcomeOverlay/welcomeHalftoneDots';
import { type WelcomeHalftoneController } from '@/onboarding/components/WelcomeOverlay/welcomeHalftoneController.type';
import { createWelcomeHalftoneRenderer } from '@/onboarding/components/WelcomeOverlay/welcomeHalftoneRenderer';
type CreateWelcomeHalftoneMainThreadControllerOptions = {
canvas: HTMLCanvasElement;
dotColor: string;
prefersReducedMotion: boolean;
initialCanvasWidth: number;
initialCanvasHeight: number;
devicePixelRatio: number;
};
export const createWelcomeHalftoneMainThreadController = ({
canvas,
dotColor,
prefersReducedMotion,
initialCanvasWidth,
initialCanvasHeight,
devicePixelRatio,
}: CreateWelcomeHalftoneMainThreadControllerOptions): WelcomeHalftoneController | null => {
let renderingContext: CanvasRenderingContext2D | null = null;
try {
renderingContext = canvas.getContext('2d');
} catch {
renderingContext = null;
}
if (!isDefined(renderingContext)) {
return null;
}
const applyBackingStoreSize = (
canvasWidth: number,
canvasHeight: number,
activeDevicePixelRatio: number,
) => {
canvas.width = Math.round(canvasWidth * activeDevicePixelRatio);
canvas.height = Math.round(canvasHeight * activeDevicePixelRatio);
};
applyBackingStoreSize(
initialCanvasWidth,
initialCanvasHeight,
devicePixelRatio,
);
const renderer = createWelcomeHalftoneRenderer({
context: renderingContext,
dashes: WELCOME_HALFTONE_DASHES,
width: initialCanvasWidth,
height: initialCanvasHeight,
devicePixelRatio,
color: dotColor,
reducedMotion: prefersReducedMotion,
});
return {
leave: () => renderer.leave(),
resize: (nextCanvasWidth, nextCanvasHeight, nextDevicePixelRatio) => {
applyBackingStoreSize(
nextCanvasWidth,
nextCanvasHeight,
nextDevicePixelRatio,
);
renderer.resize(nextCanvasWidth, nextCanvasHeight, nextDevicePixelRatio);
},
destroy: () => renderer.destroy(),
};
};