Tổng quan giải pháp
- Font ma trận: Mỗi ký tự là ma trận nhị phân 5×6 (0/1) — 1 là pixel bật, 0 là tắt.
- Engine render: Duyệt chuỗi theo từng hàng (row-major), ghép cột của từng glyph, thêm
spacinggiữa các glyph. - Biến đổi kiểu chữ:
- Block: hiển thị đầy đủ khối 1.
- Outline: chỉ giữ viền (pixel 1 giáp pixel 0).
- Slim: tinh chỉnh để mảnh hơn (lọc một số cụm 1-1-1 thành 1-0-1).
- Giãn tỉ lệ:
hscale/vscalenhân lặp phần tử theo chiều ngang/dọc.
Demo
Thiết kế font ma trận
Ý tưởng: mỗi ký tự là 6 chuỗi, mỗi chuỗi dài 5 ký tự ‘0’/’1′. Ví dụ, ký tự A:
01110
10001
10001
11111
10001
10001
Bảng font gốc (Block) được định nghĩa bằng đối tượng JavaScript. Tránh ký tự phân tách “|” để không bị CMS làm hỏng chuỗi.
const FONT_BLOCK = {
'A': ['01110','10001','10001','11111','10001','10001'],
'B': ['11110','10001','11110','10001','10001','11110'],
// ...A-Z, 0-9, khoảng trắng, dấu -, ., !, ?
};
Engine ghép ASCII: từ ma trận đến văn bản
Chúng ta duyệt theo hàng (6 hàng). Với mỗi glyph, ghép chuỗi hàng tương ứng vào rows[r], sau đó thêm spacing cột 0 để tạo khoảng cách giữa các ký tự. Giãn ngang bằng lặp bit, giãn dọc bằng lặp dòng.
function renderASCII(text, { font='block', char='#', hscale=1, vscale=1, spacing=1 }) {
text = (text ?? '').toUpperCase();
const map = FONTS[font] || FONT_BLOCK;
const glyphRows = 6, glyphCols = 5;
const glyphs = Array.from(text, ch => map[ch] || map['?']);
// Ghép theo hàng
const rows = Array.from({ length: glyphRows }, () => '');
for (const g of glyphs) {
for (let r = 0; r < glyphRows; r++) {
const row = g[r] || '0'.repeat(glyphCols);
let scaled = '';
for (const bit of row) scaled += bit.repeat(hscale); // giãn ngang
rows[r] += scaled + '0'.repeat(spacing); // khoảng trắng giữa glyph
}
}
// Giãn dọc
const out = [];
for (const line of rows) for (let k = 0; k < vscale; k++) out.push(line);
// Map 0/1 → ' '/char
return out.map(line => line.replace(/1/g, char).replace(/0/g, ' ')).join('\n');
}
Tạo biến thể font: Outline và Slim
Outline: giữ pixel biên (1 tiếp giáp 0 ở 4 hướng). Slim: làm mảnh những cụm dày 1-1-1 thành 1-0-1.
function makeOutline(block) {
const out = {};
for (const k in block) {
const g = block[k].map(r => r.split('').map(Number));
const h = g.length, w = g[0].length, edge = [];
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
for (let y=0;y<h;y++){
let line = '';
for (let x=0;x<w;x++){
if (!g[y][x]) { line += '0'; continue; }
let boundary = false;
for (const [dx,dy] of dirs){
const nx=x+dx, ny=y+dy;
if (nx<0||ny<0||nx>=w||ny>=h||g[ny][nx]===0){ boundary = true; break; }
}
line += boundary ? '1' : '0';
}
edge.push(line);
}
out[k] = edge;
}
return out;
}
function makeSlim(block) {
const out = {};
for (const k in block) {
out[k] = block[k].map(row => row.replace(/111/g,'101'));
}
return out;
}
Mở rộng font: thêm ký tự mới
Ví dụ thêm dấu gạch dưới _ và dấu hai chấm : (tuỳ biến theo style của bạn):
FONT_BLOCK['_'] = ['00000','00000','00000','00000','00000','11111'];
FONT_BLOCK[':'] = ['00000','00100','00100','00000','00100','00100'];
Tiền xử lý văn bản đầu vào
Chúng ta giới hạn ASCII art ở A–Z, 0–9, khoảng trắng và một vài ký tự. Với ký tự khác, chuyển thành dấu hỏi ?. Bạn có thể thêm bước “latin hóa” nếu muốn hỗ trợ chữ có dấu, ví dụ “HÀO” → “HAO”.
function normalizeText(input) {
return (input || '')
.normalize('NFD') // tách dấu
.replace(/\p{Diacritic}+/gu,'') // bỏ dấu
.toUpperCase()
.replace(/[^A-Z0-9 \-\.!\?:]/g, '?'); // ràng buộc tập ký tự
}
Mẹo trình bày
- Dùng font monospace khi hiển thị (đã cấu hình sẵn trong demo).
- Giữ
hscalevàvscaleở 1–3 cho độ đọc tốt; >=4 có thể “vỡ” bố cục trên màn hình nhỏ. - Chọn ký tự vẽ có độ phủ đều (ví dụ
#/@) để đường viền rõ ràng;.sẽ mỏng, hợp kiểu Slim.
Mở rộng tính năng (gợi ý nâng cấp)
- Tự thiết kế font: tạo công cụ “glyph editor” nhỏ dùng checkbox 5×6 để vẽ ký tự, xuất JSON vào
FONT_BLOCK. - Kerning thô: với kí tự mảnh như
I, giảmspacingriêng; vớiW, tăng spacing. - Multi-line: tách theo
\n, render từng dòng rồi ghép bằng 2–3 dòng trống ở giữa. - Templates: thêm preset Matrix (char =
1), Dots (char =.), Solid (char =#).
Kết luận
Với một bảng font ma trận nhỏ gọn và engine ghép chuỗi đơn giản, bạn đã có một “Text to ASCII Art Generator” mượt, dễ mở rộng, chạy ngay trong bài viết — hoàn toàn không lệ thuộc thư viện ngoài, lại cách ly CSS tuyệt đối nhờ Shadow DOM.
Bình luận