一场冬至庆祝活动如果只是展示静态海报,参与感往往比较弱。加入一个“生成我的冬至标记”的小工具,让每位访客输入自己的名字、挑选一句祝福语,再选一种颜色,页面就会实时绘制出一枚带雪花、饺子或汤圆元素的个性化标记,活动的仪式感立刻就会不一样。实现这个功能的核心是HTML5 Canvas,配合少量DOM操作和localStorage数据持久化,不需要引入任何前端框架或图形库。接下来会把完整的思路拆成三个部分:页面结构与视觉主题、Canvas绘制个性化标记、以及本地收藏与数据管理。

先搭建冬至主题的输入面板与画布区域
活动页面通常需要一个简洁的表单区域和一个实时预览区域。表单负责收集访客的昵称、祝福语和颜色选择,画布则负责把数据可视化成一个方形或圆形的徽章。HTML结构可以保持轻量,用两个div分别承载控制面板和画布容器。为了让风格更贴近冬至,可以给页面设置深蓝底色,画布卡片用暖黄色或米白色,形成冬至夜晚与灯火的对比感。
在JavaScript中,需要先获取画布元素并通过getContext方法拿到2D绘图上下文。画布尺寸建议设置得比显示尺寸稍大一些,例如显示300像素、实际绘制600像素,这样导出的图片会更清晰。这里要注意,不能只用CSS设置宽高,必须同步设置canvas的width和height属性,否则绘制内容会被拉伸变形。控制面板中的select和input元素要绑定事件监听,任何一项变化都触发重绘函数,保证预览始终与用户选择保持一致。
<div class="panel">
<label>你的昵称</label>
<input type="text" id="nickname" value="冬至小暖">
<label>祝福语</label>
<select id="blessing">
<option>冬至福至</option>
<option>团团圆圆</option>
<option>温暖过冬</option>
</select>
<label>标记配色</label>
<input type="color" id="themeColor" value="#d4a373">
<button id="saveBtn">收藏这个标记</button>
</div>
<div class="preview">
<canvas id="badgeCanvas" width="600" height="600"></canvas>
</div>
颜色选择器使用input的color类型,可以快速给用户提供颜色选项,就不必自己维护复杂调色板。祝福语用select下拉框控制,选项固定为四条左右,既能降低用户输入成本,又能保证绘制出来的文字长度在可控范围内。昵称输入框则保留开放输入,但需要在绘制时处理过长文本,避免文字溢出标记边界。
用Canvas绘制圆形徽章的核心图形与文字
绘制个性化标记的关键在于把圆形底纹、冬至元素和文字内容合理排布在600乘600的画布上。可以先从最底层开始画:用arc方法绘制一个圆形,半径约260像素,填充用户选择的主题色。然后在圆内部再画一个稍小的浅色圆或描边圆,形成层次感。为了让标记看起来更像一枚徽章,可以在外圈画一圈虚线,虚线颜色取主题色的深色变体,这样即使颜色选择不同,整体风格也能保持统一。
冬至元素可以画成简单的雪花。雪花不需要逼真,用六条从中心向外发散的线,每条线末端再画两个短分叉,就能形成清晰的雪花符号。绘制雪花时可以使用translate把坐标原点移动到画布中心,然后循环六次,每次旋转60度,在旋转后的坐标系里画一条从半径40到半径140的直线,再在末端画两个倾斜30度的短枝。这样代码量不大,但视觉效果足够表达冬至主题。如果希望加入汤圆或饺子元素,也可以用arc加半圆弧线来简化表达,但雪花是最容易识别且跨地域接受度较高的元素。
function drawSnowflake(ctx, centerX, centerY, size, color) {
ctx.save();
ctx.translate(centerX, centerY);
ctx.strokeStyle = color;
ctx.lineWidth = 6;
ctx.lineCap = "round";
for (let i = 0; i < 6; i++) {
ctx.rotate(Math.PI / 3);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -size);
ctx.stroke();
// 左侧短枝
ctx.beginPath();
ctx.moveTo(0, -size * 0.65);
ctx.lineTo(-size * 0.22, -size * 0.85);
ctx.stroke();
// 右侧短枝
ctx.beginPath();
ctx.moveTo(0, -size * 0.65);
ctx.lineTo(size * 0.22, -size * 0.85);
ctx.stroke();
}
ctx.restore();
}
绘制文字时需要对昵称长度做基本判断。如果昵称超过八个中文字符,可以适当缩小字号,或者只取前八个字加省略号。文字要同时做水平居中和垂直居中,水平居中通过设置textAlign为center实现,垂直居中则要结合textBaseline的middle值与实际绘制位置的y坐标。祝福语通常放在昵称下方,字号比昵称小一些,颜色可以稍微降低不透明度,形成主次关系。如果祝福语是四字短语,几乎不需要换行处理;如果允许用户自定义长文本,就需要写一个简单的按字符数分段函数。
实现本地收藏功能并处理多个标记数据
用户点击“收藏这个标记”后,需要把当前昵称、祝福语、配色以及标记生成时间保存到浏览器的localStorage中。localStorage只能存储字符串,因此要用JSON.stringify把对象序列化,再以数组形式追加到已有的收藏列表里。每次保存前先读取现有数据,如果没有数据就初始化为空数组,保存后再写回localStorage。这样即使页面刷新或关闭浏览器再打开,之前的收藏记录依然存在。
展示收藏列表时,可以创建一个独立的展示区域,把所有已保存的标记以小尺寸画布或图片形式呈现出来。为了减少Canvas重复绘制的性能开销,可以在保存时直接调用canvas.toDataURL方法生成一张PNG图片的base64字符串,把图片数据一并存入localStorage。这样收藏列表只需要渲染img标签即可,不需要为每个标记重新执行复杂绘制逻辑。不过需要注意,如果保存的标记数量很多,base64字符串会占满localStorage的5MB配额,因此建议限制最多保存12个标记,超出时给出提示或删除最早一条。
function saveBadge() {
const badge = {
id: Date.now(),
nickname: document.getElementById("nickname").value,
blessing: document.getElementById("blessing").value,
color: document.getElementById("themeColor").value,
image: canvas.toDataURL("image/png"),
savedAt: new Date().toLocaleString()
};
let savedList = [];
try {
savedList = JSON.parse(localStorage.getItem("dongzhiBadges")) || [];
} catch (e) {
savedList = [];
}
if (savedList.length >= 12) {
savedList.shift();
}
savedList.push(badge);
localStorage.setItem("dongzhiBadges", JSON.stringify(savedList));
renderSavedList();
}
删除收藏功能也不可少。可以在每个收藏卡片上放一个删除按钮,点击时从数组中过滤掉对应id的数据,再写回localStorage并重新渲染列表。由于收藏数据保存在用户本地浏览器中,不同设备之间不会自动同步,这一点在活动说明里可以简单告知。如果要在班级群或社区中让每个人都能看到别人的标记,就需要后端接口和数据库支持,但那就超出纯前端实现的范围了。
整个方案的核心思路并不复杂:用Canvas把文字、颜色和冬至图形组合成一张可导出的标记图,再用localStorage做轻量级的数据持久化。这个思路迁移性很强,把雪花画成月饼、灯笼或粽子,把冬至文案换成中秋、春节相关短语,就能快速做出其他节日的互动卡片生成器。活动页面的技术难度不高,但带来的参与感和个性化体验提升非常明显。
冬至庆祝活动个性化标记HTML5 Canvas修改时间:2026-08-29 21:18:59