[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"site-stats":4,"post-38":5},true,null,[6,29],{"locked":7,"post":8},false,{"id":9,"authorId":10,"categoryId":11,"tagId":12,"title":13,"tip":14,"coverUrl":15,"content":16,"videoUrl":14,"views":17,"likes":18,"isPublished":3,"isRecommended":3,"isPinned":7,"allowComment":3,"createdAt":19,"updatedAt":20,"deletedAt":4,"category":21,"tag":24,"author":26,"liked":7},38,1,5,19,"我把博客的鼠标做成了一朵樱花（附完整代码）","","https:\u002F\u002Fimg.sanshijiu.cn\u002Fpost\u002F1\u002F1790583569144f9eba361.gif",">前段时间给博客换樱花主题，越看那个系统默认的小箭头越不顺眼，索性把鼠标也重做了一遍。做完发现效果意外地好，来串门的朋友第一句话都从\"文章不错\"变成了\"你这鼠标哪弄的\"。\n\n那就开源吧，代码已经放在 [[Gitee 仓库]](https:\u002F\u002Fgitee.com\u002Fsanshijiu\u002Fsakura-cursor) ，先看效果：\n\n![樱花鼠标效果演示 #center](https:\u002F\u002Fimg.sanshijiu.cn\u002Fpost\u002F1\u002F1790583569144f9eba361.gif)\n\n## 一套完整的光标语言\n\n不只是换个图标，整套东西有六种状态，互相衔接：\n\n| 场景 | 效果 |\n|---|---|\n| 平时移动 | 一朵带花杆的小樱花，杆尖就是点击点 |\n| 悬停可点击处 | 花朵正脸盛开 |\n| 按下 | 花缩小一号，像按进花心 |\n| 输入框 | 竖着的一朵迷你樱花，替代 I 形光标 |\n| 移动轨迹 | 划过的地方飘落樱花花瓣 |\n| 点击 | 点击处炸开一圈花瓣，中心冒一颗小星星 |\n\n整套东西零依赖：一个 CSS 文件管四种静态光标，一个 JS 文件管拖尾、点击绽放和可点元素接管，加起来不到 200 行。\n\n## 实现一：四种静态光标，全部内联 SVG\n\nCSS 的 `cursor: url()` 支持直接塞一段 SVG，所以四种静态光标没有用到任何图片文件，零网络请求。花瓣的渐变、白描边、投影都是矢量画的，还带一个樱花花瓣标志性的尖端小缺口。\n\n先看默认光标这条（完整版在文末）：\n\n```css\nbody {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg ...>') 5 5, auto;\n}\n```\n\n这朵 SVG 里我最想安利的是结构：**一片花瓣画一次，`rotate(72°)` 复制五次**。五瓣樱花是旋转对称图形，写五遍几乎相同的贝塞尔曲线太蠢了，所以每片花瓣都是同一条 `path`，只是套了不同的旋转：\n\n```svg\n\u003Cg transform=\"translate(11 11) rotate(-45)\">\n  \u003Cg transform=\"rotate(0)   translate(0 -7) scale(0.75)\">\u003Cpath d=\"...\" \u002F>\u003C\u002Fg>\n  \u003Cg transform=\"rotate(72)  translate(0 -7) scale(0.75)\">\u003Cpath d=\"...\" \u002F>\u003C\u002Fg>\n  \u003Cg transform=\"rotate(144) translate(0 -7) scale(0.75)\">\u003Cpath d=\"...\" \u002F>\u003C\u002Fg>\n  \u003Cg transform=\"rotate(216) translate(0 -7) scale(0.75)\">\u003Cpath d=\"...\" \u002F>\u003C\u002Fg>\n  \u003Cg transform=\"rotate(288) translate(0 -7) scale(0.75)\">\u003Cpath d=\"...\" \u002F>\u003C\u002Fg>\n  \u003Ccircle r=\"2.6\" fill=\"#ffd76e\" stroke=\"#f0b64a\" stroke-width=\"0.7\" \u002F>\n\u003C\u002Fg>\n```\n\n外层再补两笔：一根绿色花杆（`stroke-linecap=\"round\"` 的曲线）加一片叶子，中心一点嫩黄花蕊，\"手拿一朵小花\"的感觉就出来了。\n\n有个容易忽略的细节是 `url(...)` 后面跟的两个数字——**热点坐标（hotspot）**，它决定\"点击点\"落在图案的哪个位置。四种光标各有各的讲究：\n\n| 光标 | SVG 尺寸 | 热点 | 为什么 |\n|---|---|---|---|\n| 默认（带杆小花） | 40×40 | `5 5` | 杆尖在左上角，指哪点哪 |\n| 悬停（盛开花） | 36×36 | `18 18` | 花蕊正中心 |\n| 按下（缩小花） | 36×36 | `18 18` | 和悬停一致，按下才有\"没跑偏\"的感觉 |\n| 输入框（竖花） | 14×30 | `7 15` | 花茎中点，对齐文字基线 |\n\n热点给错了，用户会觉得\"点了没反应\"或者\"偏了一点\"，这是自定义光标最常见的翻车点。\n\n四种状态的选择器大概是这个思路（完整 SVG 见文末）：\n\n```css\n\u002F* 悬停：a、button、label 等常见可点元素先列上，JS 会自动补漏 *\u002F\na, button, [role='button'], summary, label {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg ...>') 18 18, pointer;\n}\n\n\u002F* 按下：同一朵花整体 scale(0.85)，热点不变 *\u002F\na:active, button:active, [role='button']:active {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg ...>') 18 18, pointer;\n}\n\n\u002F* 输入框：竖着的迷你樱花，按钮型 input 除外（它们走可点击状态） *\u002F\ninput:not([type='button']):not([type='submit']):not([type='reset']),\ntextarea,\n[contenteditable='true'] {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg ...>') 7 15, text;\n}\n```\n\n注意每个 `cursor` 声明的**兜底值**：默认光标兜 `auto`、可点元素兜 `pointer`、输入框兜 `text`。Safari 至今不支持 SVG 光标，靠这个兜底机制自动回退系统默认，页面照常用，只是少点花。\n\n## 实现二：拖尾和点击绽放，一个 canvas 粒子池\n\n动态效果全部画在一张全屏 canvas 上。这张画布 `pointer-events: none`、`z-index` 拉满，不拦截任何点击：\n\n```js\nvar cv = document.createElement('canvas')\ncv.style.cssText =\n  'position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:99999'\ndocument.body.appendChild(cv)\nvar ctx = cv.getContext('2d')\n\n\u002F\u002F 高分屏适配，按设备像素比放大画布，最多到 2，够清晰也不浪费\nfunction resize() {\n  dpr = Math.min(window.devicePixelRatio || 1, 2)\n  cv.width = window.innerWidth * dpr\n  cv.height = window.innerHeight * dpr\n}\n```\n\n粒子本体是一个对象池：每个粒子带位置、速度、自转、寿命，寿命尽了从数组里移除：\n\n```js\nfunction spawn(x, y, vx, vy, star) {\n  parts.push({\n    x: x, y: y, vx: vx, vy: vy,\n    rot: Math.random() * Math.PI * 2,      \u002F\u002F 初始角度随机\n    vr: (Math.random() - 0.5) * 0.2,       \u002F\u002F 自转速度随机\n    size: star ? 3 + Math.random() * 3 : 4.5 + Math.random() * 3.5,\n    life: 1,\n    decay: 0.014 + Math.random() * 0.012,  \u002F\u002F 寿命衰减速度随机\n    star: !!star\n  })\n  \u002F\u002F 池子满了丢最老的，快速乱甩也不会堆积\n  if (parts.length > MAX_PARTS) parts.splice(0, parts.length - MAX_PARTS)\n}\n```\n\n拖尾和点击只是两种不同的\"出生姿势\"：\n\n```js\n\u002F\u002F 拖尾：移动出粒限频，两成概率换成小星星\nfunction onMove(e) {\n  var now = performance.now()\n  if (now - lastSpawn \u003C MOVE_GAP) return   \u002F\u002F 26ms 一片，甩再快也只有一条短尾巴\n  lastSpawn = now\n  spawn(e.clientX, e.clientY,\n    (Math.random() - 0.5) * 1.1,           \u002F\u002F 水平速度小幅随机\n    -0.4 + Math.random() * 0.9,            \u002F\u002F 垂直初速可上可下\n    Math.random() \u003C 0.2)                   \u002F\u002F 两成概率出星星\n}\n\n\u002F\u002F 点击：炸开一圈花瓣 + 中心一颗小星星\nfunction onClick(e) {\n  for (var i = 0; i \u003C 7; i++) {\n    var a = (Math.PI * 2 * i) \u002F 7 + Math.random() * 0.5  \u002F\u002F 七瓣均匀撒开，加随机抖动\n    var sp = 1.3 + Math.random() * 1.2\n    spawn(e.clientX, e.clientY, Math.cos(a) * sp, Math.sin(a) * sp - 0.5, false)\n  }\n  spawn(e.clientX, e.clientY, 0, -0.3, true)\n}\n```\n\n花瓣和星星都是 canvas 路径画的。花瓣两条贝塞尔曲线收出一个带尖端小缺口的形状；星星更取巧——四段**二次贝塞尔曲线全过原点**，天然凹成四角星：\n\n```js\nfunction drawStar(c, s) {\n  c.fillStyle = STAR_COLOR\n  c.beginPath()\n  c.moveTo(0, -s)\n  c.quadraticCurveTo(0, 0, s, 0)   \u002F\u002F 每段曲线的中途都路过圆心\n  c.quadraticCurveTo(0, 0, 0, s)   \u002F\u002F 四段拼起来就是一个凹四角星\n  c.quadraticCurveTo(0, 0, -s, 0)\n  c.quadraticCurveTo(0, 0, 0, -s)\n  c.fill()\n}\n\nfunction drawPetal(c, s) {\n  c.fillStyle = PETAL_COLOR\n  c.beginPath()\n  c.moveTo(0, s)\n  c.bezierCurveTo(-s * 0.9, s * 0.2, -s * 0.55, -s * 0.75, 0, -s * 0.9)\n  c.bezierCurveTo(s * 0.55, -s * 0.75, s * 0.9, s * 0.2, 0, s)\n  c.fill()\n}\n```\n\n每帧更新就是标准的粒子循环：位移、加重力、自转、扣寿命、画出来：\n\n```js\nfunction frame() {\n  ctx.clearRect(0, 0, cv.width, cv.height)\n  for (var i = parts.length - 1; i >= 0; i--) {\n    var p = parts[i]\n    p.x += p.vx\n    p.y += p.vy\n    p.vy += 0.02          \u002F\u002F 一点点重力，花瓣缓缓往下飘\n    p.rot += p.vr\n    p.life -= p.decay\n    if (p.life \u003C= 0) { parts.splice(i, 1); continue }\n    ctx.save()\n    ctx.translate(p.x * dpr, p.y * dpr)\n    ctx.rotate(p.rot)\n    \u002F\u002F 全程不透明，颜色和光标一致，寿命尽了直接消失\n    if (p.star) drawStar(ctx, p.size * dpr)\n    else drawPetal(ctx, p.size * dpr)\n    ctx.restore()\n  }\n  requestAnimationFrame(frame)\n}\n```\n\n性能上就两个限制：**粒子池上限 56 个、移动出粒限频**。加上所有事件监听都标了 `{ passive: true }` 告诉浏览器\"我不调 `preventDefault`，你尽管滚动优化\"，快速甩鼠标也只有一条优雅的短尾巴，不会糊成一片。\n\n## 实现三：可点元素自动接管，最得意的一招\n\n给链接、按钮换光标，常规做法是列一堆类名清单：`a, button, .el-button, .el-switch...`。但只要用了组件库，清单永远列不全，总有小图标按钮漏出来显示系统小手，特别出戏。\n\n我的做法是反过来：监听全局 `mouseover`，凡是计算样式里 `cursor` 为 `pointer` 的元素——不管这个 pointer 是浏览器默认给的、组件库设置的、还是自己手写的——当场把它的光标改写成盛开花：\n\n```js\nfunction onOver(e) {\n  var el = e.target\n  if (!(el instanceof Element) || el.style.cursor) return  \u002F\u002F 已经改写过的直接跳过\n  if (getComputedStyle(el).cursor === 'pointer') el.style.cursor = FLOWER_CURSOR\n}\n```\n\n三行代码，等效于一份**永远不会漏的清单**：\n\n- `el.style.cursor` 非空说明改写过（或开发者手动指定过），跳过，几乎零开销；\n- `getComputedStyle(el).cursor === 'pointer'` 是把判定权交给浏览器——它说能点，那就能点；\n- `FLOWER_CURSOR` 就是 CSS 里那朵盛开花的 data URI，JS 里存了一份同样的字符串。\n\n唯一的代价是滑过每个元素时查一次计算样式，实测完全无感。\n\n## 拿去用\n\n仓库在这里：[https:\u002F\u002Fgitee.com\u002Fsanshijiu\u002Fsakura-cursor](https:\u002F\u002Fgitee.com\u002Fsanshijiu\u002Fsakura-cursor)，MIT 协议，两步接入：\n\n```html\n\u003Clink rel=\"stylesheet\" href=\"sakura-cursor.css\">\n\u003Cscript src=\"sakura-cursor.js\">\u003C\u002Fscript>\n```\n\n纯 HTML、WordPress、Hexo、Vue、React 都能直接用。SSR 项目记得把脚本放客户端执行（Vue 套 `\u003CClientOnly>`）。JS 开头有四个参数可调：\n\n```js\nvar PETAL_COLOR = '#ffb7c5' \u002F\u002F 花瓣颜色\nvar STAR_COLOR = '#ffe3ec'  \u002F\u002F 星星颜色\nvar MOVE_GAP = 26           \u002F\u002F 拖尾间隔毫秒，越小越密\nvar MAX_PARTS = 56          \u002F\u002F 粒子池上限\n```\n\n换整套配色时记得 JS 的 `PETAL_COLOR` 和 CSS 光标 SVG 里的 `%23ffb7c5` 一起换（`%23` 是 `#` 的 URL 编码），两边一致才和谐——别问我怎么知道的。\n\n## 完整代码\n\n两个文件全文贴上，复制即用。\n\n**sakura-cursor.css**（四种静态光标）：\n\n```css\n\u002F* ============================================================\n   樱花鼠标 sakura-cursor.css\n   四种状态：默认带杆小樱花 \u002F 可点击盛开花 \u002F 按下缩小 \u002F 输入框竖花\n   Safari 不支持 SVG 光标会自动回退系统默认，不影响使用\n   ============================================================ *\u002F\n\n\u002F* 默认光标：一朵带花杆的小樱花，杆尖就是点击点 *\u002F\nbody {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"40\" height=\"40\" viewBox=\"0 0 32 32\">\u003Cdefs>\u003ClinearGradient id=\"pg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\u003Cstop offset=\"0\" stop-color=\"%23ffb7c5\"\u002F>\u003Cstop offset=\"1\" stop-color=\"%23ffb7c5\"\u002F>\u003C\u002FlinearGradient>\u003Cfilter id=\"fs\" x=\"-40%\" y=\"-40%\" width=\"180%\" height=\"180%\">\u003CfeDropShadow dx=\"0.8\" dy=\"0.8\" stdDeviation=\"0.9\" flood-color=\"%23e08cab\" flood-opacity=\"0.4\"\u002F>\u003C\u002Ffilter>\u003C\u002Fdefs>\u003Cpath d=\"M12.5 12.5 C 17.5 17.5, 22 22, 29 29\" fill=\"none\" stroke=\"%237cc4b0\" stroke-width=\"2.4\" stroke-linecap=\"round\"\u002F>\u003Cpath d=\"M16.5 16.5 C 21 15.4, 24 17.2, 24.9 20.8 C 20.5 22, 17.5 20.6, 16.5 16.5 Z\" fill=\"%2393d3bf\"\u002F>\u003Cg transform=\"translate(11 11) rotate(-45)\" filter=\"url(%23fs)\">\u003Cg transform=\"rotate(0) translate(0 -7) scale(0.75)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(72) translate(0 -7) scale(0.75)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(144) translate(0 -7) scale(0.75)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(216) translate(0 -7) scale(0.75)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(288) translate(0 -7) scale(0.75)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Ccircle r=\"2.6\" fill=\"%23ffd76e\" stroke=\"%23f0b64a\" stroke-width=\"0.7\"\u002F>\u003C\u002Fg>\u003C\u002Fsvg>') 5 5, auto;\n}\n\n\u002F* 可点击元素：盛开的五瓣樱花\n   （JS 还会自动接管其它 pointer 元素，这里只是先把最常见的列上） *\u002F\na,\nbutton,\n[role='button'],\nsummary,\nlabel {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"36\" height=\"36\" viewBox=\"0 0 32 32\">\u003Cdefs>\u003ClinearGradient id=\"pg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\u003Cstop offset=\"0\" stop-color=\"%23ffb7c5\"\u002F>\u003Cstop offset=\"1\" stop-color=\"%23ffb7c5\"\u002F>\u003C\u002FlinearGradient>\u003Cfilter id=\"fs\" x=\"-40%\" y=\"-40%\" width=\"180%\" height=\"180%\">\u003CfeDropShadow dx=\"0.8\" dy=\"0.8\" stdDeviation=\"0.9\" flood-color=\"%23e08cab\" flood-opacity=\"0.4\"\u002F>\u003C\u002Ffilter>\u003C\u002Fdefs>\u003Cg transform=\"translate(16 16)\" filter=\"url(%23fs)\">\u003Cg transform=\"rotate(0) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(72) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(144) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(216) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(288) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Ccircle r=\"2.8\" fill=\"%23ffd76e\" stroke=\"%23f0b64a\" stroke-width=\"0.7\"\u002F>\u003C\u002Fg>\u003C\u002Fsvg>') 18 18, pointer;\n}\n\n\u002F* 按下瞬间：同一朵花缩一号 *\u002F\na:active,\nbutton:active,\n[role='button']:active {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"36\" height=\"36\" viewBox=\"0 0 32 32\">\u003Cdefs>\u003ClinearGradient id=\"pg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\u003Cstop offset=\"0\" stop-color=\"%23ffb7c5\"\u002F>\u003Cstop offset=\"1\" stop-color=\"%23ffb7c5\"\u002F>\u003C\u002FlinearGradient>\u003C\u002Fdefs>\u003Cg transform=\"translate(2.4 2.4) scale(0.85)\">\u003Cg transform=\"translate(16 16)\">\u003Cg transform=\"rotate(0) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(72) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(144) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(216) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(288) translate(0 -7.5) scale(0.8)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1\"\u002F>\u003C\u002Fg>\u003Ccircle r=\"2.8\" fill=\"%23ffd76e\" stroke=\"%23f0b64a\" stroke-width=\"0.7\"\u002F>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fsvg>') 18 18, pointer;\n}\n\n\u002F* 输入框：竖着拿的迷你樱花（按钮型 input 除外，它们走可点击状态） *\u002F\ninput:not([type='button']):not([type='submit']):not([type='reset']):not([type='checkbox']):not([type='radio']),\ntextarea,\n[contenteditable='true'] {\n  cursor: url('data:image\u002Fsvg+xml;utf8,\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"14\" height=\"30\" viewBox=\"0 0 14 30\">\u003Cdefs>\u003ClinearGradient id=\"pg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\u003Cstop offset=\"0\" stop-color=\"%23ffb7c5\"\u002F>\u003Cstop offset=\"1\" stop-color=\"%23ffb7c5\"\u002F>\u003C\u002FlinearGradient>\u003Cfilter id=\"fs\" x=\"-40%\" y=\"-40%\" width=\"180%\" height=\"180%\">\u003CfeDropShadow dx=\"0.6\" dy=\"0.6\" stdDeviation=\"0.7\" flood-color=\"%23e08cab\" flood-opacity=\"0.4\"\u002F>\u003C\u002Ffilter>\u003C\u002Fdefs>\u003Cpath d=\"M7 12 C7 17, 7 22, 7 29\" fill=\"none\" stroke=\"%237cc4b0\" stroke-width=\"1.4\" stroke-linecap=\"round\"\u002F>\u003Cpath d=\"M7 19 C9.5 18.4, 11 19.3, 11.4 21.2 C9.3 21.8, 7.6 21, 7 19 Z\" fill=\"%2393d3bf\"\u002F>\u003Cg transform=\"translate(7 7)\" filter=\"url(%23fs)\">\u003Cg transform=\"rotate(0) translate(0 -4.2) scale(0.5)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1.2\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(72) translate(0 -4.2) scale(0.5)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1.2\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(144) translate(0 -4.2) scale(0.5)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1.2\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(216) translate(0 -4.2) scale(0.5)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1.2\"\u002F>\u003C\u002Fg>\u003Cg transform=\"rotate(288) translate(0 -4.2) scale(0.5)\">\u003Cpath d=\"M0 5.5 C -3.8 4.2, -4.8 0.6, -3.2 -2.4 C -2.4 -4, -1.6 -4.8, -1 -5 L 0 -3.8 L 1 -5 C 1.6 -4.8, 2.4 -4, 3.2 -2.4 C 4.8 0.6, 3.8 4.2, 0 5.5 Z\" fill=\"url(%23pg)\" stroke=\"%23ffffff\" stroke-width=\"1.2\"\u002F>\u003C\u002Fg>\u003Ccircle r=\"1.8\" fill=\"%23ffd76e\" stroke=\"%23f0b64a\" stroke-width=\"0.5\"\u002F>\u003C\u002Fg>\u003C\u002Fsvg>') 7 15, text;\n}\n```\n\n**sakura-cursor.js**（拖尾、点击绽放、可点元素接管）：\n\n```js\n\u002F* ============================================================\n   樱花鼠标 sakura-cursor.js\n   三件事：鼠标拖尾飘花瓣 \u002F 点击炸开一圈花印 \u002F 自动把可点元素换成盛开花\n   无依赖，放到页面任意位置即可\n   ============================================================ *\u002F\n(function () {\n  \u002F\u002F 触摸设备没有光标，开了也看不见\n  if (!window.matchMedia('(pointer: fine)').matches) return\n  \u002F\u002F 用户系统开了\"减少动态效果\"就不玩了\n  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return\n\n  \u002F\u002F 想换颜色改这里（和 CSS 光标里的色值保持一致才和谐）\n  var PETAL_COLOR = '#ffb7c5'\n  var STAR_COLOR = '#ffe3ec'\n  \u002F\u002F 拖尾每隔多少毫秒出一片花瓣，越小越密\n  var MOVE_GAP = 26\n  \u002F\u002F 粒子池上限，防止快速乱甩糊成一片\n  var MAX_PARTS = 56\n\n  \u002F\u002F 盛开花的 SVG，和 CSS 里那段相同，用于接管可点元素\n  var FLOWER_CURSOR = \"url('data:image\u002Fsvg+xml;utf8,\u003Csvg ...>') 18 18, pointer\"\n\n  \u002F\u002F 全屏画布，不拦截任何点击\n  var cv = document.createElement('canvas')\n  cv.style.cssText = 'position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:99999'\n  document.body.appendChild(cv)\n  var ctx = cv.getContext('2d')\n  var dpr = 1\n\n  function resize() {\n    dpr = Math.min(window.devicePixelRatio || 1, 2)\n    cv.width = window.innerWidth * dpr\n    cv.height = window.innerHeight * dpr\n  }\n  resize()\n\n  var parts = []\n  var lastSpawn = 0\n\n  function spawn(x, y, vx, vy, star) {\n    parts.push({\n      x: x, y: y,\n      vx: vx, vy: vy,\n      rot: Math.random() * Math.PI * 2,\n      vr: (Math.random() - 0.5) * 0.2,\n      size: star ? 3 + Math.random() * 3 : 4.5 + Math.random() * 3.5,\n      life: 1,\n      decay: 0.014 + Math.random() * 0.012,\n      star: !!star\n    })\n    if (parts.length > MAX_PARTS) parts.splice(0, parts.length - MAX_PARTS)\n  }\n\n  \u002F\u002F 拖尾：移动时隔一阵出一片花瓣，两成概率换成小星星\n  function onMove(e) {\n    var now = performance.now()\n    if (now - lastSpawn \u003C MOVE_GAP) return\n    lastSpawn = now\n    spawn(e.clientX, e.clientY, (Math.random() - 0.5) * 1.1, -0.4 + Math.random() * 0.9, Math.random() \u003C 0.2)\n  }\n\n  \u002F\u002F 点击：炸开一圈花瓣，中心一颗小星星\n  function onClick(e) {\n    for (var i = 0; i \u003C 7; i++) {\n      var a = (Math.PI * 2 * i) \u002F 7 + Math.random() * 0.5\n      var sp = 1.3 + Math.random() * 1.2\n      spawn(e.clientX, e.clientY, Math.cos(a) * sp, Math.sin(a) * sp - 0.5, false)\n    }\n    spawn(e.clientX, e.clientY, 0, -0.3, true)\n  }\n\n  \u002F\u002F 可点元素自动换盛开花：滑到任何 cursor:pointer 的元素上就地改写，\n  \u002F\u002F 比写死类名清单靠谱，组件库的、系统默认的、漏网的全都能接住\n  function onOver(e) {\n    var el = e.target\n    if (!(el instanceof Element) || el.style.cursor) return\n    if (getComputedStyle(el).cursor === 'pointer') el.style.cursor = FLOWER_CURSOR\n  }\n\n  function drawStar(c, s) {\n    c.fillStyle = STAR_COLOR\n    c.beginPath()\n    c.moveTo(0, -s)\n    c.quadraticCurveTo(0, 0, s, 0)\n    c.quadraticCurveTo(0, 0, 0, s)\n    c.quadraticCurveTo(0, 0, -s, 0)\n    c.quadraticCurveTo(0, 0, 0, -s)\n    c.fill()\n  }\n\n  function drawPetal(c, s) {\n    c.fillStyle = PETAL_COLOR\n    c.beginPath()\n    c.moveTo(0, s)\n    c.bezierCurveTo(-s * 0.9, s * 0.2, -s * 0.55, -s * 0.75, 0, -s * 0.9)\n    c.bezierCurveTo(s * 0.55, -s * 0.75, s * 0.9, s * 0.2, 0, s)\n    c.fill()\n  }\n\n  function frame() {\n    ctx.clearRect(0, 0, cv.width, cv.height)\n    for (var i = parts.length - 1; i >= 0; i--) {\n      var p = parts[i]\n      p.x += p.vx\n      p.y += p.vy\n      p.vy += 0.02 \u002F\u002F 一点点重力，花瓣缓缓往下飘\n      p.rot += p.vr\n      p.life -= p.decay\n      if (p.life \u003C= 0) {\n        parts.splice(i, 1)\n        continue\n      }\n      ctx.save()\n      ctx.translate(p.x * dpr, p.y * dpr)\n      ctx.rotate(p.rot)\n      \u002F\u002F 全程不透明，颜色和光标一致，寿命尽了直接消失\n      if (p.star) drawStar(ctx, p.size * dpr)\n      else drawPetal(ctx, p.size * dpr)\n      ctx.restore()\n    }\n    requestAnimationFrame(frame)\n  }\n\n  window.addEventListener('resize', resize)\n  window.addEventListener('mousemove', onMove, { passive: true })\n  window.addEventListener('mousedown', onClick, { passive: true })\n  window.addEventListener('mouseover', onOver, { passive: true })\n  requestAnimationFrame(frame)\n})()\n```\n\n换个颜色，就是你的了。",10,3,"2026-09-28T08:33:58.300Z","2026-10-01T01:44:06.346Z",{"id":11,"name":22,"color":23},"网站日志",4,{"id":12,"name":25},"代码分享",{"id":10,"nickname":27,"avatarUrl":28},"叁拾玖","https:\u002F\u002Fimg.sanshijiu.cn\u002Favatar\u002F1\u002F1787397803188bebb136a.jpg",{"prev":30,"next":4},{"id":31,"title":32},35,"都说ZCode送3亿Token是封口费？我查了时间线，事情不简单"]