news 2026/9/3 17:33:32

split dance 前端逐字动画:从字符拆分到 Web Animations API 实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
split dance 前端逐字动画:从字符拆分到 Web Animations API 实战

split dance 不是某个框架的官方 API,它是这个前端示例的项目代号。这个示例要解决的问题很小但很典型:界面上有一行文字,点击按钮之后,文字里的每个字符都被拆开,各自向上、向下、旋转、缩放,形成一种类似跳舞的错峰动画。拆分字符串本身是入门级的操作,但一旦拆分对象从字符串变成屏幕上的 DOM 节点,就会牵扯到 Unicode 编码、元素宽度、事件重复触发、动画生命周期管理等一系列细节。这篇文章会带你把一个这样的效果从零写出来,并解释每一步为什么要这样做,以及实际项目里最容易被忽略的坑。

这个 demo 不需要任何前端框架,不需要 npm 包,也不需要后端接口。保存为一个 HTML 文件,用浏览器打开就能复现。适合刚接触原生 JavaScript 的开发者,也适合给团队做活动页、个人主页、课程宣传页时参考。最终你会得到一个可以自由改文字、改运动幅度、改播放节奏的单文件组件。

1. 先把“拆分”和“动画”两个概念对齐

很多人第一次看到 split dance 这个名字,第一反应是“用 split 分割字符串”。这个方向并不错,但仅仅调用 split 并不能让文字动起来。真正让文字从静态变为动态的,是拆完之后的 DOM 节点结构发生了变化。

1.1 为什么要拆分:文本节点和元素节点的区别

在 HTML 中,下面这段代码里的文字虽然可见,但浏览器只会把它当成一个文本节点:

<p>split dance 文字动画</p>

文本节点本身不能拥有 transform、opacity、transition 这类视觉动画属性。你只能对整个 p 元素设置样式,动画发生时,整行文字会作为一个整体移动。

想让一行文字呈现出每个字符各自飞散、跳动、回弹的效果,必须先把一个字符串切分成很多个字符,然后为每个字符分别创建一个独立的元素节点。常见做法是让每个字符都待在一个 span 里:

<p> <span>s</span><span>p</span><span>l</span><span>i</span><span>t</span> </p>

这样每个 span 都是独立盒子,可以被 JavaScript 单独选中、单独设置动画。所谓 split dance,拆开的是字符串,搭建的是动画控制单元。

1.2 split 的数据结果:不是切词,而是切字符

在英文语境里,split 常被理解为“按空格切分成单词”。但做逐字动画时,希望拿到的是单个可见字符,比如把hello变成['h', 'e', 'l', 'l', 'o'],把你好世界变成['你', '好', '世', '界']

这一点和传统字符串切分不同。传统切分逻辑关心分隔符,例如:

const text = 'split dance 文字动画'; console.log(text.split(' ')); // ['split', 'dance', '文字动画']

这种结果对统计单词有用,但对逐字动画不够细。split dance 需要把粒度降到字符,而不是词语。

1.3 拆分后的字符节点如何成为动画的控制单元

当一个字符待在自己独立的 span 里,就可以给这个 span 设置transformopacity。控制单元的最小粒度是“一个字符为一个 span”,而不是“一行文字一个 span”。

在实际视觉表现中,这个模型可以拆成三层:

  • 容器层:负责框住整行文字,控制宽度和对齐。
  • 字符层:每一个 span 对应一个可见字符,负责位移和旋转。
  • 时间层:通过delay让不同字符在不同时间启动,形成错峰感。

当这三层配合起来,视觉效果就不再是文字整体平移,而像一排小角色依次起跳。后面所有代码都会围绕这三层结构展开。

2. 用一个 HTML 文件搭建最小可运行页面

先不要想得太复杂。最小闭环需要四个内容:一个字符串来源、一个显示区域、一个触发按钮、一段 JavaScript 逻辑。

2.1 环境要求与文件保存方式

这个示例只需要现代浏览器。推荐使用 Chrome 或 Edge 的最新稳定版本,因为代码里用到了 Web Animations API,从 Chrome 36、Firefox 48、Safari 13.1 开始都已有较好支持。

在电脑上新建一个文件夹,比如split-dance-demo,在文件夹里新建文件index.html。用文本编辑器打开它,把 HTML 代码写进去,然后直接用浏览器打开这个文件即可。

如果要把效果放进已有前端项目,也可以把样式和脚本分别移到对应文件中。这里用单文件示例先保证逻辑可运行,后续再谈工程化拆分。

2.2 页面结构与样式

HTML 结构里,显示文字的区域使用p标签,按钮使用button。给它们设置好 id,方便 JavaScript 获取。

<main class="stage"> <p class="text-line" id="textLine"></p> <button type="button" id="playBtn">开始跳舞</button> </main>

这里字符会被添加到textLine里。按钮点击后,重新拆分文字并启动动画。

样式上要特别注意.char-itemdisplay。如果 span 保持默认的 inline 布局,设置 transform 时不会生效。常见做法是设置为inline-block,让它既能在文本流中排布,又能作为动画元素存在:

.char-item { display: inline-block; white-space: pre; will-change: transform; }

white-space: pre是为了保留空格。逐字拆分后,空格也会被包进 span,如果样式表使用了默认的空格折叠规则,可能会让空格宽度消失。

2.3 初始状态下不让文字出现乱跳

页面首次加载时,一般希望看到一行正常的静态文字。只在点击按钮后才开始拆分和动画。所以在页面初始化阶段,调用一个把文字渲染到页面的函数,但不启动动画:

function renderText(str) { textLine.textContent = ''; const chars = Array.from(str); chars.forEach((ch) => { const span = document.createElement('span'); span.textContent = ch; textLine.appendChild(span); }); } renderText('split dance');

这里先把文字显示出来。真正做拆分、做动画的代码,会在后面章节逐步加入。

3. 用 Array.from 完成 split dance 的第一步:逐字拆分

逐字拆分的实现看起来只是把字符串变成数组,但如果直接调用字符的split(''),会在包含 emoji、特殊符号时得到意料之外的结果。

3.1 使用 split('') 会踩到的 Unicode 问题

JavaScript 字符串底层使用 UTF-16 编码。很多常见字符正好是一个 UTF-16 代码单元,但 emoji 和部分生僻字会用两个代码单元表示。比如下面这个例子:

const text = 'split dance 😀'; console.log(text.split(''));

在浏览器里运行时,emoji 可能变成两个乱码符号。这是因为split('')按 UTF-16 代码单元切分,没有理解人类眼中的“一个字符”。

更稳妥的做法是使用Array.from

const chars = Array.from('split dance 😀'); console.log(chars);

Array.from会按可迭代对象展开字符串,能识别码点,所以对 emoji 的切分要比split('')好很多。

但这里要补充一点:Array.from也不是万能方案。如果字符串包含组合字符,比如一个字母后面跟着音标符号,它仍然可能被拆成两个码点。视觉上看起来是一个字符,实际上由多个码点组成。更精细的做法需要借助Intl.Segmenter按可感知字符切分。对于大多数标题、口号、短句场景,Array.from已经足够。

3.2 创建 span 节点并保留空格

不要把拆分后的字符直接拼成 HTML 字符串,尤其是文字内容来自用户、接口或数据库时,直接拼 innerHTML 可能引入 XSS 风险。推荐用document.createElement创建节点,再用textContent设置内容。

function splitTextIntoChars(str) { textLine.textContent = ''; textLine.setAttribute('aria-label', str); const chars = Array.from(str); const elements = []; chars.forEach((ch) => { const span = document.createElement('span'); span.setAttribute('aria-hidden', 'true'); span.className = 'char-item'; if (ch === ' ') { span.textContent = '\u00A0'; span.classList.add('char-space'); } else { span.textContent = ch; } textLine.appendChild(span); elements.push(span); }); return elements; }

这里做了两件额外的事:

  • 空格字符不直接使用普通空格,而是使用不换行空格\u00A0,并增加一个char-space类,避免空格被折叠后视觉上消失。
  • 给 textLine 设置aria-label,让辅助技术可以读整段文字,而不会把每个字符当成独立词语逐个朗读。

3.3 拆分阶段完成后应看到的节点结构

页面初始化后,打开浏览器开发者工具,Elements 面板里应该可以看到类似的层级:

<p class="text-line" id="textLine" aria-label="split dance 文字动画"> <span class="char-item" aria-hidden="true">s</span> <span class="char-item" aria-hidden="true">p</span> <span class="char-item" aria-hidden="true">l</span> <span class="char-item" aria-hidden="true">i</span> <span class="char-item" aria-hidden="true">t</span> <span class="char-item char-space" aria-hidden="true">&nbsp;</span> <span class="char-item" aria-hidden="true">d</span> ... </p>

如果页面上直接显示出了整个数组对象,比如s,p,l,i,t,说明代码里把数组赋给了 textContent,而不是把数组里的字符逐个创建成节点。

4. 让字符在屏幕上跳舞:Web Animations API 用法

字符已经拆到独立的 span 里,接下来要处理动画。这里不使用 setInterval 手写每一帧,而是采用浏览器提供的 Web Animations API。

4.1 为什么选 Web Animations API 而不是 setInterval

实现逐字运动有很多方式:

  • CSS transition 配合 class:简单,但适合单次状态变化。
  • CSS keyframes 配合动画类:适合通用节奏,但很难做到每个字符轨迹都不同。
  • setInterval 或 requestAnimationFrame:可控性强,但要自己计算每一帧的位置。
  • Web Animations API:可以直接在 JavaScript 里生成 keyframes,并且支持动态随机轨迹。

split dance 需要“每个字符都有一点自己的运动轨迹”,而不是所有字符都执行完全一样的动画。用 Web Animations API 最方便,因为可以为每个元素单独生成 keyframes 数组:

element.animate(keyframes, options);

每次调用会返回一个 Animation 对象,可以对这个对象执行cancel()pause()play(),适合处理连续点击时的清理问题。

4.2 为每个字符生成随机轨迹

为了让视觉效果像舞蹈,而不是整齐划一的机械运动,需要给每个字符生成不完全相同的水平位移、垂直位移和旋转角度。

先写一个随机数工具函数:

function randomBetween(min, max) { return Math.random() * (max - min) + min; }

在一次动画周期里,可以让字符经历起飞、高处旋转、回弹、归位几个阶段。每个阶段的坐标都用随机数生成:

const keyframes = [ { transform: 'translate(0, 0) rotate(0deg) scale(1)', offset: 0, }, { transform: `translate(${randomBetween(-28, 28)}px, ${randomBetween(-42, -18)}px) rotate(${randomBetween(-45, 45)}deg) scale(${randomBetween(0.9, 1.3)})`, offset: 0.2, }, { transform: `translate(${randomBetween(-38, 38)}px, ${randomBetween(12, 42)}px) rotate(${randomBetween(-60, 60)}deg) scale(${randomBetween(0.9, 1.15)})`, offset: 0.55, }, { transform: `translate(${randomBetween(-16, 16)}px, ${randomBetween(-10, 10)}px) rotate(${randomBetween(-18, 18)}deg) scale(1)`, offset: 0.8, }, { transform: 'translate(0, 0) rotate(0deg) scale(1)', offset: 1, }, ];

offset 表示 keyframe 在时间轴上的位置。这里 0 对应起点,1 对应终点。中间加入不同阶段的控制点,能让动画看起来有节奏变化。

4.3 延迟与循环方向的取舍

逐字动画的错峰效果来自延迟。如果所有字符都在同一毫秒开始,它们会像一组整齐列队同时跳,缺少“舞蹈”的层次感。

常见做法是用字符序号乘以固定步进:

const options = { duration: randomBetween(800, 1400), easing: 'ease-in-out', delay: index * 18, iterations: Infinity, direction: 'alternate', };

这里delay让每个字符比前一个晚 18 毫秒进入动画。使用iterations: Infinity可以让它反复播放,形成持续的舞蹈效果。direction: 'alternate'会让动画正向结束后再反向播放,避免每次归位时都出现生硬的跳变。

如果只想播放一次,只需要把iterations调成12。实际项目中,循环次数不要盲目设置成 Infinity,因为无限动画对当前页面功耗有影响。

4.4 完整可运行代码

下面是完整的单文件实现。可以直接复制保存为index.html后打开。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>split dance 文字动画示例</title> <style> :root { --font-size: 2rem; } body { margin: 0; min-height: 100vh; display: flex; justify-content: center; align-items: center; background: #f6f7fb; font-family: "PingFang SC", "Microsoft YaHei", sans-serif; } .stage { max-width: 720px; width: calc(100% - 32px); padding: 24px; background: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); } .text-line { font-size: var(--font-size); line-height: 1.8; min-height: calc(var(--font-size) * 2.2); user-select: none; border: 1px dashed #d8d8e0; border-radius: 8px; padding: 24px; overflow-wrap: break-word; } .char-item { display: inline-block; white-space: pre; will-change: transform; } .char-space { min-width: 0.5em; } #playBtn { margin-top: 16px; font-size: 16px; padding: 8px 20px; cursor: pointer; border: none; border-radius: 8px; background: #36b37e; color: #ffffff; } #playBtn:hover { opacity: 0.85; } </style> </head> <body> <main class="stage"> <p class="text-line" id="textLine"></p> <button type="button" id="playBtn">开始跳舞</button> </main> <script> const textLine = document.getElementById('textLine'); const playBtn = document.getElementById('playBtn'); const originalText = 'split dance 项目示例,符号和空格都会被保留'; let runningAnimations = []; function splitTextIntoChars(str) { textLine.textContent = ''; textLine.setAttribute('aria-label', str); const chars = Array.from(str); const elements = []; chars.forEach((ch) => { const span = document.createElement('span'); span.setAttribute('aria-hidden', 'true'); span.className = 'char-item'; if (ch === ' ') { span.textContent = '\u00A0'; span.classList.add('char-space'); } else { span.textContent = ch; } textLine.appendChild(span); elements.push(span); }); return elements; } function randomBetween(min, max) { return Math.random() * (max - min) + min; } function playDance(elements) { runningAnimations.forEach((animation) => animation.cancel()); runningAnimations = []; elements.forEach((element, index) => { const keyframes = [ { transform: 'translate(0, 0) rotate(0deg) scale(1)', offset: 0, }, { transform: `translate(${randomBetween(-28, 28)}px, ${randomBetween(-42, -18)}px) rotate(${randomBetween(-45, 45)}deg) scale(${randomBetween(0.9, 1.3)})`, offset: 0.2, }, { transform: `translate(${randomBetween(-38, 38)}px, ${randomBetween(12, 42)}px) rotate(${randomBetween(-60, 60)}deg) scale(${randomBetween(0.9, 1.15)})`, offset: 0.55, }, { transform: `translate(${randomBetween(-16, 16)}px, ${randomBetween(-10, 10)}px) rotate(${randomBetween(-18, 18)}deg) scale(1)`, offset: 0.8, }, { transform: 'translate(0, 0) rotate(0deg) scale(1)', offset: 1, }, ]; const animation = element.animate(keyframes, { duration: randomBetween(800, 1400), easing: 'ease-in-out', delay: index * 18, iterations: Infinity, direction: 'alternate', }); runningAnimations.push(animation); }); } playBtn.addEventListener('click', () => { const elements = splitTextIntoChars(originalText); playDance(elements); }); splitTextIntoChars(originalText); </script> </body> </html>

这段代码的核心流程很清晰:点击按钮 -> 重新拆分文字 -> 取消上一次动画 -> 为每个字符生成随机 keyframes -> 启动无限循环动画。

这里要注意一个容易写错的地方:如果runningAnimations没有先执行cancel(),用户连续点击按钮时,旧的 Animation 对象仍然可能持有被删除的 DOM 引用,既浪费内存,又可能在后台继续执行无效动画。

5. 验证结果:如何确认 split dance 是按预期工作的

代码写完不等于效果正常。需要从页面表现、DOM 结构、异常输入三个角度验证。

5.1 手工观察的最低标准

打开页面后,应该先看到不带动画的静态文字。点击“开始跳舞”按钮后,字符应该逐个错峰运动。判断标准包含以下几点:

  • 字符没有直接变成数组逗号分隔的样子。
  • 字符之间的空格仍然可见,文字没有挤成一团。
  • 每个字符都在独立运动,而不是整行文字一起平移。
  • 动画反复播放时没有停止或闪烁。
  • 连续点击按钮后,页面不会出现多个动画叠加造成的卡顿。

如果点击后页面没有变化,优先打开浏览器控制台检查 JavaScript 报错。

5.2 在 DevTools 里检查字符层级

按 F12 打开开发者工具,选择 Elements 面板,查看textLine下的子节点。见到的结构应当是一排 span,而不是一段纯文本节点。

如果看到的是纯文本,说明拆分函数没有被调用,或者textContent被设置成了数组本身。

在 Console 里还可以主动验证拆分逻辑:

const text = 'split dance 😀'; console.log(text.split('')); console.log(Array.from(text));

两组输出对比后,能看到split('')对 emoji 的输出是异常的两个码点,而Array.from更接近视觉上的单字符。

5.3 验证异常输入和边界情况

推荐准备几组特殊输入来测试:

  • 只有空格的字符串。
  • 包含英文逗号、中文逗号、句号、引号的字符串。
  • 包含 emoji 的字符串。
  • 包含中英文混排的长句子。

分别修改originalText后刷新页面观察。最明显的风险是空格被吞掉、emoji 被拆成两半、长文本换行时字符被截断到下一行但动画仍然遮挡其他模块。

在真实项目中,如果要处理用户输入内容,还应该加入白名单校验或限制输入长度,避免把一段上千字的文本全部拆成几万个 span 导致页面卡死。

6. 常见问题与排查链路

写这个 demo 时最容易出问题的点,集中在拆分方式、空格处理、动画生命周期和重复点击这几个环节。

6.1 页面打开后只显示一整段静态文字

现象:页面能看到文字,但点击按钮没有任何反应。

可能原因:

  • playBtn的 click 事件没有正确绑定。
  • splitTextIntoChars抛出了异常,动画没有执行到。
  • textLine的 id 或脚本位置不对,获取元素时返回 null。

检查方式:打开控制台,查看是否有 TypeError。如果脚本放在页面 head 中,且没有使用DOMContentLoaded,执行时可能找不到按钮元素。

处理建议:把 script 标签放在 body 底部;确保 id 和代码里一致;在 click 回调第一行加console.log验证事件触发。

6.2 动画只闪一下就不再继续

现象:首次点击后字符动了一下,随后停下来。

可能原因:

  • iterations被设置成了1,动画只播放一次就结束。
  • direction不是alternate,动画回到终点后直接停止。
  • 动画被后续代码立即取消。

检查方式:查看调用element.animate时传入的 options,确认iterations是否为Infinity。同时检查是否在循环中误调用了runningAnimations.forEach((animation) => animation.cancel())

处理建议:如果希望循环播放,显式设置iterations: Infinity。如果希望播放固定次数,设置数字,并不要把取消逻辑放在每次循环内部。

6.3 空格消失或字符间距异常

现象:按钮点击后,原本单词之间的距离没了,或者中文文字之间出现明显多余间隔。

可能原因:

  • 空格被包进 inline 元素后,受默认white-space: normal影响被折叠。
  • 把空格直接设置成空字符串。
  • 对 span 设置了过大的 padding 或 margin。

检查方式:在 Elements 面板中查看空格对应的 span 内容。如果是空格字符,检查样式表里是否设置了white-space: pre;如果字符直接不存在,检查拆分函数是否正确处理了空格。

处理建议:将空格字符转成\u00A0,并为空格的 span 设置最小宽度。

6.4 连续点击按钮导致动画越来越卡

现象:多次点击按钮后,页面响应变慢,甚至越来越卡。

可能原因:

  • 每次点击生成大量 span,旧节点没有真正释放。
  • 每次点击都创建 Web Animations API 对象,但没有 cancel 旧对象。
  • will-change: transform被加到了大量长期元素上。

检查方式:在 Performance 面板里录制点击前后的性能数据,观察 JS 堆内存和动画节点数量。也可以在 Console 执行document.querySelectorAll('.char-item').length检查节点是否持续增加。

处理建议:点击时先清空容器,再 cancel 动画列表,并且把runningAnimations重新赋值为空数组。不要给成百上千个元素都加will-change

6.5 排查顺序总表

问题现象常见原因检查方式处理建议
点击无反应脚本获取不到按钮或事件未绑定查看 Console 报错script 放 body 底部,检查 id
文字被拆成逗号数组把数组直接赋给了textContent查看 Elements 面板遍历数组逐个创建 span
emoji 显示异常使用split('')按 UTF-16 代码单元切分Console 运行Array.from对比改用Array.fromIntl.Segmenter
动画只播放一次options.iterations 被设为 1查看 animate 配置设置iterations: Infinity或需要的次数
文字间距异常空格被折叠或 span 样式错误检查空格 span 内容和 CSS使用 NBSP 并设置white-space: pre
多次点击卡顿旧动画未取消查看 runningAnimations 数量每次点击前 cancel 所有旧动画

7. 从 split dance 延伸出去的开发建议与最佳实践

动画项目很容易写完就不管了,但放到真实项目里,还需要考虑用户感受、性能边界和无障碍问题。

7.1 拆出来的不一定是字符,也可以是词语或 DOM 元素

split dance 的思路可以扩展到更多场景。如果做关键词强调,可以把一段话按词语拆开,只有重点词语参与动画。如果做列表展示,可以把列表项当成“字符”,让它们在进入可视区域时逐项错峰出现。

切分单位取决于需求,而不一定非是字符。单位越大,动画越容易控制,阅读干扰越小。

7.2 短文案展示比长段落更适合字符动画

逐字动画确实很生动,但不宜滥用。标题、按钮文案、短标语用起来效果较好,因为用户能一眼看出完整内容。正文、长段落、合同文案、数据表格这类内容如果逐字乱跳,用户几乎无法阅读。

实际项目中建议:

  • 页面首屏最多一到两个逐字动画模块。
  • 正文内容使用普通文本,最多做整体淡入。
  • 带图标的标签不要把图标和文字同时拆散,视觉噪音会很大。
  • 用户可以手动关闭动画时,体验会更好。

7.3 用浏览器原生能力减少动画卡顿

现代浏览器对 transform 和 opacity 做了合成器优化,动画过程中尽量避免改变lefttopwidthheight这类属性,否则会触发布局和重绘。

当前示例中的位移全程使用translate,旋转使用rotate,缩放使用scale,都属于合成器友好的属性。这是合适的做法。

另外,Web Animations API 会在元素被移除时释放动画对象,但为了稳妥,仍建议在创建新动画前统一 cancel。

7.4 发布前检查清单

把类似 split dance 的动画模块交付前,可以按下面这张清单逐项过一遍:

  • 确认文字内容能按预期拆分成单字符,中文、英文、空格、标点都正确。
  • 确认 emoji 不会被拆成乱码。
  • 确认页面上至少保留一份原始完整文本供辅助技术读取。
  • 确认动画是用户主动触发,而不是页面加载后强制播放。
  • 确认短文字效果正常,长文字不会造成性能问题。
  • 确认连续点击不会叠加多个动画。
  • 确认按钮在触屏设备上有足够的点击区域。
  • 确认弱网或低端机环境下,动画不会阻塞文本阅读。

这个项目的核心其实不是把字符串切开,而是把一个不可分割的文本节点,重构成一组可以独立控制、又能按统一秩序协作的动画单元。理解了这一点,你会发现 split 只是一个入口,后面接的舞台可以有无数种玩法。下一步可以继续练习的关键方向有三个:一是把动态 keyframes 抽成可配置参数,让设计师不用改代码就能调动作幅度;二是加入 IntersectionObserver,让文字滚动到可视区域后再开始跳舞;三是按词语或短句分组拆分,让动画节奏更有语义,而不是单纯逐字炫技。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 17:29:55

从《So Long》看独立音乐传播:解码村曲新歌追踪与歌词视频策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 17:29:25

Groovy Tech House Set混音工程:从BPM分析到响度标准化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 17:27:35

从零构建C#/.NET商用项目:跨平台开发与电商订单系统实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 17:26:24

基于Dify平台构建RAG智能体:从零实现代码分析助手

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 17:22:42

Actor-Critic强化学习算法原理与Matlab工程实现详解

简介&#xff1a;本资源是面向强化学习初学者与Matlab实践者的Actor-Critic算法完整实现包&#xff0c;聚焦于连续/离散控制任务中的策略优化与价值评估协同训练问题&#xff0c;适用于高校课程设计、科研原型验证及RL算法原理理解。压缩包共10个文件&#xff0c;含7个核心MATL…

作者头像 李华
网站建设 2026/9/3 17:19:32

纪念一下自己第一个成型的项目【记得闪卡】(jidecards)基本完善(鸿蒙Next端原生闪卡式学习记忆软件)(原名记得闪卡)

【记得闪卡】算是我第一个真正做成型的项目。目前已经上架华为应用市场进行了多轮迭代。但是迭代无穷无尽&#xff0c;现在又往里面塞了个Agent&#xff0c;不知道3.0.0版本会不会因此被卡上线 项目地址&#xff1a; jidecards-anki-harmonyos:基于 HarmonyOS 与 Anki 内核的…

作者头像 李华