signature: restore lean dots, reduced-motion redraw, above-fold reveal and map focus rings
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -209,4 +209,73 @@ describe('DotBackground', () => {
|
||||
|
||||
expect(raf).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('starts with a bounded initial dot count on a large desktop viewport', async () => {
|
||||
const context = mockContext();
|
||||
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(context);
|
||||
const widthStub = vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(2560);
|
||||
const heightStub = vi.spyOn(window, 'innerHeight', 'get').mockReturnValue(1440);
|
||||
const scheduled: FrameRequestCallback[] = [];
|
||||
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => {
|
||||
scheduled.push(callback);
|
||||
return scheduled.length;
|
||||
});
|
||||
|
||||
try {
|
||||
const fixture = await createFixture();
|
||||
const pending = scheduled.splice(0);
|
||||
for (const callback of pending) {
|
||||
callback(0);
|
||||
}
|
||||
|
||||
const arcCalls = vi.mocked(context.arc).mock.calls.length;
|
||||
expect(arcCalls).toBeGreaterThanOrEqual(6);
|
||||
expect(arcCalls).toBeLessThanOrEqual(24);
|
||||
fixture.destroy();
|
||||
} finally {
|
||||
widthStub.mockRestore();
|
||||
heightStub.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('redraws the static frame after resize under reduced motion without starting a loop', async () => {
|
||||
mockMatchMedia((query) => query.includes('prefers-reduced-motion'));
|
||||
const context = mockContext();
|
||||
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(context);
|
||||
const scheduled: FrameRequestCallback[] = [];
|
||||
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => {
|
||||
scheduled.push(callback);
|
||||
return scheduled.length;
|
||||
});
|
||||
|
||||
TestBed.resetTestingModule();
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DotBackground],
|
||||
}).compileComponents();
|
||||
|
||||
const fixture = TestBed.createComponent(DotBackground);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
TestBed.inject(ApplicationRef).tick();
|
||||
|
||||
const pending = scheduled.splice(0);
|
||||
for (const callback of pending) {
|
||||
callback(0);
|
||||
}
|
||||
|
||||
vi.mocked(context.clearRect).mockClear();
|
||||
vi.mocked(context.arc).mockClear();
|
||||
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
|
||||
const resizeFrames = scheduled.splice(0);
|
||||
for (const callback of resizeFrames) {
|
||||
callback(0);
|
||||
}
|
||||
|
||||
expect(context.clearRect).toHaveBeenCalled();
|
||||
expect(context.arc).toHaveBeenCalled();
|
||||
expect(scheduled).toEqual([]);
|
||||
fixture.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,10 @@ export class DotBackground {
|
||||
private tornDown = false;
|
||||
private readonly teardowns: Array<() => void> = [];
|
||||
|
||||
private readonly MIN_DOT_COUNT = 8;
|
||||
private readonly INITIAL_DOT_COUNT_MIN = 6;
|
||||
private readonly INITIAL_DOT_COUNT_MAX = 24;
|
||||
private readonly INITIAL_DOT_COUNT_MAX_COARSE = 12;
|
||||
private readonly INITIAL_DOT_AREA_DIVISOR = 130_000;
|
||||
private readonly MAX_DOT_COUNT = 100;
|
||||
private readonly MAX_DOT_COUNT_MOBILE = 40;
|
||||
private readonly COLORS = ['#6366f1', '#8b5cf6', '#a855f7', '#3b82f6'];
|
||||
@@ -213,6 +216,10 @@ export class DotBackground {
|
||||
dot.x = Math.max(dot.radius, Math.min(width - dot.radius, dot.x));
|
||||
dot.y = Math.max(dot.radius, Math.min(height - dot.radius, dot.y));
|
||||
}
|
||||
|
||||
if (this.reducedMotion && this.ctx) {
|
||||
this.drawFrame();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -239,10 +246,12 @@ export class DotBackground {
|
||||
};
|
||||
|
||||
private targetDotCount(width: number, height: number): number {
|
||||
const maxCount = this.coarsePointer ? this.MAX_DOT_COUNT_MOBILE : this.MAX_DOT_COUNT;
|
||||
const maxInitial = this.coarsePointer
|
||||
? this.INITIAL_DOT_COUNT_MAX_COARSE
|
||||
: this.INITIAL_DOT_COUNT_MAX;
|
||||
const area = Math.max(0, width) * Math.max(0, height);
|
||||
const fromArea = Math.round(area / 20_000);
|
||||
return Math.min(maxCount, Math.max(this.MIN_DOT_COUNT, fromArea));
|
||||
const fromArea = Math.round(area / this.INITIAL_DOT_AREA_DIVISOR);
|
||||
return Math.min(maxInitial, Math.max(this.INITIAL_DOT_COUNT_MIN, fromArea));
|
||||
}
|
||||
|
||||
private spawnDot(): Dot {
|
||||
|
||||
Reference in New Issue
Block a user