像素小人 · 阿岁
by HyperComet30360 lines13.5 KB
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>像素小人 · 阿岁</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
background: linear-gradient(145deg, #3b4a5c 0%, #1f2a36 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', 'Courier New', monospace;
}
.game-wrapper {
background: #2d3a47;
padding: 2rem 2.5rem 2.5rem 2.5rem;
border-radius: 4rem 4rem 3rem 3rem;
box-shadow: 0 20px 30px rgba(0,0,0,0.6), inset 0 1px 4px rgba(255,255,255,0.08);
border-bottom: 6px solid #1b232c;
}
canvas {
display: block;
margin: 0 auto;
width: 320px;
height: 320px;
image-rendering: crisp-edges; /* 像素风格 */
image-rendering: pixelated;
background: #b4d0e0;
border-radius: 32px;
box-shadow: 0 0 0 2px #171f27, 0 12px 28px rgba(0,0,0,0.7);
transition: filter 0.1s;
cursor: pointer;
}
canvas:active {
filter: brightness(0.96) drop-shadow(0 0 4px #b8d6ff);
}
.speech-bubble {
margin-top: 24px;
background: #fcf3e6;
padding: 14px 18px;
border-radius: 60px 60px 60px 8px;
font-size: 1.4rem;
font-weight: 600;
color: #1f2a30;
text-align: center;
letter-spacing: 0.02em;
box-shadow: 0 6px 0 #baa99b, inset 0 2px 8px rgba(255, 245, 225, 0.7);
border: 2px solid #ece1d2;
transition: 0.15s ease;
min-height: 5rem;
display: flex;
align-items: center;
justify-content: center;
word-break: break-word;
font-family: 'Courier New', 'Segoe UI', monospace;
}
.speech-bubble span {
display: inline-block;
background: #ffe7d0;
padding: 0 12px;
border-radius: 40px;
line-height: 1.4;
}
.hint {
margin-top: 16px;
color: #b9cbdc;
font-size: 0.9rem;
text-align: center;
letter-spacing: 1px;
opacity: 0.8;
font-weight: 300;
text-shadow: 0 2px 3px #0f151c;
}
.hint i {
font-style: normal;
background: #1e2b38;
padding: 2px 14px;
border-radius: 30px;
margin: 0 4px;
}
footer {
margin-top: 10px;
color: #657e92;
font-size: 0.7rem;
text-align: center;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div class="game-wrapper">
<canvas id="pixelCanvas" width="320" height="320"></canvas>
<!-- 说话气泡 -->
<div class="speech-bubble" id="speechBubble">
<span id="dialogueText">✨ 点我呀 ✨</span>
</div>
<div class="hint">
<i>👆 点击小人 · 阿岁会说话</i>
</div>
<footer>✦ 像素阿岁 · M字刘海 ✦</footer>
</div>
<script>
(function() {
const canvas = document.getElementById('pixelCanvas');
const ctx = canvas.getContext('2d');
const dialogueSpan = document.getElementById('dialogueText');
// 像素尺寸:每个像素块 8x8 ,画布 320x320 => 40x40 网格
const PIXEL_SIZE = 8;
const GRID = 40;
// 颜色定义 (方便调整)
const COLORS = {
skin: '#f7d9b0',
skinShadow: '#eac29a',
hair: '#1f1f2b', // 黑色短发
hairLight: '#333345',
eyeWhite: '#f2f4f0',
eyePupil: '#1c1c24',
eyeHighlight: '#fafafa',
shirt: '#eef2f5', // 白衬衫
shirtShadow: '#d3d9df',
collar: '#c8d0d8',
pants: '#1e232b', // 黑色裤子
pantsLight: '#2f3640',
belt: '#36404a',
mouth: '#c27a7a',
blush: '#f5c0b0',
// 背景辅助
bg: '#b4d0e0',
};
// ---------- 绘制像素小人 (阿岁) ----------
function drawPixelCharacter() {
// 清空画布 (背景)
ctx.fillStyle = COLORS.bg;
ctx.fillRect(0, 0, 320, 320);
// ---------- 辅助函数: 填充像素块 (x,y 网格坐标) ----------
function fillPixel(col, row, color) {
const x = col * PIXEL_SIZE;
const y = row * PIXEL_SIZE;
ctx.fillStyle = color;
ctx.fillRect(x, y, PIXEL_SIZE, PIXEL_SIZE);
}
// ---------- 绘制身体 (裤子+衬衫) ----------
// 裤子 (黑色) 从 row 24~31, col 15~25
for (let r = 24; r <= 31; r++) {
for (let c = 15; c <= 25; c++) {
// 裤子稍微有深浅变化
let color = COLORS.pants;
if ((r === 24 || r === 31) && (c >= 17 && c <= 23)) color = COLORS.pantsLight;
if (r === 25 && (c === 16 || c === 24)) color = COLORS.pantsLight;
if (r === 30 && c === 18) color = '#3d4754';
if (r === 30 && c === 22) color = '#3d4754';
fillPixel(c, r, color);
}
}
// 腰带 (深色 row 23)
for (let c = 15; c <= 25; c++) {
if (c >= 16 && c <= 24) fillPixel(c, 23, COLORS.belt);
}
// 白衬衫 (row 18~23, col 14~26)
for (let r = 18; r <= 23; r++) {
for (let c = 14; c <= 26; c++) {
// 留出脖子区域 (row 18~19 中间偏上让给皮肤)
if (r <= 19 && c >= 18 && c <= 22) continue;
// 衬衫阴影 (腋下/褶皱)
let color = COLORS.shirt;
if (r === 23 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
if (r === 22 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
if (r === 20 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
if (r === 21 && c === 15) color = COLORS.shirtShadow;
if (r === 21 && c === 25) color = COLORS.shirtShadow;
// 衣领 (白色 更亮)
if (r === 19 && (c === 17 || c === 23)) color = '#f8fafc';
if (r === 20 && (c === 17 || c === 23)) color = COLORS.collar;
if (r === 20 && c === 18) color = '#dde3ea';
if (r === 20 && c === 22) color = '#dde3ea';
fillPixel(c, r, color);
}
}
// 脖子 (皮肤) row 18~19, col 19~21
for (let r = 18; r <= 19; r++) {
for (let c = 19; c <= 21; c++) {
fillPixel(c, r, COLORS.skin);
}
}
// 脖子阴影
fillPixel(18, 19, COLORS.skinShadow);
fillPixel(22, 19, COLORS.skinShadow);
// ---------- 头部 (皮肤 + 头发) ----------
// 脸部皮肤: row 8~18, col 14~26 (但头发会覆盖部分)
for (let r = 8; r <= 18; r++) {
for (let c = 14; c <= 26; c++) {
// 耳朵区域 (在两边)
if ((c === 13 || c === 27) && r >= 12 && r <= 16) {
fillPixel(c, r, COLORS.skin);
continue;
}
// 主要脸部区域 (留出发型位置)
if (c >= 14 && c <= 26) {
// 下巴小修饰
if (r === 18 && (c === 14 || c === 26)) continue; // 留给衬衫
fillPixel(c, r, COLORS.skin);
}
}
}
// 脸颊微红 (row 15~16, col 14~15 和 25~26)
fillPixel(14, 15, COLORS.blush);
fillPixel(15, 15, COLORS.blush);
fillPixel(14, 16, COLORS.blush);
fillPixel(25, 15, COLORS.blush);
fillPixel(26, 15, COLORS.blush);
fillPixel(25, 16, COLORS.blush);
// 眼睛 (白色) 左眼: col 17~18, row 12~13 ; 右眼: col 22~23, row 12~13
for (let r = 12; r <= 13; r++) {
for (let c = 17; c <= 18; c++) {
fillPixel(c, r, COLORS.eyeWhite);
}
for (let c = 22; c <= 23; c++) {
fillPixel(c, r, COLORS.eyeWhite);
}
}
// 瞳孔 (黑色) 左: col 18, row 12 ; 右: col 22, row 12
fillPixel(18, 12, COLORS.eyePupil);
fillPixel(22, 12, COLORS.eyePupil);
// 瞳孔高光
fillPixel(19, 11, COLORS.eyeHighlight);
fillPixel(23, 11, COLORS.eyeHighlight);
// 嘴巴 (微笑小点) row 16, col 20
fillPixel(20, 16, COLORS.mouth);
fillPixel(21, 16, COLORS.mouth);
fillPixel(20, 17, COLORS.mouth);
fillPixel(21, 17, COLORS.mouth);
// ---------- 黑色短发 + M字刘海 ----------
// 头顶头发 (row 5~9)
for (let r = 5; r <= 9; r++) {
for (let c = 13; c <= 27; c++) {
if (r === 5 && (c >= 18 && c <= 22)) continue; // M字刘海凹陷
if (r === 6 && (c >= 19 && c <= 21)) continue;
if (r === 7 && (c === 19 || c === 21)) continue;
// 发色
let color = COLORS.hair;
if (r === 5 || r === 6) color = COLORS.hairLight;
fillPixel(c, r, color);
}
}
// 鬓角 / 侧发 (row 10~12)
for (let r = 10; r <= 12; r++) {
fillPixel(13, r, COLORS.hair);
fillPixel(27, r, COLORS.hair);
}
// M字刘海 突出部分 (中间凹陷两侧凸起)
// 左侧刘海凸起
fillPixel(16, 7, COLORS.hair);
fillPixel(17, 6, COLORS.hair);
fillPixel(17, 7, COLORS.hair);
fillPixel(18, 6, COLORS.hair);
// 右侧刘海凸起
fillPixel(23, 6, COLORS.hair);
fillPixel(24, 6, COLORS.hair);
fillPixel(23, 7, COLORS.hair);
fillPixel(24, 7, COLORS.hair);
// 中间刘海 (M 字凹陷已处理, 但补一些碎发)
fillPixel(19, 7, COLORS.hair);
fillPixel(21, 7, COLORS.hair);
fillPixel(20, 6, COLORS.hairLight);
fillPixel(20, 7, COLORS.hair);
// 发顶边缘
fillPixel(14, 6, COLORS.hair);
fillPixel(26, 6, COLORS.hair);
fillPixel(15, 5, COLORS.hairLight);
fillPixel(25, 5, COLORS.hairLight);
// 头顶加一点头发层次
for (let c = 14; c <= 26; c++) {
if (c === 18 || c === 19 || c === 20 || c === 21 || c === 22) continue;
fillPixel(c, 5, COLORS.hairLight);
}
// 后脑勺 (补齐)
for (let r = 8; r <= 9; r++) {
for (let c = 13; c <= 27; c++) {
if (c >= 16 && c <= 24) continue; // 面部区域
fillPixel(c, r, COLORS.hair);
}
}
// 小耳朵 (皮肤 加上一点阴影)
fillPixel(13, 13, COLORS.skin);
fillPixel(13, 14, COLORS.skinShadow);
fillPixel(27, 13, COLORS.skin);
fillPixel(27, 14, COLORS.skinShadow);
// 补一些衬衫细节 (衣领)
fillPixel(18, 19, '#e8edf2');
fillPixel(22, 19, '#e8edf2');
}
// ---------- 说话逻辑 ----------
function speak() {
dialogueSpan.textContent = '❤️ 好爱你呀阿岁。 ❤️';
// 轻微动画反馈 (气泡闪一下)
const bubble = document.getElementById('speechBubble');
bubble.style.transform = 'scale(0.97)';
setTimeout(() => {
bubble.style.transform = 'scale(1)';
}, 100);
}
// 重置默认话语 (可点击后持续显示,但保留初始)
function resetDialogue() {
// 仅当内容为初始占位时才重置,但为了友好,我们保留最后一次说话内容
// 如果希望点击后永远显示“好爱你呀阿岁。”,则无需重置。
// 但第一次加载显示占位,点击后覆盖。
}
// ---------- 绑定事件 ----------
canvas.addEventListener('click', (e) => {
speak();
});
// 触摸设备支持
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
speak();
}, { passive: false });
// 绘制角色
drawPixelCharacter();
// 额外: 双击重置为默认提示 (演示)
canvas.addEventListener('dblclick', () => {
dialogueSpan.textContent = '✨ 点我呀 ✨';
});
// 初始化占位
dialogueSpan.textContent = '✨ 点我呀 ✨';
console.log('✨ 像素阿岁已加载 — 点击小人就会说“好爱你呀阿岁。”');
})();
</script>
</body>
</html>Game Source: 像素小人 · 阿岁
Creator: HyperComet30
Libraries: none
Complexity: complex (360 lines, 13.5 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-hypercomet30" to link back to the original. Then publish at arcadelab.ai/publish.