You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让CSS动画在重新生成网格容器时仅执行一次?

解决笔记容器动画重复执行的问题

我来帮你搞定这个每次提交表单就所有容器重新播放动画的问题~核心原因是你的draw_flex()函数每次都会重新生成所有HTML元素并替换整个容器内容,相当于所有笔记容器都被重新创建了一遍,CSS动画自然会再次触发。下面给你几种实用的解决方案,按推荐程度排序:

方法1:仅渲染新添加的笔记(最优方案)

与其每次清空重绘所有元素,不如只追加新的笔记,这样只有新容器会触发动画,旧容器不受影响。我们需要维护一个变量记录最后一次渲染到哪个索引,每次只处理新增的笔记:

修改JavaScript代码:

// 初始化最后渲染的索引,从0开始
let lastRenderedIndex = 0;

function draw_flex() {
  // 只处理从lastRenderedIndex到notes末尾的新笔记
  for(let i = lastRenderedIndex; i < notes.length; i++) {
    // 创建外层列容器
    const noteCol = document.createElement('div');
    noteCol.className = 'col-12 col-sm-12 col-md-4 col-lg-4 col-xl-2';
    
    // 创建笔记容器
    const container = document.createElement('div');
    container.className = 'container';
    
    // 创建任务文本元素
    const taskEl = document.createElement('div');
    taskEl.className = 'text';
    taskEl.textContent = notes[i].task;
    
    // 创建日期元素
    const dateEl = document.createElement('div');
    dateEl.className = 'date';
    dateEl.textContent = notes[i].date;
    
    // 创建时间元素
    const timeEl = document.createElement('div');
    timeEl.className = 'time';
    timeEl.textContent = notes[i].time;
    
    // 创建删除按钮
    const deleteBtn = document.createElement('div');
    deleteBtn.className = 'button';
    deleteBtn.addEventListener('click', () => delete_note_by_key(i));
    
    const icon = document.createElement('i');
    icon.className = 'fa fa-times';
    deleteBtn.appendChild(icon);
    
    // 组装元素
    container.appendChild(taskEl);
    container.appendChild(dateEl);
    container.appendChild(timeEl);
    container.appendChild(deleteBtn);
    noteCol.appendChild(container);
    
    // 追加到页面容器中
    document.getElementById('my-flex').appendChild(noteCol);
  }
  
  // 更新最后渲染的索引,下次从这里开始
  lastRenderedIndex = notes.length;
}

调整CSS(可选,但更稳妥):

给动画加上animation-fill-mode,让元素保持动画结束后的状态,同时明确动画只执行一次:

div.container {
  background: url('notebg.png') no-repeat;
  height: 250px;
  animation-duration: 4s;
  animation-name: fadeIn;
  animation-fill-mode: forwards; /* 保持动画结束后的样式 */
  animation-iteration-count: 1; /* 明确只执行一次(默认就是1,写出来更清晰) */
}

这种方法不仅解决了动画重复的问题,还提升了页面性能——不用每次销毁重建所有DOM元素。

方法2:给已动画过的容器标记状态

如果因为某些原因必须重新渲染所有元素,可以给每个笔记对象添加一个状态标记,只给未播放过动画的容器添加动画类:

修改CSS:

把动画抽成单独的类,默认容器不包含动画:

div.container {
  background: url('notebg.png') no-repeat;
  height: 250px;
}

.container.animate {
  animation-duration: 4s;
  animation-name: fadeIn;
  animation-fill-mode: forwards;
}

修改JavaScript代码:

function draw_flex() {
  let html_string = '';
  for(let i = 0; i < notes.length; i++) {
    // 检查当前笔记是否已经播放过动画,没有的话加animate类
    const animateClass = notes[i].hasAnimated ? '' : 'animate';
    
    html_string += `
      <div class="col-12 col-sm-12 col-md-4 col-lg-4 col-xl-2">
        <div class="container ${animateClass}">
          <div class="text">${notes[i].task}</div>
          <div class="date">${notes[i].date}</div>
          <div class="time">${notes[i].time}</div>
          <div class="button" onClick="delete_note_by_key(${i})">
            <i class="fa fa-times"></i>
          </div>
        </div>
      </div>
    `;
    
    // 标记该笔记已播放过动画
    notes[i].hasAnimated = true;
  }
  
  // 注意:原代码最后多了一个</div>,这里要去掉,因为外层容器已经存在
  document.getElementById('my-flex').innerHTML = html_string;
}

这种方法需要维护每个笔记的状态,适合必须全量渲染的场景,但性能不如方法1。

方法3:动画结束后移除动画类

通过监听animationend事件,在动画播放完毕后移除动画相关的类,这样即使元素被重新创建(不推荐),也不会再次触发动画:

修改CSS:

div.container {
  background: url('notebg.png') no-repeat;
  height: 250px;
}

.fade-in {
  animation-duration: 4s;
  animation-name: fadeIn;
}

修改JavaScript代码:

function draw_flex() {
  // 清空容器
  const flexContainer = document.getElementById('my-flex');
  flexContainer.innerHTML = '';
  
  for(let i = 0; i < notes.length; i++) {
    const noteCol = document.createElement('div');
    noteCol.className = 'col-12 col-sm-12 col-md-4 col-lg-4 col-xl-2';
    
    const container = document.createElement('div');
    container.className = 'container fade-in';
    
    // 监听动画结束事件,移除动画类
    container.addEventListener('animationend', () => {
      container.classList.remove('fade-in');
    });
    
    // 剩下的元素创建逻辑和方法1一致,这里简化一下
    container.innerHTML = `
      <div class="text">${notes[i].task}</div>
      <div class="date">${notes[i].date}</div>
      <div class="time">${notes[i].time}</div>
      <div class="button" onClick="delete_note_by_key(${i})">
        <i class="fa fa-times"></i>
      </div>
    `;
    
    noteCol.appendChild(container);
    flexContainer.appendChild(noteCol);
  }
}

这种方法虽然能解决问题,但全量渲染DOM的性能开销还是存在,所以优先推荐方法1。

内容的提问来源于stack exchange,提问作者Anton Susman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:17:50