Files
jiazi-gala-poster-demo/assets/js/scene.js
T

161 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 落日文字画 · 28 × 44 网格
构图全部由下方数组描述;空白格自动用「天」铺满。
修改元素只需调整数组中的坐标与尺寸,行列范围:x 0–27,y 0–43。 */
(() => {
const COLS = 28;
const ROWS = 44;
/* 云:x/y 为最宽一行的左上角,三行 3/5/3;上下拉开分布 */
const clouds = [
{ x: 10, y: 2, tone: 'pink' },
{ x: 18, y: 6, tone: 'violet' },
{ x: 1, y: 10, tone: 'orange' },
{ x: 23, y: 13, tone: 'lilac' },
{ x: 4, y: 16, tone: 'rose' },
];
/* 落日:整体下移贴近楼群,形成日落;
色调按数组顺序,顶部偏红 → 底部偏黄 */
const sun = [
{ y: 18, x: 13, w: 2 },
{ y: 19, x: 10, w: 8 },
{ y: 20, x: 9, w: 10 },
{ y: 21, x: 8, w: 12 },
{ y: 22, x: 7, w: 14 },
{ y: 23, x: 7, w: 14 },
{ y: 24, x: 6, w: 16 },
{ y: 25, x: 6, w: 16 },
{ y: 26, x: 6, w: 16 },
{ y: 27, x: 6, w: 16 },
{ y: 28, x: 6, w: 16 },
{ y: 29, x: 7, w: 14 },
{ y: 30, x: 7, w: 14 },
{ y: 31, x: 8, w: 12 },
{ y: 32, x: 9, w: 10 },
{ y: 33, x: 10, w: 8 },
{ y: 34, x: 13, w: 2 },
];
/* 飞鸟:数量减少,间距不等,仅少量成对;上下拉开分布 */
const birds = [
{ x: 0, y: 2, kind: 'gull' },
{ x: 26, y: 2, kind: 'gull' },
{ x: 1, y: 4, kind: 'sparrow' },
{ x: 3, y: 8, kind: 'crow' },
{ x: 6, y: 13, kind: 'gull' },
{ x: 9, y: 15, kind: 'sparrow' },
{ x: 26, y: 5, kind: 'crow' },
{ x: 17, y: 9, kind: 'sparrow' },
{ x: 21, y: 12, kind: 'gull' },
{ x: 19, y: 15, kind: 'crow' },
];
/* 天线:单列「线」,从 y 向下叠 h 格;楼顶在第 31 行,露出部分为 3 / 4 格 */
const antennas = [
{ x: 7, y: 28, h: 3 },
{ x: 22, y: 27, h: 4 },
];
/* 楼群:n 对应 .bld-n,最终按 x/y/w/h 铺满「楼」(高度裁到第 44 行) */
const buildings = [
{ n: 1, x: 0, y: 35, w: 3, h: 9 },
{ n: 2, x: 3, y: 39, w: 3, h: 5 },
{ n: 3, x: 6, y: 31, w: 4, h: 13 },
{ n: 4, x: 10, y: 36, w: 3, h: 8 },
{ n: 5, x: 13, y: 30, w: 4, h: 14 },
{ n: 6, x: 17, y: 34, w: 3, h: 10 },
{ n: 7, x: 20, y: 31, w: 4, h: 13 },
{ n: 8, x: 24, y: 37, w: 4, h: 7 },
];
/* 标题区:整格扣出的居中矩形,左右各留 3 格 */
const TITLE = {
text: '关于「甲子」的虚构故事',
top: 14,
height: 5,
side: 3,
};
const BIRD_WORDS = { sparrow: '麻雀', gull: '海鸥', crow: '乌鸦' };
const grid = Array.from({ length: ROWS }, () =>
Array.from({ length: COLS }, () => ({ ch: '天', cls: 'sky' }))
);
const put = (x, y, ch, cls) => {
if (x < 0 || x >= COLS || y < 0 || y >= ROWS) {
throw new Error(`元素超出网格范围:(${x}, ${y})`);
}
grid[y][x] = { ch, cls };
};
const putRow = (y, x, n, ch, cls) => {
for (let i = 0; i < n; i++) put(x + i, y, ch, cls);
};
for (const { x, y, tone } of clouds) {
const cls = `cloud cloud-${tone}`;
putRow(y, x + 1, 3, '云', cls);
putRow(y + 1, x, 5, '云', cls);
putRow(y + 2, x + 1, 3, '云', cls);
}
sun.forEach(({ y, x, w }, i) => {
const tone = `sun-t${i + 1}`;
for (let dx = 0; dx < w; dx++) put(x + dx, y, '日', `sun ${tone}`);
});
for (const { x, y, kind } of birds) {
const [a, b] = BIRD_WORDS[kind];
put(x, y, a, `bird-${kind}`);
put(x + 1, y, b, `bird-${kind}`);
}
for (const { x, y, h } of antennas) {
for (let i = 0; i < h; i++) put(x, y + i, '线', 'ant');
}
for (const { n, x, y, w, h } of buildings) {
for (let j = 0; j < h; j++) putRow(y + j, x, w, '楼', `bld bld-${n}`);
}
for (let y = TITLE.top; y < TITLE.top + TITLE.height; y++) {
for (let x = TITLE.side; x < COLS - TITLE.side; x++) {
grid[y][x] = { ch: ' ', cls: 'cut' };
}
}
/* 把同 class 的连续字符合并成一个 span,减少 DOM 节点 */
const html = grid
.map((row) => {
let line = '';
for (let i = 0; i < COLS; ) {
let j = i + 1;
while (j < COLS && row[j].cls === row[i].cls) j++;
const text = row.slice(i, j).map((cell) => cell.ch).join('');
line += `<span class="${row[i].cls}">${text}</span>`;
i = j;
}
return `<div class="line">${line}</div>`;
})
.join('');
const scene = document.querySelector('.scene');
scene.innerHTML = html;
const panel = document.createElement('div');
panel.className = 'title-panel';
panel.style.top = `${TITLE.top}em`;
panel.style.left = `${TITLE.side}em`;
panel.style.width = `${COLS - TITLE.side * 2}em`;
panel.style.height = `${TITLE.height}em`;
const title = document.createElement('h1');
title.className = 'title';
title.textContent = TITLE.text;
panel.append(title);
scene.append(panel);
})();