From ada7eb1d88e0baed9e9d652bcee78f7ce54d0312 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?=
<71827178+bosiraphael@users.noreply.github.com>
Date: Wed, 29 Jul 2026 18:14:46 +0200
Subject: [PATCH] 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.
---
.../WelcomeHalftoneCanvasEffect.tsx | 4 -
.../WelcomeOverlay/WelcomeOverlay.tsx | 18 -----
.../WelcomeOverlay/WelcomePersonChip.tsx | 5 --
.../buildWelcomeHalftoneParticles.test.ts | 75 +++++++++++++++++++
.../buildWelcomeHalftoneParticles.ts | 29 +++----
...eateWelcomeHalftoneMainThreadController.ts | 3 -
.../createWelcomeHalftoneWorkerController.ts | 3 -
.../WelcomeOverlay/welcomeHalftone.css | 1 -
.../WelcomeOverlay/welcomeHalftone.worker.ts | 2 -
.../welcomeHalftoneParticle.type.ts | 1 +
.../WelcomeOverlay/welcomeHalftoneRenderer.ts | 74 ++++++++----------
11 files changed, 123 insertions(+), 92 deletions(-)
create mode 100644 packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/__tests__/buildWelcomeHalftoneParticles.test.ts
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeHalftoneCanvasEffect.tsx b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeHalftoneCanvasEffect.tsx
index f5ee2a9513..5cca25a397 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeHalftoneCanvasEffect.tsx
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeHalftoneCanvasEffect.tsx
@@ -33,9 +33,6 @@ export const WelcomeHalftoneCanvasEffect = ({
const dotColor = canvasStyles
.getPropertyValue('--welcome-dot-color')
.trim();
- const dotHighlightColor =
- canvasStyles.getPropertyValue('--welcome-dot-highlight').trim() ||
- dotColor;
const readDevicePixelRatio = () =>
Math.min(window.devicePixelRatio || 1, 2);
const readCanvasClientSize = () => ({
@@ -47,7 +44,6 @@ export const WelcomeHalftoneCanvasEffect = ({
const sharedControllerOptions = {
canvas,
dotColor,
- dotHighlightColor,
prefersReducedMotion,
initialCanvasWidth: initialCanvasSize.width,
initialCanvasHeight: initialCanvasSize.height,
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeOverlay.tsx b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeOverlay.tsx
index 244e1bdfe1..dec8109d0f 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeOverlay.tsx
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/WelcomeOverlay.tsx
@@ -141,23 +141,6 @@ const StyledTitle = styled.div`
}
`;
-const StyledTitleSurface = styled.div`
- background: ${themeCssVariables.background.primary};
- border-radius: ${themeCssVariables.border.radius.pill};
- inset: 0;
- position: absolute;
-
- .is-flying & {
- animation: welcomeTitleSurfaceOut 0.24s ease-out forwards;
- }
-
- @keyframes welcomeTitleSurfaceOut {
- to {
- opacity: 0;
- }
- }
-`;
-
const StyledTitleBoldRun = styled.span`
align-items: center;
display: inline-flex;
@@ -303,7 +286,6 @@ export const WelcomeOverlay = () => {
className={titleClassName}
style={titleStyle}
>
-
{welcomeTitleWords.map((word, index) => (
`
align-items: center;
- background: ${themeCssVariables.background.transparent.light};
- border-radius: ${({ sizeVariant }) =>
- sizeVariant === 'compact'
- ? themeCssVariables.border.radius.sm
- : themeCssVariables.border.radius.md};
display: inline-flex;
gap: ${({ sizeVariant }) =>
sizeVariant === 'compact'
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/__tests__/buildWelcomeHalftoneParticles.test.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/__tests__/buildWelcomeHalftoneParticles.test.ts
new file mode 100644
index 0000000000..c8803b3782
--- /dev/null
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/__tests__/buildWelcomeHalftoneParticles.test.ts
@@ -0,0 +1,75 @@
+import { buildWelcomeHalftoneParticles } from '@/onboarding/components/WelcomeOverlay/buildWelcomeHalftoneParticles';
+
+const CANVAS_WIDTH = 1280;
+const CANVAS_HEIGHT = 832;
+const VIEWBOX_WIDTH = 297.037;
+const MINIMUM_STROKE_WIDTH = 0.6;
+const VIEWBOX_TO_CANVAS_SCALE =
+ Math.max(CANVAS_WIDTH * 1.05, CANVAS_HEIGHT * 1.3) / VIEWBOX_WIDTH;
+
+describe('buildWelcomeHalftoneParticles', () => {
+ it('should elongate zero length dashes to the halftone aspect ratio', () => {
+ const { particles } = buildWelcomeHalftoneParticles(
+ [[148.5, 148.5, 119.5, 2]],
+ CANVAS_WIDTH,
+ CANVAS_HEIGHT,
+ );
+
+ expect(particles[0].dashLength).toBeCloseTo(
+ 2 * VIEWBOX_TO_CANVAS_SCALE * 1.452,
+ );
+ expect(particles[0].strokeWidth).toBeCloseTo(2 * VIEWBOX_TO_CANVAS_SCALE);
+ });
+
+ it('should keep the dash length when the source dash is longer than the aspect ratio', () => {
+ const { particles } = buildWelcomeHalftoneParticles(
+ [[148.5, 158.5, 119.5, 2]],
+ CANVAS_WIDTH,
+ CANVAS_HEIGHT,
+ );
+
+ expect(particles[0].dashLength).toBeCloseTo(10 * VIEWBOX_TO_CANVAS_SCALE);
+ });
+
+ it('should keep the dash length proportional to the source stroke width when the minimum stroke width applies', () => {
+ const smallCanvasWidth = 600;
+ const smallCanvasHeight = 500;
+ const smallCanvasScale =
+ Math.max(smallCanvasWidth * 1.05, smallCanvasHeight * 1.3) /
+ VIEWBOX_WIDTH;
+ const fainterSourceStrokeWidth = 0.19;
+ const denserSourceStrokeWidth = 0.25;
+
+ const { particles } = buildWelcomeHalftoneParticles(
+ [
+ [148.5, 148.5, 119.5, fainterSourceStrokeWidth],
+ [148.5, 148.5, 119.5, denserSourceStrokeWidth],
+ ],
+ smallCanvasWidth,
+ smallCanvasHeight,
+ );
+
+ expect(particles[0].strokeWidth).toBe(MINIMUM_STROKE_WIDTH);
+ expect(particles[1].strokeWidth).toBe(MINIMUM_STROKE_WIDTH);
+ expect(particles[0].dashLength).toBeCloseTo(
+ fainterSourceStrokeWidth * smallCanvasScale * 1.452,
+ );
+ expect(particles[1].dashLength / particles[0].dashLength).toBeCloseTo(
+ denserSourceStrokeWidth / fainterSourceStrokeWidth,
+ );
+ });
+
+ it('should point the burst direction outward from the canvas center', () => {
+ const { particles } = buildWelcomeHalftoneParticles(
+ [
+ [0, 0, 119.5, 2],
+ [297.037, 297.037, 119.5, 2],
+ ],
+ CANVAS_WIDTH,
+ CANVAS_HEIGHT,
+ );
+
+ expect(particles[0].burstDirectionX).toBeLessThan(0);
+ expect(particles[1].burstDirectionX).toBeGreaterThan(0);
+ });
+});
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/buildWelcomeHalftoneParticles.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/buildWelcomeHalftoneParticles.ts
index 12723ac498..9cee630d8d 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/buildWelcomeHalftoneParticles.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/buildWelcomeHalftoneParticles.ts
@@ -7,6 +7,7 @@ const VIEWBOX_CENTER_Y = 119.5;
const ASSEMBLE_STAGGER_SECONDS = 0.18;
const ASSEMBLE_JITTER_SECONDS = 0.12;
const MINIMUM_STROKE_WIDTH = 0.6;
+const HALFTONE_DASH_LENGTH_TO_STROKE_WIDTH_RATIO = 1.452;
const pseudoRandomFromSeed = (seed: number) => {
const noise = Math.sin(seed) * 43758.5453;
@@ -44,26 +45,27 @@ export const buildWelcomeHalftoneParticles = (
);
const scatterAngle = pseudoRandomFromSeed(dashIndex * 1.3) * TAU;
const scatterRadius =
- halftoneSize * (0.35 + 0.5 * pseudoRandomFromSeed(dashIndex * 2.1));
+ halftoneSize * (0.1 + 0.1 * pseudoRandomFromSeed(dashIndex * 2.1));
+ const scaledStrokeWidth = dashStrokeWidth * viewboxToCanvasScale;
+ const burstDirectionX =
+ distanceToCenter > 0 ? (targetX - canvasCenterX) / distanceToCenter : 0;
+ const burstDirectionY =
+ distanceToCenter > 0
+ ? (targetY - canvasCenterY) / distanceToCenter
+ : -1;
return {
targetX,
targetY,
scatterStartX: canvasCenterX + Math.cos(scatterAngle) * scatterRadius,
scatterStartY: canvasCenterY + Math.sin(scatterAngle) * scatterRadius,
- dashLength: Math.max(dashEndX - dashStartX, 0) * viewboxToCanvasScale,
- strokeWidth: Math.max(
- dashStrokeWidth * viewboxToCanvasScale,
- MINIMUM_STROKE_WIDTH,
+ dashLength: Math.max(
+ (dashEndX - dashStartX) * viewboxToCanvasScale,
+ scaledStrokeWidth * HALFTONE_DASH_LENGTH_TO_STROKE_WIDTH_RATIO,
),
- burstDirectionX:
- distanceToCenter > 0
- ? (targetX - canvasCenterX) / distanceToCenter
- : 0,
- burstDirectionY:
- distanceToCenter > 0
- ? (targetY - canvasCenterY) / distanceToCenter
- : -1,
+ strokeWidth: Math.max(scaledStrokeWidth, MINIMUM_STROKE_WIDTH),
+ burstDirectionX,
+ burstDirectionY,
distanceToCenter,
assembleDelaySeconds:
ASSEMBLE_STAGGER_SECONDS * (distanceToCenter / maxDistanceToCenter) +
@@ -72,6 +74,7 @@ export const buildWelcomeHalftoneParticles = (
positionAtLeaveStartX: 0,
positionAtLeaveStartY: 0,
opacityAtLeaveStart: 0,
+ capsuleLengthAtLeaveStart: 0,
};
},
);
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneMainThreadController.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneMainThreadController.ts
index 83dc0d23c1..01db37506a 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneMainThreadController.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneMainThreadController.ts
@@ -7,7 +7,6 @@ import { createWelcomeHalftoneRenderer } from '@/onboarding/components/WelcomeOv
type CreateWelcomeHalftoneMainThreadControllerOptions = {
canvas: HTMLCanvasElement;
dotColor: string;
- dotHighlightColor: string;
prefersReducedMotion: boolean;
initialCanvasWidth: number;
initialCanvasHeight: number;
@@ -17,7 +16,6 @@ type CreateWelcomeHalftoneMainThreadControllerOptions = {
export const createWelcomeHalftoneMainThreadController = ({
canvas,
dotColor,
- dotHighlightColor,
prefersReducedMotion,
initialCanvasWidth,
initialCanvasHeight,
@@ -54,7 +52,6 @@ export const createWelcomeHalftoneMainThreadController = ({
height: initialCanvasHeight,
devicePixelRatio,
color: dotColor,
- highlightColor: dotHighlightColor,
reducedMotion: prefersReducedMotion,
});
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneWorkerController.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneWorkerController.ts
index 3e8a1b1ed9..11964f616a 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneWorkerController.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/createWelcomeHalftoneWorkerController.ts
@@ -9,7 +9,6 @@ const WORKER_READY_TIMEOUT_MS = 1500;
type CreateWelcomeHalftoneWorkerControllerOptions = {
canvas: HTMLCanvasElement;
dotColor: string;
- dotHighlightColor: string;
prefersReducedMotion: boolean;
initialCanvasWidth: number;
initialCanvasHeight: number;
@@ -20,7 +19,6 @@ type CreateWelcomeHalftoneWorkerControllerOptions = {
export const createWelcomeHalftoneWorkerController = ({
canvas,
dotColor,
- dotHighlightColor,
prefersReducedMotion,
initialCanvasWidth,
initialCanvasHeight,
@@ -80,7 +78,6 @@ export const createWelcomeHalftoneWorkerController = ({
height: initialCanvasHeight,
devicePixelRatio,
color: dotColor,
- highlightColor: dotHighlightColor,
reducedMotion: prefersReducedMotion,
},
[offscreenCanvas],
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.css b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.css
index 84a8ccebc0..58346d0e49 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.css
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.css
@@ -1,4 +1,3 @@
:root {
--welcome-dot-color: #4a38f5;
- --welcome-dot-highlight: #9b91f9;
}
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.worker.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.worker.ts
index 88f8bbf3ef..776e71eace 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.worker.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftone.worker.ts
@@ -8,7 +8,6 @@ type InitMessage = {
height: number;
devicePixelRatio: number;
color: string;
- highlightColor: string;
reducedMotion: boolean;
};
@@ -63,7 +62,6 @@ addEventListener(
height: message.height,
devicePixelRatio: message.devicePixelRatio,
color: message.color,
- highlightColor: message.highlightColor,
reducedMotion: message.reducedMotion,
});
workerSelf.postMessage({ type: 'ready' });
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneParticle.type.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneParticle.type.ts
index 4255460b84..14dd481fef 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneParticle.type.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneParticle.type.ts
@@ -13,4 +13,5 @@ export type WelcomeHalftoneParticle = {
positionAtLeaveStartX: number;
positionAtLeaveStartY: number;
opacityAtLeaveStart: number;
+ capsuleLengthAtLeaveStart: number;
};
diff --git a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneRenderer.ts b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneRenderer.ts
index 569fa4a82e..4ebc87a3f8 100644
--- a/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneRenderer.ts
+++ b/packages/twenty-front/src/modules/onboarding/components/WelcomeOverlay/welcomeHalftoneRenderer.ts
@@ -4,18 +4,16 @@ import { buildWelcomeHalftoneParticles } from '@/onboarding/components/WelcomeOv
import { createWelcomeHalftoneAnimationFrameLoop } from '@/onboarding/components/WelcomeOverlay/createWelcomeHalftoneAnimationFrameLoop';
import { type WelcomeHalftoneParticle } from '@/onboarding/components/WelcomeOverlay/welcomeHalftoneParticle.type';
-const DASH_ASSEMBLE_DURATION_SECONDS = 0.62;
+const DASH_ASSEMBLE_DURATION_SECONDS = 0.9;
+const DASH_STRETCH_START_ASSEMBLE_PROGRESS = 0.35;
const BURST_DURATION_SECONDS = 0.75;
const SETTLE_DRIFT_DURATION_SECONDS = 0.6;
-const SHIMMER_BAND_HALF_WIDTH = 0.028;
const MINIMUM_VISIBLE_OPACITY = 0.01;
const clampToUnitRange = (value: number) => Math.max(0, Math.min(1, value));
const interpolate = (from: number, to: number, ratio: number) =>
from + (to - from) * ratio;
const easeOutCubic = (ratio: number) => 1 - Math.pow(1 - ratio, 3);
-const easeOutExpo = (ratio: number) =>
- ratio >= 1 ? 1 : 1 - Math.pow(2, -10 * ratio);
const smootherStep = (ratio: number) => ratio * ratio * (3 - 2 * ratio);
type WelcomeHalftoneRendererOptions = {
@@ -25,7 +23,6 @@ type WelcomeHalftoneRendererOptions = {
height: number;
devicePixelRatio: number;
color: string;
- highlightColor: string;
reducedMotion: boolean;
};
@@ -34,7 +31,6 @@ export const createWelcomeHalftoneRenderer = (
) => {
const { context, dashes, reducedMotion } = options;
const baseColor = options.color;
- const highlightColor = options.highlightColor;
let canvasWidth = options.width;
let canvasHeight = options.height;
@@ -67,7 +63,7 @@ export const createWelcomeHalftoneRenderer = (
particle: WelcomeHalftoneParticle,
elapsedSeconds: number,
) => {
- const assembleProgress = easeOutExpo(
+ const assembleProgress = easeOutCubic(
clampToUnitRange(
(elapsedSeconds - particle.assembleDelaySeconds) /
DASH_ASSEMBLE_DURATION_SECONDS,
@@ -83,20 +79,30 @@ export const createWelcomeHalftoneRenderer = (
particle.targetY,
assembleProgress,
);
+ const secondsSinceLanding =
+ elapsedSeconds -
+ (particle.assembleDelaySeconds + DASH_ASSEMBLE_DURATION_SECONDS);
const settleDriftProgress = clampToUnitRange(
- (elapsedSeconds -
- (particle.assembleDelaySeconds + DASH_ASSEMBLE_DURATION_SECONDS)) /
- SETTLE_DRIFT_DURATION_SECONDS,
+ secondsSinceLanding / SETTLE_DRIFT_DURATION_SECONDS,
);
particleX +=
- Math.sin(elapsedSeconds * 1.6 + particle.driftPhase) *
+ Math.sin(elapsedSeconds * 1.1 + particle.driftPhase) *
settleDriftProgress *
- 1.2;
+ 0.5;
particleY +=
- Math.cos(elapsedSeconds * 1.4 + particle.driftPhase) *
+ Math.cos(elapsedSeconds * 0.95 + particle.driftPhase) *
settleDriftProgress *
- 0.8;
- return { particleX, particleY, opacity: assembleProgress };
+ 0.35;
+ const stretchProgress = clampToUnitRange(
+ (assembleProgress - DASH_STRETCH_START_ASSEMBLE_PROGRESS) /
+ (1 - DASH_STRETCH_START_ASSEMBLE_PROGRESS),
+ );
+ return {
+ particleX,
+ particleY,
+ opacity: assembleProgress,
+ capsuleLength: particle.dashLength * stretchProgress,
+ };
};
const drawFrame = (timeMs: number) => {
@@ -112,11 +118,13 @@ export const createWelcomeHalftoneRenderer = (
particle.positionAtLeaveStartX = particle.targetX;
particle.positionAtLeaveStartY = particle.targetY;
particle.opacityAtLeaveStart = 1;
+ particle.capsuleLengthAtLeaveStart = particle.dashLength;
} else {
const assembleState = computeAssembleState(particle, elapsedSeconds);
particle.positionAtLeaveStartX = assembleState.particleX;
particle.positionAtLeaveStartY = assembleState.particleY;
particle.opacityAtLeaveStart = assembleState.opacity;
+ particle.capsuleLengthAtLeaveStart = assembleState.capsuleLength;
}
}
}
@@ -130,18 +138,13 @@ export const createWelcomeHalftoneRenderer = (
context.clearRect(0, 0, canvasWidth, canvasHeight);
- const shimmerSweepPosition = ((elapsedSeconds * 0.5) % 1.3) / 1.3;
- const isShimmerActive = !reducedMotion && !isLeaving;
- const inverseViewportSpan = 1 / (canvasWidth + canvasHeight);
- let currentStrokeColor = '';
+ context.strokeStyle = baseColor;
for (const particle of particles) {
let particleX: number;
let particleY: number;
let particleOpacity: number;
let capsuleLength = particle.dashLength;
- let capsuleDirectionX = 1;
- let capsuleDirectionY = 0;
if (reducedMotion) {
particleX = particle.targetX;
@@ -152,12 +155,12 @@ export const createWelcomeHalftoneRenderer = (
particleX = assembleState.particleX;
particleY = assembleState.particleY;
particleOpacity = assembleState.opacity;
+ capsuleLength = assembleState.capsuleLength;
} else {
- const easedBurstProgress = smootherStep(burstProgress);
const outwardPushDistance =
- easedBurstProgress *
+ smootherStep(burstProgress) *
halftoneSize *
- 1.1 *
+ 0.25 *
(0.6 + 0.8 * (particle.distanceToCenter / maxDistanceToCenter));
particleX =
particle.positionAtLeaveStartX +
@@ -168,34 +171,19 @@ export const createWelcomeHalftoneRenderer = (
particleOpacity =
particle.opacityAtLeaveStart *
(1 - easeOutCubic(clampToUnitRange(burstProgress / 0.85)));
- capsuleLength =
- particle.dashLength + outwardPushDistance * 0.12 * burstProgress;
- capsuleDirectionX = particle.burstDirectionX;
- capsuleDirectionY = particle.burstDirectionY;
+ capsuleLength = particle.capsuleLengthAtLeaveStart;
}
if (particleOpacity <= MINIMUM_VISIBLE_OPACITY) {
continue;
}
- const isParticleInShimmerBand =
- isShimmerActive &&
- Math.abs(
- (particleX + particleY) * inverseViewportSpan - shimmerSweepPosition,
- ) < SHIMMER_BAND_HALF_WIDTH;
- const strokeColor = isParticleInShimmerBand ? highlightColor : baseColor;
- if (strokeColor !== currentStrokeColor) {
- context.strokeStyle = strokeColor;
- currentStrokeColor = strokeColor;
- }
-
context.globalAlpha = particleOpacity;
context.lineWidth = particle.strokeWidth;
context.beginPath();
- const halfCapsuleX = (capsuleDirectionX * capsuleLength) / 2;
- const halfCapsuleY = (capsuleDirectionY * capsuleLength) / 2;
- context.moveTo(particleX - halfCapsuleX, particleY - halfCapsuleY);
- context.lineTo(particleX + halfCapsuleX, particleY + halfCapsuleY);
+ const halfCapsuleLength = capsuleLength / 2;
+ context.moveTo(particleX - halfCapsuleLength, particleY);
+ context.lineTo(particleX + halfCapsuleLength, particleY);
context.stroke();
}