给单文件 2048 加新块出现动画:CSS transition 的一处小坑

举报
yd_212030130 发表于 2026/11/06 09:58:59 2026/11/06
【摘要】 新块凭空出现,是单文件 2048 里最容易被忽略的体验缺口:每次合并后棋盘上突然多一块 2 或 4,玩家视线跟不上,常常要重新扫一遍棋盘才知道新块落在哪。最直觉的做法是给格子加 transition:.tile { transition: transform 120ms ease, opacity 120ms ease; }.tile-new { transform: scale(0.6);...

新块凭空出现,是单文件 2048 里最容易被忽略的体验缺口:每次合并后棋盘上突然多一块 2 或 4,玩家视线跟不上,常常要重新扫一遍棋盘才知道新块落在哪。

最直觉的做法是给格子加 transition:

.tile { transition: transform 120ms ease, opacity 120ms ease; }
.tile-new { transform: scale(0.6); opacity: 0; }

然后在 JS 里先渲染带 .tile-new 的格子,再去掉这个类,期望它从小变大淡入。但第一次写大概率会发现动画根本没播:格子直接以最终大小出现。

坑在这里:transition 只在“样式发生变化”时才触发。如果元素插入 DOM 时就已经是最终样式,浏览器没有“变化”可播。正确做法是分两步:先以初始态插入,再下一帧改到最终态。

function spawnTile(value, row, col) {
  const el = document.createElement('div');
  el.className = 'tile tile-new';
  el.textContent = value;
  positionTile(el, row, col);   // 先摆到位置上,但仍是 scale(0.6)
  board.appendChild(el);
  requestAnimationFrame(() => {
    el.classList.remove('tile-new');  // 下一帧去掉初始态,transition 开始播
  });
}

requestAnimationFrame 是关键:它保证浏览器先把初始态画出来,再处理样式变化,transition 才有起点和终点。

如果不想管帧时序,也可以用 @keyframes,一步到位:

@keyframes pop-in {
  from { transform: scale(0.6); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
.tile-new { animation: pop-in 120ms ease; }

animation 插入即播,不需要等下一帧,写起来更省事。代价是动画播完后要记得去掉 .tile-new 这个类,否则下次复用这个格子元素时动画会重播一次。

还有一个细节:新块动画只给“新生成”的格子,合并产生的格子不要复用同一套。合并格可以单独给一个更短的弹跳(比如 scale 1.15 再回落),玩家一眼就能区分“刚长出来的”和“刚合并的”,棋盘变化一下就读懂了。

总结:transition 需要样式变化才触发,插入时先给初始态、下一帧再改终态;嫌麻烦就直接用 @keyframes。动画时长 120ms 左右就够,太长会拖慢连续操作的节奏。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。