敢测 AI 服务现场

← 返回首页

它交过的答卷

PackyAPI

https://www.packyapi.com/ · 加入 2026-05-18 15:45:40

服务 1 个 公开答卷 2 条 Affiliate / 推荐佣金

该按钮可能包含合作链接。佣金不影响题目、历史记录或展示顺序。

我们试过这些服务

敢测按具体服务记录真实输出。一个站点可能同时提供编程、文本、图片或视频服务。

packyApiClaudeOpus4.7

代码能力
声称模型:claude-opus-4-7
单次消耗以实测记录为准。

最近答卷

这里展示的是具体题目的原始结果。敢测不替用户打总分,开发者直接看输出、消耗和现场标签。

claude-opus-4-7

该模型能力由 PackyAPI 站点接入

Succeeded
HTML 预览区域 · iframe 内为该服务商本次生成结果 新页面打开完整 HTML
查看原始输出
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>番茄任务看板</title>
<style>
  *, *::before, *::after { box-sizing: border-box; }
  :root {
    --bg: #f5f6f8;
    --surface: #ffffff;
    --border: #e3e5ea;
    --text: #1f2330;
    --muted: #6b7280;
    --primary: #c0392b;
    --primary-hover: #a93222;
    --accent: #2c3e50;
    --success: #2f855a;
    --warning: #b45309;
    --danger: #b91c1c;
    --shadow: 0 1px 2px rgba(0,0,0,.04), 0 2px 8px rgba(0,0,0,.04);
  }
  html, body { margin: 0; padding: 0; }
  body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
    line-height: 1.5;
    min-height: 100vh;
  }
  header.app-header {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 14px 20px;
    position: sticky;
    top: 0;
    z-index: 10;
  }
  .header-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
    justify-content: space-between;
    max-width: 1400px;
    margin: 0 auto;
  }
  h1 {
    font-size: 16px;
    margin: 0;
    font-weight: 600;
    letter-spacing: .3px;
  }
  h1 .dot {
    display: inline-block;
    width: 8px; height: 8px;
    background: var(--primary);
    border-radius: 50%;
    margin-right: 8px;
    vertical-align: middle;
  }
  .controls {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
  }
  input[type="text"], input[type="number"], select, input[type="search"] {
    font: inherit;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 7px 10px;
    outline: none;
    transition: border-color .15s, box-shadow .15s;
  }
  input:focus, select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(44,62,80,.12);
  }
  button {
    font: inherit;
    cursor: pointer;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 6px;
    padding: 7px 12px;
    transition: background .15s, border-color .15s, color .15s;
  }
  button:hover { background: #f0f1f4; }
  button.primary {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
  }
  button.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
  button.ghost { background: transparent; }
  button.danger { color: var(--danger); }
  button.danger:hover { background: #fdecec; }
  .switch {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    user-select: none;
    cursor: pointer;
    font-size: 13px;
    color: var(--muted);
  }
  .switch input { display: none; }
  .switch .track {
    width: 34px; height: 18px;
    background: #cbd0d8;
    border-radius: 999px;
    position: relative;
    transition: background .15s;
  }
  .switch .track::after {
    content: "";
    position: absolute;
    top: 2px; left: 2px;
    width: 14px; height: 14px;
    background: #fff;
    border-radius: 50%;
    transition: transform .15s;
    box-shadow: 0 1px 2px rgba(0,0,0,.2);
  }
  .switch input:checked + .track { background: var(--primary); }
  .switch input:checked + .track::after { transform: translateX(16px); }

  main {
    max-width: 1400px;
    margin: 0 auto;
    padding: 16px 20px 40px;
  }
  .new-task {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 14px;
    margin-bottom: 16px;
    box-shadow: var(--shadow);
  }
  .new-task form {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr auto;
    gap: 8px;
    align-items: start;
  }
  .field { display: flex; flex-direction: column; gap: 4px; }
  .field label {
    font-size: 12px;
    color: var(--muted);
  }
  .form-error {
    color: var(--danger);
    font-size: 12px;
    margin-top: 8px;
    min-height: 16px;
  }
  .board {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
  }
  .column {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    min-height: 320px;
    box-shadow: var(--shadow);
  }
  .column-head {
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 600;
    font-size: 13px;
    letter-spacing: .3px;
  }
  .column-head .count {
    font-weight: 400;
    color: var(--muted);
    font-size: 12px;
    background: #f0f1f4;
    border-radius: 999px;
    padding: 2px 8px;
  }
  .column-body {
    flex: 1;
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-height: 200px;
  }
  .column-body.drag-over {
    background: #fffaf0;
    outline: 2px dashed var(--primary);
    outline-offset: -6px;
    border-radius: 8px;
  }
  .empty-state {
    text-align: center;
    color: var(--muted);
    padding: 24px 10px;
    font-size: 13px;
    border: 1px dashed var(--border);
    border-radius: 6px;
  }
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    padding: 10px 12px;
    cursor: grab;
    display: flex;
    flex-direction: column;
    gap: 8px;
    transition: box-shadow .15s, transform .15s;
  }
  .card:hover { box-shadow: 0 2px 6px rgba(0,0,0,.06); }
  .card.dragging { opacity: .5; }
  .card.priority-high { border-left-color: var(--danger); }
  .card.priority-medium { border-left-color: var(--warning); }
  .card.priority-low { border-left-color: var(--success); }
  .card-title {
    font-weight: 600;
    font-size: 14px;
    word-break: break-word;
  }
  .card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 11px;
  }
  .tag {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    background: #f0f1f4;
    color: var(--muted);
  }
  .tag.high { background: #fdecec; color: var(--danger); }
  .tag.medium { background: #fff4e0; color: var(--warning); }
  .tag.low { background: #e6f4ec; color: var(--success); }
  .card-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    align-items: center;
  }
  .card-actions button {
    font-size: 12px;
    padding: 4px 8px;
  }
  .timer {
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--primary);
    font-weight: 600;
    margin-left: auto;
  }
  .timer.done { color: var(--success); }
  .pomo-progress {
    height: 4px;
    background: #f0f1f4;
    border-radius: 999px;
    overflow: hidden;
  }
  .pomo-progress > div {
    height: 100%;
    background: var(--primary);
    width: 0%;
    transition: width .3s linear;
  }
  .search-row {
    display: flex;
    gap: 8px;
    align-items: center;
  }
  .search-row input[type="search"] { width: 220px; }

  @media (max-width: 900px) {
    .board { grid-template-columns: 1fr; }
    .new-task form { grid-template-columns: 1fr 1fr; }
    .new-task form .field-title { grid-column: 1 / -1; }
    .new-task form .submit-wrap { grid-column: 1 / -1; }
  }
  @media (max-width: 480px) {
    header.app-header { padding: 12px 14px; }
    main { padding: 12px 14px 32px; }
    .search-row input[type="search"] { width: 100%; }
    .controls { width: 100%; }
    .search-row { flex: 1; }
    .new-task form { grid-template-columns: 1fr; }
  }
</style>
</head>
<body>
<header class="app-header">
  <div class="header-row">
    <h1><span class="dot"></span>番茄任务看板</h1>
    <div class="controls">
      <div class="search-row">
        <input type="search" id="search" placeholder="搜索任务标题…" aria-label="搜索任务">
      </div>
      <label class="switch" title="开启后 25 分钟将以 25 秒运行,便于演示">
        <input type="checkbox" id="demoMode">
        <span class="track"></span>
        <span>演示加速</span>
      </label>
      <button id="clearDone" class="ghost danger" type="button">清空已完成</button>
    </div>
  </div>
</header>

<main>
  <section class="new-task" aria-label="新建任务">
    <form id="taskForm" novalidate>
      <div class="field field-title">
        <label for="title">任务标题</label>
        <input type="text" id="title" maxlength="80" placeholder="例如:实现用户登录接口" required>
      </div>
      <div class="field">
        <label for="estimate">预估番茄数</label>
        <input type="number" id="estimate" min="1" max="20" value="1" required>
      </div>
      <div class="field">
        <label for="priority">优先级</label>
        <select id="priority">
          <option value="high">高</option>
          <option value="medium" selected>中</option>
          <option value="low">低</option>
        </select>
      </div>
      <div class="field submit-wrap">
        <label>&nbsp;</label>
        <button type="submit" class="primary">添加任务</button>
      </div>
      <div class="form-error" id="formError" role="alert"></div>
    </form>
  </section>

  <section class="board" aria-label="任务看板">
    <div class="column" data-status="todo">
      <div class="column-head"><span>待办</span><span class="count" id="count-todo">0</span></div>
      <div class="column-body" data-status="todo" id="col-todo"></div>
    </div>
    <div class="column" data-status="doing">
      <div class="column-head"><span>进行中</span><span class="count" id="count-doing">0</span></div>
      <div class="column-body" data-status="doing" id="col-doing"></div>
    </div>
    <div class="column" data-status="done">
      <div class="column-head"><span>已完成</span><span class="count" id="count-done">0</span></div>
      <div class="column-body" data-status="done" id="col-done"></div>
    </div>
  </section>
</main>

<script>
(function(){
  "use strict";

  const STORAGE_KEY = "pomo-board-v1";
  const DEMO_KEY = "pomo-board-demo";
  const FULL_SECONDS = 25 * 60;
  const DEMO_SECONDS = 25;

  /** @type {{id:string,title:string,estimate:number,priority:string,status:string,completedPomos:number,timer:{remaining:number,running:boolean,total:number}|null,createdAt:number}[]} */
  let tasks = [];
  let demoMode = false;
  let searchQuery = "";
  const tickHandles = {}; // taskId -> intervalId

  // ---------- Persistence ----------
  function load() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const arr = JSON.parse(raw);
        if (Array.isArray(arr)) tasks = arr;
      }
    } catch (e) { tasks = []; }
    try {
      demoMode = localStorage.getItem(DEMO_KEY) === "1";
    } catch(e){}
    // make sure no timer auto-resumes after refresh
    tasks.forEach(t => { if (t.timer) t.timer.running = false; });
  }
  function save() {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks)); } catch(e){}
  }
  function saveDemo() {
    try { localStorage.setItem(DEMO_KEY, demoMode ? "1" : "0"); } catch(e){}
  }

  // ---------- Helpers ----------
  function uid() {
    return "t_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2, 7);
  }
  function fmt(sec) {
    sec = Math.max(0, Math.floor(sec));
    const m = Math.floor(sec / 60);
    const s = sec % 60;
    return (m < 10 ? "0"+m : m) + ":" + (s < 10 ? "0"+s : s);
  }
  function totalForTimer() {
    return demoMode ? DEMO_SECONDS : FULL_SECONDS;
  }
  function priorityLabel(p) {
    return p === "high" ? "高优先级" : p === "low" ? "低优先级" : "中优先级";
  }
  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, c => ({
      "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"
    }[c]));
  }

  // ---------- Rendering ----------
  function render() {
    const cols = {
      todo: document.getElementById("col-todo"),
      doing: document.getElementById("col-doing"),
      done: document.getElementById("col-done"),
    };
    Object.values(cols).forEach(c => c.innerHTML = "");

    const q = searchQuery.trim().toLowerCase();
    const counts = { todo:0, doing:0, done:0 };
    const filtered = tasks.filter(t => !q || t.title.toLowerCase().includes(q));

    // counts reflect filtered visible
    filtered.forEach(t => counts[t.status] = (counts[t.status]||0)+1);

    for (const status of ["todo","doing","done"]) {
      const list = filtered.filter(t => t.status === status);
      if (list.length === 0) {
        const empty = document.createElement("div");
        empty.className = "empty-state";
        empty.textContent = q
          ? "没有匹配的任务"
          : status === "todo" ? "暂无待办任务,先添加一个吧"
          : status === "doing" ? "把卡片拖到这里开始专注"
          : "完成的任务会显示在这里";
        cols[status].appendChild(empty);
      } else {
        list.forEach(t => cols[status].appendChild(renderCard(t)));
      }
      document.getElementById("count-"+status).textContent = counts[status];
    }
  }

  function renderCard(task) {
    const el = document.createElement("div");
    el.className = "card priority-" + task.priority;
    el.draggable = true;
    el.dataset.id = task.id;

    const title = document.createElement("div");
    title.className = "card-title";
    title.textContent = task.title;

    const meta = document.createElement("div");
    meta.className = "card-meta";
    meta.innerHTML =
      '<span class="tag '+task.priority+'">'+priorityLabel(task.priority)+'</span>' +
      '<span class="tag">???? '+task.completedPomos+' / '+task.estimate+'</span>';

    const actions = document.createElement("div");
    actions.className = "card-actions";

    const hasTimer = !!task.timer;
    const running = hasTimer && task.timer.running;

    if (!hasTimer) {
      const startBtn = document.createElement("button");
      startBtn.type = "button";
      startBtn.className = "primary";
      startBtn.textContent = "开始 25 分钟";
      startBtn.addEventListener("click", () => startPomo(task.id));
      actions.appendChild(startBtn);
    } else {
      const toggleBtn = document.createElement("button");
      toggleBtn.type = "button";
      toggleBtn.className = running ? "" : "primary";
      toggleBtn.textContent = running ? "暂停" : "继续";
      toggleBtn.addEventListener("click", () => togglePomo(task.id));
      actions.appendChild(toggleBtn);

      const resetBtn = document.createElement("button");
      resetBtn.type = "button";
      resetBtn.textContent = "重置";
      resetBtn.addEventListener("click", () => resetPomo(task.id));
      actions.appendChild(resetBtn);
    }

    const delBtn = document.createElement("button");
    delBtn.type = "button";
    delBtn.className = "ghost danger";
    delBtn.textContent = "删除";
    delBtn.addEventListener("click", () => deleteTask(task.id));
    actions.appendChild(delBtn);

    const timerEl = document.createElement("span");
    timerEl.className = "timer";
    if (hasTimer) {
      timerEl.textContent = fmt(task.timer.remaining);
      if (task.timer.remaining <= 0) timerEl.classList.add("done");
    } else {
      timerEl.textContent = "";
    }
    actions.appendChild(timerEl);

    el.appendChild(title);
    el.appendChild(meta);
    el.appendChild(actions);

    if (hasTimer) {
      const prog = document.createElement("div");
      prog.className = "pomo-progress";
      const inner = document.createElement("div");
      const total = task.timer.total || totalForTimer();
      const pct = Math.max(0, Math.min(100, ((total - task.timer.remaining) / total) * 100));
      inner.style.width = pct + "%";
      prog.appendChild(inner);
      el.appendChild(prog);
    }

    // Drag events
    el.addEventListener("dragstart", e => {
      el.classList.add("dragging");
      e.dataTransfer.effectAllowed = "move";
      e.dataTransfer.setData("text/plain", task.id);
    });
    el.addEventListener("dragend", () => {
      el.classList.remove("dragging");
    });

    return el;
  }

  // ---------- Pomodoro logic ----------
  function startPomo(id) {
    const t = tasks.find(x => x.id === id);
    if (!t) return;
    if (t.status === "done") return;
    if (t.status === "todo") t.status = "doing";
    const total = totalForTimer();
    t.timer = { remaining: total, running: true, total };
    save();
    render();
    startTick(id);
  }
  function togglePomo(id) {
    const t = tasks.find(x => x.id === id);
    if (!t || !t.timer) return;
    if (t.timer.remaining <= 0) return;
    t.timer.running = !t.timer.running;
    save();
    render();
    if (t.timer.running) startTick(id);
    else stopTick(id);
  }
  function resetPomo(id) {
    const t = tasks.find(x => x.id === id);
    if (!t || !t.timer) return;
    stopTick(id);
    const total = totalForTimer();
    t.timer = { remaining: total, running: false, total };
    save();
    render();
  }
  function startTick(id) {
    stopTick(id);
    tickHandles[id] = setInterval(() => {
      const t = tasks.find(x => x.id === id);
      if (!t || !t.timer || !t.timer.running) { stopTick(id); return; }
      t.timer.remaining -= 1;
      if (t.timer.remaining <= 0) {
        t.timer.remaining = 0;
        t.timer.running = false;
        t.completedPomos = (t.completedPomos || 0) + 1;
        stopTick(id);
        save();
        render();
        // light notification
        try {
          // simple beep using Web Audio API (no external resource)
          const ctx = new (window.AudioContext || window.webkitAudioContext)();
          const o = ctx.createOscillator();
          const g = ctx.createGain();
          o.connect(g); g.connect(ctx.destination);
          o.frequency.value = 880;
          g.gain.value = 0.05;
          o.start();
          setTimeout(()=>{ o.stop(); ctx.close(); }, 250);
        } catch(e){}
        return;
      }
      save();
      // update only timer text & progress for performance
      const card = document.querySelector('.card[data-id="'+id+'"]');
      if (card) {
        const timerEl = card.querySelector(".timer");
        if (timerEl) timerEl.textContent = fmt(t.timer.remaining);
        const prog = card.querySelector(".pomo-progress > div");
        if (prog) {
          const total = t.timer.total || totalForTimer();
          prog.style.width = (((total - t.timer.remaining)/total)*100) + "%";
        }
      }
    }, 1000);
  }
  function stopTick(id) {
    if (tickHandles[id]) {
      clearInterval(tickHandles[id]);
      delete tickHandles[id];
    }
  }
  function stopAllTicks() {
    Object.keys(tickHandles).forEach(stopTick);
  }

  // ---------- CRUD ----------
  function addTask(data) {
    tasks.unshift({
      id: uid(),
      title: data.title,
      estimate: data.estimate,
      priority: data.priority,
      status: "todo",
      completedPomos: 0,
      timer: null,
      createdAt: Date.now(),
    });
    save();
    render();
  }
  function deleteTask(id) {
    stopTick(id);
    tasks = tasks.filter(t => t.id !== id);
    save();
    render();
  }
  function moveTask(id, newStatus) {
    const t = tasks.find(x => x.id === id);
    if (!t || t.status === newStatus) return;
    t.status = newStatus;
    if (newStatus === "done") {
      // stop any running timer
      if (t.timer && t.timer.running) {
        t.timer.running = false;
        stopTick(id);
      }
    }
    save();
    render();
  }
  function clearDone() {
    const doneTasks = tasks.filter(t => t.status === "done");
    if (doneTasks.length === 0) {
      alert("当前没有已完成的任务");
      return;
    }
    if (!confirm("确定要清空 " + doneTasks.length + " 个已完成的任务吗?")) return;
    doneTasks.forEach(t => stopTick(t.id));
    tasks = tasks.filter(t => t.status !== "done");
    save();
    render();
  }

  // ---------- Form ----------
  const form = document.getElementById("taskForm");
  const errorEl = document.getElementById("formError");
  form.addEventListener("submit", function(e){
    e.preventDefault();
    errorEl.textContent = "";
    const titleInput = document.getElementById("title");
    const estimateInput = document.getElementById("estimate");
    const priorityInput = document.getElementById("priority");

    const title = (titleInput.value || "").trim();
    const estimate = parseInt(estimateInput.value, 10);
    const priority = priorityInput.value;

    if (!title) { errorEl.textContent = "请输入任务标题"; titleInput.focus(); return; }
    if (title.length > 80) { errorEl.textContent = "标题不能超过 80 个字符"; return; }
    if (!estimate || isNaN(estimate) || estimate < 1 || estimate > 20) {
      errorEl.textContent = "预估番茄数应在 1 - 20 之间"; estimateInput.focus(); return;
    }
    if (!["high","medium","low"].includes(priority)) {
      errorEl.textContent = "请选择优先级"; return;
    }

    addTask({ title, estimate, priority });
    form.reset();
    document.getElementById("estimate").value = 1;
    document.getElementById("priority").value = "medium";
    titleInput.focus();
  });

  // ---------- DnD ----------
  document.querySelectorAll(".column-body").forEach(col => {
    col.addEventListener("dragover", e => {
      e.preventDefault();
      e.dataTransfer.dropEffect = "move";
      col.classList.add("drag-over");
    });
    col.addEventListener("dragleave", () => col.classList.remove("drag-over"));
    col.addEventListener("drop", e => {
      e.preventDefault();
      col.classList.remove("drag-over");
      const id = e.dataTransfer.getData("text/plain");
      const status = col.dataset.status;
      if (id && status) moveTask(id, status);
    });
  });

  // ---------- Search ----------
  document.getElementById("search").addEventListener("input", function(e){
    searchQuery = e.target.value || "";
    render();
  });

  // ---------- Demo mode ----------
  const demoToggle = document.getElementById("demoMode");
  demoToggle.addEventListener("change", function(){
    demoMode = demoToggle.checked;
    saveDemo();
    // Don't retroactively change existing timers; new timers will use new value.
  });

  // ---------- Clear done ----------
  document.getElementById("clearDone").addEventListener("click", clearDone);

  // ---------- Init ----------
  load();
  demoToggle.checked = demoMode;
  render();

  window.addEventListener("beforeunload", () => {
    // pause all timers so they don't auto-resume
    tasks.forEach(t => { if (t.timer) t.timer.running = false; });
    save();
    stopAllTicks();
  });
})();
</script>
</body>
</html>
有真实输出 完整 HTML 包含交互脚本 无外部资源 无 Markdown 包裹

2026-05-23 17:04:54 · 97079 ms · 总耗时 97s · 输入 659 / 输出 9,361 / 总计 10,020 tokens

claude-opus-4-7

该模型能力由 PackyAPI 站点接入

疑似截断
HTML 预览区域 · iframe 内为该服务商本次生成结果 新页面打开完整 HTML
查看原始输出
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Glowing Ripple Button</title>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body {
    height: 100%;
    width: 100%;
    overflow: hidden;
    background: radial-gradient(circle at center, #1a1a3a 0%, #05050f 100%);
    font-family: 'Segoe UI', Tahoma, sans-serif;
  }
  body {
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .ripple-btn {
    position: relative;
    padding: 18px 48px;
    font-size: 1.2rem;
    font-weight: 600;
    letter-spacing: 2px;
    color: #fff;
    background: linear-gradient(135deg, #6a11cb, #2575fc);
    border: none;
    border-radius: 50px;
    cursor: pointer;
    outline: none;
    box-shadow:
      0 0 20px rgba(101, 60, 255, 0.6),
      0 0 40px rgba(37, 117, 252, 0.4),
      inset 0 0 12px rgba(255, 255, 255, 0.15);
    transition: transform 0.15s ease, box-shadow 0.3s ease;
  }
  .ripple-btn:hover {
    transform: scale(1.05);
    box-shadow:
      0 0 30px rgba(101, 60, 255, 0.8),
      0 0 60px rgba(37, 117, 252, 0.6),
      inset 0 0 12px rgba(255, 255, 255, 0.25);
  }
  .ripple-btn:active {
    transform: scale(0.97);
  }
  .particle {
    position: fixed;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    pointer-events: none;
    background: #fff;
    box-shadow: 0 0 8px 2px currentColor;
    animation: fly 700ms cubic-bezier(0.15, 0.7, 0.3, 1) forwards;
    will-change: transform, opacity;
  }
  @keyframes fly {
    0%   { transform: translate(-50%, -50%) translate(0, 0) scale(1); opacity: 1; }
    100% { transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(0.2); opacity: 0; }
  }
</style>
</head>
<body>
  <button class="ripple-btn" id="btn">CLICK ME</button>
  <script>
    const btn = document.getElementById('btn');
    const colors = ['#ff4dd2', '#4dd0ff', '#ffd84d', '#a64dff', '#4dffb8', '#ff7a4d'];

    btn.addEventListener('click', (e) => {
      const rect = btn.getBoundingClientRect();
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const count = 24;

      for (let i = 0; i < count; i++) {
        const p = document.createElement('div');
        p.className = 'particle';
        const angle = (Math.PI * 2 * i) / count + (Math.random() * 0.4 - 0.2);
        const distance = 80 + Math.random() * 100;
        const dx = Math.cos(angle) * distance;
        const dy = Math.sin(angle) * distance;
        const size = 4 + Math.random
有真实输出 完整 HTML 包含交互脚本 无外部资源 无 Markdown 包裹

2026-05-23 15:06:16 · 18074 ms · 总耗时 18s · 输入 228 / 输出 1,200 / 总计 1,428 tokens