Skip to content

Commit 04b0680

Browse files
authored
feat: [#95] grid fix
* wip: simplify the grid pixel * feat: simplified the grid pixels
1 parent 04bd4fe commit 04b0680

File tree

4 files changed

+81
-45
lines changed

4 files changed

+81
-45
lines changed

src/components/pixel-grid/index.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,27 @@ export type GRID_CONFIG = {
66
colors: string[];
77
};
88

9-
const EMPTY_RATIO = 0.5; // Fixed constant for empty bias
9+
const EMPTY_RATIO = 0.3; // Controls the "empty zone" bias (0.0 = all empty, 1.0 = no empty zone)
1010

1111
// --------------------------------------------------------------------------------
1212

13-
export function createPixelGrid(config: GRID_CONFIG, alignment: 'left' | 'right'): HTMLCanvasElement {
13+
export function createPixelGrid(config: GRID_CONFIG, alignment: 'left' | 'right' | 'full'): HTMLCanvasElement {
1414
const canvas = createCanvasElement();
1515
const ctx = canvas.getContext('2d');
1616

1717
if (!ctx) return canvas;
1818

19-
// Use ResizeObserver for responsive updates
19+
let resizeTimeout: number;
20+
21+
// Use ResizeObserver with debouncing for responsive updates
2022
const resizeObserver = new ResizeObserver(() => {
21-
const parent = canvas.parentElement;
22-
if (parent) {
23-
renderPixelGrid(canvas, ctx, config, alignment, parent.clientWidth, parent.clientHeight);
24-
}
23+
clearTimeout(resizeTimeout);
24+
resizeTimeout = window.setTimeout(() => {
25+
const parent = canvas.parentElement;
26+
if (parent) {
27+
renderPixelGrid(canvas, ctx, config, alignment, parent.clientWidth, parent.clientHeight);
28+
}
29+
}, 100); // 100ms debounce
2530
});
2631

2732
// Initial render
@@ -49,7 +54,7 @@ function renderPixelGrid(
4954
canvas: HTMLCanvasElement,
5055
ctx: CanvasRenderingContext2D,
5156
config: GRID_CONFIG,
52-
alignment: 'left' | 'right',
57+
alignment: 'left' | 'right' | 'full',
5358
width: number,
5459
height: number
5560
) {
@@ -63,23 +68,27 @@ function renderPixelGrid(
6368

6469
// Calculate number of columns dynamically based on width and pixel size
6570
const cols = Math.ceil(width / pixelSize);
66-
67-
const canvasRect = canvas.getBoundingClientRect();
71+
72+
// Pre-calculate common values
73+
const totalRows = config.rows;
74+
const totalCols = cols;
75+
const colors = config.colors;
6876

6977
// Render each pixel
7078
for (let row = 0; row < config.rows; row++) {
7179
for (let col = 0; col < cols; col++) {
72-
const screenX = canvasRect.left + (col * pixelSize);
80+
// Use canvas-relative position for consistent 50/50 split
81+
const canvasX = col * pixelSize;
7382
const color = createPixelPattern({
7483
row,
7584
col,
76-
totalRows: config.rows,
77-
totalCols: cols,
78-
screenX,
79-
screenWidth: window.innerWidth,
85+
totalRows,
86+
totalCols,
87+
screenX: canvasX,
88+
screenWidth: width, // Use canvas width instead of viewport width
8089
alignment,
8190
emptyRatio: EMPTY_RATIO,
82-
colors: config.colors
91+
colors
8392
});
8493

8594
if (color) {

src/components/pixel-grid/pixel.ts

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type PixelInput = {
66
totalCols: number;
77
screenX: number;
88
screenWidth: number;
9-
alignment: 'left' | 'right';
9+
alignment: 'left' | 'right' | 'full';
1010
emptyRatio: number;
1111
colors: readonly string[];
1212
};
@@ -23,7 +23,9 @@ export function createPixelPattern(input: PixelInput): string | null {
2323
normalizedScreenX,
2424
normalizedY,
2525
input.alignment,
26-
input.emptyRatio
26+
input.emptyRatio,
27+
input.col,
28+
input.totalCols
2729
);
2830

2931
return isEmpty ? null : getRandomColor(input.colors);
@@ -34,35 +36,56 @@ export function createPixelPattern(input: PixelInput): string | null {
3436
function shouldPixelBeEmpty(
3537
_normalizedX: number, // Container position (unused, kept for future use)
3638
normalizedScreenX: number,
37-
normalizedY: number,
38-
alignment: 'left' | 'right',
39-
emptyRatio: number
39+
_normalizedY: number, // Not used in this implementation
40+
alignment: 'left' | 'right' | 'full',
41+
_emptyRatio: number, // Not used in simple split
42+
col: number,
43+
totalCols: number
4044
): boolean {
41-
const boundary = calculateBoundary(normalizedY, emptyRatio);
42-
45+
// For 'full' alignment, stretch the grid to 100% with no empty zones
46+
if (alignment === 'full') {
47+
return false; // Show all pixels across the entire grid
48+
}
49+
50+
// 60/40 split - pixels appear on opposite side of alignment with more coverage
51+
const splitPoint = 0.45; // 40% threshold means 60% coverage on the opposite side
52+
53+
let shouldHideBasedOnAlignment = false;
54+
4355
if (alignment === 'right') {
44-
return normalizedScreenX > boundary ? hasGhostPixel() : hasScatterEffect(normalizedScreenX, boundary, normalizedY);
56+
// For right alignment: show pixels on the LEFT 60% (inverted)
57+
shouldHideBasedOnAlignment = normalizedScreenX > splitPoint;
4558
} else {
46-
return normalizedScreenX < boundary ? hasGhostPixel() : hasScatterEffect(normalizedScreenX, boundary, normalizedY);
59+
// For left alignment: show pixels on the RIGHT 60% (inverted)
60+
shouldHideBasedOnAlignment = normalizedScreenX < splitPoint;
4761
}
48-
}
49-
50-
function calculateBoundary(normalizedY: number, emptyRatio: number): number {
51-
const waveOffset = Math.sin(normalizedY * Math.PI * 3) * 0.1;
52-
const randomOffset = (Math.random() - 0.5) * 0.15;
53-
return emptyRatio + waveOffset + randomOffset;
54-
}
55-
56-
function hasScatterEffect(screenX: number, boundary: number, normalizedY: number): boolean {
57-
const distance = Math.abs(screenX - boundary);
58-
const scatterChance = Math.pow(1 - distance / boundary, 2) * 0.25;
59-
const rowVariation = Math.sin(normalizedY * Math.PI * 2) * 0.1;
6062

61-
return Math.random() < (scatterChance + rowVariation);
62-
}
63-
64-
function hasGhostPixel(): boolean {
65-
return Math.random() >= 0.3;
63+
// If already hidden by alignment, return true
64+
if (shouldHideBasedOnAlignment) {
65+
return true;
66+
}
67+
68+
// Apply random hiding to only the last column of the visible area
69+
const splitCol = Math.floor(totalCols * splitPoint); // Column where the split happens
70+
let shouldApplyRandomHiding = false;
71+
72+
if (alignment === 'right') {
73+
// For right alignment: pixels show on LEFT side (cols 0 to splitCol)
74+
// Target only the last visible column (rightmost edge of visible area)
75+
const isLastVisibleColumn = col === splitCol;
76+
shouldApplyRandomHiding = isLastVisibleColumn;
77+
} else {
78+
// For left alignment: pixels show on RIGHT side (cols splitCol to totalCols)
79+
// Target only the first visible column (leftmost edge of visible area)
80+
const isFirstVisibleColumn = col === splitCol + 1;
81+
shouldApplyRandomHiding = isFirstVisibleColumn;
82+
}
83+
84+
if (shouldApplyRandomHiding) {
85+
return Math.random() < 0.50; // 40% chance for random gaps in the target column
86+
}
87+
88+
return false; // All other visible pixels are shown
6689
}
6790

6891
function getRandomColor(colors: readonly string[]): string {

src/views/about/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export function aboutView() {
2626

2727
const firstSection = createAboutSection(aboutContent.manifesto);
2828
firstSection.classList.add('first-section');
29-
firstSection.appendChild(createPixelGridBackground('left', pixelGridConfigs));
29+
firstSection.appendChild(createPixelGridBackground('full', pixelGridConfigs));
30+
3031

3132
page.appendChild(firstSection);
3233
page.appendChild(createPixelBannerCTA(aboutContent.ctaBannerA));

src/views/utils/backgrounds-utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ export function createVideoBackground(videoWebm: string, videoMp4: string, oneSh
3434
return background;
3535
}
3636

37-
export function createPixelGridBackground(contentAlignment: 'left' | 'right', configs: GRID_CONFIG) {
38-
const alignment = contentAlignment === 'right' ? 'left' : 'right';
37+
export function createPixelGridBackground(contentAlignment: 'left' | 'right' | 'full', configs: GRID_CONFIG) {
38+
// For 'full' alignment, pass it directly. For 'left'/'right', invert the alignment
39+
const alignment = contentAlignment === 'full'
40+
? 'full'
41+
: (contentAlignment === 'right' ? 'left' : 'right');
3942
return createPixelGrid(configs, alignment);
4043
}

0 commit comments

Comments
 (0)