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

display:none状态下CSS @keyframes文本动画失效问题求助

问题:点击复制图标时通知动画未生效

我受教授指派实现点击复制图标时显示通知的功能,尝试使用@keyframes结合opacity实现动画,但未达到预期效果,相关代码如下:

关键帧设置

.copyNotification {
    display: none;
    position: absolute;
    right: 0;
}

.copyNotification.show {
    display: block;
    animation: show 0.5s backwards;
}

@keyframes show {
    0% {
        transform: translateY(-100%);
        opacity: 0;
    }
    100% {
        transform: translateY(0%);
        opacity: 1;
    }
}

完整代码片段

JavaScript

const copyBtn = document.querySelector("#copyBtn");
const copyNotification = document.querySelector("#copyNotification");

copyBtn.addEventListener("click", function (e) {
  e.preventDefault();
  copyNotification.classList.add("show");
});

CSS

:root {
  /*  Font Size */
  --ff--primary: "Chivo", sans-serif;
  /* Font Weight */
  --fw--bold: 400;
  --fw--very-bold: 600;

  /* Colors */
  --clr--purple: hsl(250, 95%, 74%);
  --clr--white: hsl(0, 0%, 100%);
}

/*
  1. Use a more-intuitive box-sizing model.
*/
*,
*::before,
*::after {
  box-sizing: border-box;
}
/*
    2. Remove default margin
  */
* {
  margin: 0;
}
/*
    Typographic tweaks!
    3. Add accessible line-height
    4. Improve text rendering
  */
body {
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/*
    5. Improve media defaults
  */
img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}
/*
    6. Remove built-in form typography styles
  */
input,
button,
textarea,
select {
  font: inherit;
}
/*
    7. Avoid text overflows
  */
p,
h1,
h2,
h3,
h4,
h5,
h6 {
  overflow-wrap: break-word;
}
/*
    8. Create a root stacking context
  */
#root,
#__next {
  isolation: isolate;
}

body {
  background-color: var(--clr--purple);
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: var(--ff--primary);
  font-size: var(--fw--bold);
}

.passwordForm {
  display: grid;
  height: 200px;
  max-width: 450px;
  background-color: var(--clr--white);
  padding: 1.5rem;
  border-radius: 1rem;

  h1 {
    text-align: center;
  }

  .divider {
    border-top: 1.5px solid var(--clr--purple);
    margin-bottom: 1rem;
    margin-inline: -1.5rem;
  }

  form {
    display: grid;
    gap: 2rem;
    
    .copyNotification {
      display: none;
      position: absolute;
      right: 0;
    }

    .copyNotification.show {
      display: block;
      animation: show 0.5s backwards;
    }

    @keyframes show {
      0% {
        transform: translateY(0%);
        opacity: 0;
      }
      100% {
        transform: translateY(-100%);
        opacity: 1;
      }
    }

    .passwordDisplay {
      position: relative;
      width: 100%;

      .copyBtn {
        position: absolute;
        font-size: 2rem;
        top: 0;
        right: 1rem;

        i {
          cursor: pointer;
          color: var(--clr--purple);
        }

        i:hover {
          opacity: 0.7;
        }

        i:active {
          opacity: 0.5;
        }
      }

      .passwordValue {
        border: 2px solid #937efc;
        width: 100%;
        padding: 0.7rem 1rem;
      }
      .passwordValue:focus {
        border-color: hsl(250, 95%, 74%, 0.6);
        -webkit-box-shadow: -2px 4px 15px -6px rgba(147, 126, 252, 1);
        -moz-box-shadow: -2px 4px 15px -6px rgba(147, 126, 252, 1);
        box-shadow: -2px 4px 15px -6px rgba(147, 126, 252, 1);
        outline: 0 none;
      }
    }
  }
}

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="style.css" class="css" />
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" />
  <link href="https://fonts.googleapis.com/css2?family=Chivo:wght@400;600&display=swap" rel="stylesheet" />
  <title>Password Generator JS</title>
</head>
<body>
  <main class="passwordForm">
    <h1>Password Generator</h1>
    <div class="divider"></div>
    <form action="#">
      <div class="passwordDisplay">
        <input type="text" disabled="disabled" class="passwordValue" id="passwordValue" placeholder="Password" />
        <div class="copyBtn" id="copyBtn">
          <i class="fa-regular fa-copy"></i>
        </div>
        <p class="copyNotification" id="copyNotification">Copied</p>
      </div>
    </form>
  </main>
  <script src="app.js"></script>
</body>
</html>

问题分析与修复方案

核心问题

  1. display: none无法触发动画:元素从display: none切换到display: block时,浏览器不会计算过渡/动画,因为元素瞬间进入文档流,没有中间状态。
  2. 动画关键帧逻辑混乱:原代码中关键帧的transform方向前后不一致,且未给通知设置初始定位,导致动画效果异常。
  3. 通知未自动隐藏:点击后通知会一直停留在页面上,没有自动消失的交互逻辑。

修改后的代码

CSS 调整
/* 替换原copyNotification相关样式 */
.copyNotification {
  position: absolute;
  right: 1rem;
  top: -2.5rem; /* 定位到复制按钮上方 */
  opacity: 0;
  visibility: hidden;
  transform: translateY(20px); /* 初始向下偏移 */
  transition: all 0.3s ease;
  background-color: var(--clr--purple);
  color: var(--clr--white);
  padding: 0.3rem 0.8rem;
  border-radius: 0.3rem;
}

.copyNotification.show {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
JavaScript 调整
const copyBtn = document.querySelector("#copyBtn");
const copyNotification = document.querySelector("#copyNotification");

copyBtn.addEventListener("click", function (e) {
  e.preventDefault();
  // 先移除类再添加,避免重复点击无动画
  copyNotification.classList.remove("show");
  void copyNotification.offsetWidth; // 触发重绘
  copyNotification.classList.add("show");
  
  // 2秒后自动隐藏通知
  setTimeout(() => {
    copyNotification.classList.remove("show");
  }, 2000);
});

修复说明

  • 用opacity和visibility替代display控制显示隐藏,保留元素在文档流中的位置,确保过渡动画生效。
  • 给通知添加背景色和内边距,提升视觉辨识度,同时设置合理的定位,避免遮挡其他元素。
  • 添加定时器实现通知自动隐藏,同时处理重复点击的动画触发问题(通过移除类+触发重绘,确保每次点击都能播放动画)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:58:12