feat(vk): move the tile letter to the corner, add the light cover
Sets the letter in the tile's top-left corner with the value staying bottom right — the in-game tile layout — instead of centring it. The glyph is placed by its ink box rather than the text origin, so every letter lands on the same corner whatever its side bearings and ascent. Adds day-spot: the same crop and camera as night-spot on a light ground, so the chosen composition exists as a light/dark pair.
@@ -425,11 +425,15 @@ function tileTexture(pal, grid, opts) {
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
|
// Letter in the top-left corner, value in the bottom-right — the in-game tile layout.
|
||||||
|
// The glyph is placed by its INK box, not by the text origin, so every letter lines up
|
||||||
|
// on the same corner regardless of its side bearings and ascent.
|
||||||
ctx.fillStyle = pal.tileText;
|
ctx.fillStyle = pal.tileText;
|
||||||
ctx.font = brand ? `${TEX_CELL * 0.74}px EruditBrand` : `700 ${TEX_CELL * 0.66}px DejaVu Sans`;
|
ctx.font = brand ? `${TEX_CELL * 0.74}px EruditBrand` : `700 ${TEX_CELL * 0.66}px DejaVu Sans`;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'left';
|
||||||
ctx.textBaseline = 'alphabetic';
|
ctx.textBaseline = 'alphabetic';
|
||||||
ctx.fillText(letter, x + w * 0.46, y + w * (brand ? 0.79 : 0.775));
|
const m = ctx.measureText(letter);
|
||||||
|
ctx.fillText(letter, x + w * 0.1 + m.actualBoundingBoxLeft, y + w * 0.1 + m.actualBoundingBoxAscent);
|
||||||
|
|
||||||
ctx.font = `600 ${TEX_CELL * 0.23}px DejaVu Sans`;
|
ctx.font = `600 ${TEX_CELL * 0.23}px DejaVu Sans`;
|
||||||
ctx.textAlign = 'right';
|
ctx.textAlign = 'right';
|
||||||
@@ -679,6 +683,22 @@ const VARIANTS = [
|
|||||||
vignette(ctx, W, H, 0.6);
|
vignette(ctx, W, H, 0.6);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'day-spot',
|
||||||
|
title: 'День — тот же кадр, светлая пара к night-spot',
|
||||||
|
palette: WARM,
|
||||||
|
font: 'brand',
|
||||||
|
cam: { elevDeg: 27, dist: 24, widthFrac: 1.4, aimX: 0.8, aimY: WORDS_Y, aimYFrac: 0.56 },
|
||||||
|
background(ctx, W, H) {
|
||||||
|
linear(ctx, W, H, [[0, '#fbf3e7'], [0.6, '#f0e2ce'], [1, '#d8bd98']], 0, 0, 0, H);
|
||||||
|
radialGlow(ctx, W, H, W * 0.5, H * 0.5, W * 0.55, 'rgba(255,250,236,0.85)', 'rgba(255,250,236,0)');
|
||||||
|
},
|
||||||
|
effects(ctx, W, H) {
|
||||||
|
farHaze(ctx, W, H, '251,243,231', 0.5, 0.9);
|
||||||
|
nearFade(ctx, W, H, '150,113,72', 0.84, 0.35);
|
||||||
|
vignette(ctx, W, H, 0.3);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'brand-blue',
|
id: 'brand-blue',
|
||||||
title: 'Бренд-градиент — доска парит, продуктовый вид',
|
title: 'Бренд-градиент — доска парит, продуктовый вид',
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 960 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 373 KiB After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 435 KiB After Width: | Height: | Size: 435 KiB |
|
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 391 KiB |
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 473 KiB After Width: | Height: | Size: 474 KiB |