<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>thun888</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.hzchu.top/</id>
  <link href="https://blog.hzchu.top/" rel="alternate"/>
  <link href="https://blog.hzchu.top/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, thun888</rights>
  <subtitle>夏日当空，心如深渊 | 还看（；´д｀）ゞ</subtitle>
  <title>Thun888</title>
  <updated>2026-04-11T13:11:53.000Z</updated>
  <entry>
    <author>
      <name>thun888</name>
    </author>
    <category term="瞎折腾" scheme="https://blog.hzchu.top/categories/%E7%9E%8E%E6%8A%98%E8%85%BE/"/>
    <category term="网工" scheme="https://blog.hzchu.top/tags/%E7%BD%91%E5%B7%A5/"/>
    <content>
      <![CDATA[<h2 id="前言"><a class="markdownIt-Anchor" href="#前言"></a> 前言</h2><p>实验室网络的布局大概长这样：</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1137/1200;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg?fmt=avif" alt="原网络拓扑" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="原网络拓扑" href="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">原网络拓扑</span></div></div><p>别问，问就是硬件条件制约，一堆百兆网线，少数几根千兆网线还不够长，实际布局更离谱<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/Blob/blobcatcomftears.png"/></span></p><p>后来在角落里发现了前辈遗留下来的一台中新网安的防火墙，全千兆的接口，那就利用这个来改进一下实验室的网络吧</p><p>首先，最大的硬件制约在于——接入层交换机是两个千兆的上联口、24个百兆的下联口。上联线路是千兆的，但是内网被百兆锁死了。（所以说才接了那么多交换机，那些交换机都是千兆的，方便内网传数据）</p><p>最激进的方案就是直接替换掉这个百兆的接入层交换机。但是这样子的话改动有点大。所以我想了想，干脆将其中的一个上联口通过调整配置改为下联口，再接入到另外一台千兆交换机，就可以达到近似千兆的效果了。</p><p>同时这样也不用手动配置端口模式和vlan那些，让原来的交换机负责</p><p>接上<code>console</code>线，试了一下常见的弱密码，发现都不行。所以需要想办法绕过密码。</p><blockquote><p>这个交换机也是个老古董，部分实现在新型号上可能有所差异，仅供参考。</p></blockquote><h2 id="获取交换机密码"><a class="markdownIt-Anchor" href="#获取交换机密码"></a> 获取交换机密码</h2>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">下文来自<a target="_blank" href="/wiki/more/实验室网络/获取锐捷交换机的特权密码/">「获取锐捷交换机的特权密码」</a></span>      <div class="divider-line"></div>    </div>  <h3 id="引导模式下去掉配置"><a class="markdownIt-Anchor" href="#引导模式下去掉配置"></a> 引导模式下去掉配置</h3><p>在接上<code>console</code>线的情况下，断电重启。</p><blockquote><p>注意，在没进入特权模式下，是没有办法执行<code>reload</code>重启的</p></blockquote><p>重启后可以看到，<code>Press Ctrl+B to enter Boot Menu</code>，尽快按下<kbd>Ctrl</kbd> + <kbd>B</kbd>，进入引导菜单。</p><p><img src="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69d9aeb734672.png" alt="image.png" /></p><p>随后按下1进入Ctrl模式，在读条完成后，需要按下<kbd>Ctrl</kbd> + <kbd>C</kbd>二次确定</p><blockquote><p>若长期无响应，可以尝试按下回车，查看前面是否出现了<code>Ctrl&gt;</code>的提示符</p></blockquote><p>在进入该调试环境后，可以使用<code>dir</code>列出文件目录。在列出文件目录后，可以看到有一个<code>config.text</code>文件，储存了交换机的配置</p><p>使用<code>rename config.text config.bak</code>将该文件重命名，随后执行<code>reload</code>重启，进入主系统</p><p>在主系统下使用<code>enable</code>进入特权模式，随后执行<code>copy config.bak config.text</code>将配置文件改回去</p><p>执行<code>copy startup-config running-config</code>应用配置</p><blockquote><p>可以使用<code>show running-config</code>查看配置</p></blockquote><p>随后，趁交换机<span class="annotated" data-tippy-content="仍处于特权模式下">“不注意”</span>，设置一下密码覆盖掉配置里原有的设置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入全局配置模式</span></span><br><span class="line">configure terminal</span><br><span class="line"><span class="comment"># 启用密码加密功能</span></span><br><span class="line">service password-encryption</span><br><span class="line"><span class="comment"># 设置管理账号</span></span><br><span class="line">username admin password yourpassword</span><br><span class="line"><span class="comment"># 设置特权模式密码</span></span><br><span class="line"><span class="built_in">enable</span> password yourpassword</span><br></pre></td></tr></table></figure><h3 id="查看配置文件并反解密码"><a class="markdownIt-Anchor" href="#查看配置文件并反解密码"></a> 查看配置文件并反解密码</h3><p>直接改人家密码，终归是不太好。既然我们都能够读出配置文件了，能不能直接反解出密码呢？看了一眼配置文件，使用的是<code>Type 7</code>加密形式来保存密码，这种加密也和明文差不多了。</p><p>在网上查多数是思科的交换机的资料，锐捷交换机的也有：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">选择明文攻击</span></span><br><span class="line"><span class="string">根据已有密码和算法计算xlat</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getxlat</span>(<span class="params">enc_pw,dec_pw</span>):</span><br><span class="line">xlat = [<span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>,<span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>,<span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>,<span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>]</span><br><span class="line"><span class="comment">#seed为enc_pw的前两个字母</span></span><br><span class="line">seed = <span class="built_in">int</span>(enc_pw[<span class="number">0</span>:<span class="number">2</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;seed:&quot;</span>,seed)</span><br><span class="line">val = <span class="number">0</span></span><br><span class="line"><span class="comment">#enc_pw中的每两个字母对应一个明文字母</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>,<span class="built_in">len</span>(enc_pw)):</span><br><span class="line"><span class="built_in">print</span>(i)</span><br><span class="line"><span class="keyword">if</span> i%<span class="number">2</span> == <span class="number">0</span> <span class="keyword">and</span> i &gt;<span class="number">2</span> :</span><br><span class="line">seed = seed +<span class="number">1</span></span><br><span class="line">xlat[seed] = val ^ <span class="built_in">ord</span>(dec_pw[<span class="built_in">int</span>(i/<span class="number">2</span> - <span class="number">2</span>)])</span><br><span class="line"></span><br><span class="line">val = <span class="number">0</span></span><br><span class="line"><span class="built_in">print</span>(seed,xlat[seed])</span><br><span class="line">val = val *<span class="number">16</span></span><br><span class="line">tmp = enc_pw[i].upper()</span><br><span class="line"><span class="keyword">if</span> tmp &gt;= <span class="string">&#x27;0&#x27;</span> <span class="keyword">and</span>  tmp &lt;= <span class="string">&#x27;9&#x27;</span> :</span><br><span class="line">val = val  + <span class="built_in">ord</span>(tmp) - <span class="built_in">ord</span>(<span class="string">&#x27;0&#x27;</span>)</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> tmp &gt;= <span class="string">&#x27;A&#x27;</span> <span class="keyword">and</span>  tmp &lt;= <span class="string">&#x27;F&#x27;</span> : </span><br><span class="line">val = val + <span class="built_in">ord</span>(tmp) - <span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>) + <span class="number">10</span>;</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="built_in">print</span>(xlat)</span><br><span class="line"><span class="keyword">return</span> xlat</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decode</span>(<span class="params">xlat,enc_pw</span>):</span><br><span class="line">test_pw = <span class="string">&#x27;&#x27;</span></span><br><span class="line">seed = <span class="built_in">int</span>(enc_pw[<span class="number">0</span>:<span class="number">2</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;seed:&quot;</span>,seed)</span><br><span class="line">val = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>,<span class="built_in">len</span>(enc_pw)):</span><br><span class="line"><span class="built_in">print</span>(i)</span><br><span class="line"><span class="keyword">if</span> i%<span class="number">2</span> == <span class="number">0</span> <span class="keyword">and</span> i &gt;<span class="number">2</span> :</span><br><span class="line">seed = seed +<span class="number">1</span></span><br><span class="line">test_pw_char = <span class="built_in">chr</span>(val ^xlat[seed])</span><br><span class="line">test_pw += test_pw_char</span><br><span class="line">val = <span class="number">0</span></span><br><span class="line"><span class="built_in">print</span>(seed,xlat[seed],test_pw_char)</span><br><span class="line">val = val *<span class="number">16</span></span><br><span class="line">tmp = enc_pw[i].upper()</span><br><span class="line"><span class="keyword">if</span> tmp &gt;= <span class="string">&#x27;0&#x27;</span> <span class="keyword">and</span>  tmp &lt;= <span class="string">&#x27;9&#x27;</span> :</span><br><span class="line">val = val  + <span class="built_in">ord</span>(tmp) - <span class="built_in">ord</span>(<span class="string">&#x27;0&#x27;</span>)</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> tmp &gt;= <span class="string">&#x27;A&#x27;</span> <span class="keyword">and</span>  tmp &lt;= <span class="string">&#x27;F&#x27;</span> : </span><br><span class="line">val = val + <span class="built_in">ord</span>(tmp) - <span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>) + <span class="number">10</span>;</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="built_in">print</span>(test_pw)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span> :</span><br><span class="line">enc_pw = <span class="string">&#x27;1100320c1843080143797f&#x27;</span>+<span class="string">&#x27;\0&#x27;</span></span><br><span class="line">dec_pw = <span class="string">&#x27;ruijie@123&#x27;</span></span><br><span class="line">xlat = getxlat(enc_pw,dec_pw)</span><br><span class="line"></span><br><span class="line">xlat = [<span class="number">9999</span>, <span class="number">42</span>,   <span class="number">64</span>,   <span class="number">35</span>,   <span class="number">35</span>,   <span class="number">87</span>,   <span class="number">120</span>,  <span class="number">102</span>,  <span class="number">94</span>,   <span class="number">99</span>,   <span class="number">79</span>,   <span class="number">117</span>,  <span class="number">114</span>, <span class="number">71</span>, <span class="number">101</span>, <span class="number">114</span>, <span class="number">42</span>, <span class="number">109</span>, <span class="number">65</span>, <span class="number">114</span>, <span class="number">75</span>, <span class="number">76</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>, <span class="number">9999</span>]</span><br><span class="line">enc_pw = <span class="string">&#x27;1100320c1843080143797f&#x27;</span>+<span class="string">&#x27;\0&#x27;</span></span><br><span class="line">decode(xlat,enc_pw)</span><br></pre></td></tr></table></figure><blockquote><p>来自：<a href="https://www.cnblogs.com/dongpohezui/p/16065709.html">锐捷password 7 解密 - 东坡何罪 - 博客园</a></p></blockquote><h3 id="参考资料"><a class="markdownIt-Anchor" href="#参考资料"></a> 参考资料</h3><ol><li><a href="https://image.ruijie.com.cn/Upload/Article/c15d3e02-0e6a-4e5a-b9da-1a21f200871e/%E9%94%90%E6%8D%B7%E4%BA%A4%E6%8D%A2%E6%9C%BA%E4%BA%A7%E5%93%81%E7%BA%BF%E9%97%AE%E9%A2%98%E4%B8%80%E6%9C%AC%E9%80%9A%EF%BC%88V1.0%EF%BC%89/%E4%BA%A4%E6%8D%A2%E6%9C%BA%E4%BA%A7%E5%93%81%E7%BA%BF%E9%97%AE%E9%A2%98%E4%B8%80%E6%9C%AC%E9%80%9AV1.0/5d55c674-53bc-4f82-8b7f-b411a9f1eb32.htm">01 日常维护</a></li><li><a href="https://www.cnblogs.com/dongpohezui/p/16065709.html">锐捷password 7 解密 - 东坡何罪 - 博客园</a></li><li><a href="https://insecure.org/sploits/cisco.passwords.html">Cisco password decryption</a></li><li><a href="https://blog.csdn.net/andy891218/article/details/89446176">锐捷交换机-管理密码清除-Uboot层不支持“main_config_password_clear”命令恢复_xmodem utilities-CSDN博客</a></li></ol>  <div class="divider-container">    <div class="divider-line"></div>    <span class="divider-text">外部引用截止</span>    <div class="divider-line"></div>  </div>  <h2 id="修改配置"><a class="markdownIt-Anchor" href="#修改配置"></a> 修改配置</h2><p>交换机接口没有物理上“上下联”的区别，这里的情况是两个配置为<code>trunk</code>模式，其他为<code>access</code>模式，将其中一个改为<code>access</code>模式</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">enable</span></span><br><span class="line">configure terminal</span><br><span class="line">interface GigabitEthernet 0/25</span><br><span class="line"><span class="comment"># 关闭DHCP分配和ARP的信任</span></span><br><span class="line">no ip dhcp snooping trust</span><br><span class="line">no ip arp inspection trust</span><br><span class="line"><span class="comment"># 改为access模式</span></span><br><span class="line">switchport mode access</span><br><span class="line"><span class="comment"># 设置对应的vlan</span></span><br><span class="line">switchport access vlan 11111</span><br><span class="line">end</span><br><span class="line">write</span><br></pre></td></tr></table></figure><p>什么？你说为什么不直接把新交换机的其中一个网口也设为中继模式，然后直接接过来。这样子确实更好一点，但是它好处我用不上也没权限用，干脆改完以后一劳永逸。</p><h2 id="将防火墙变为普通交换机"><a class="markdownIt-Anchor" href="#将防火墙变为普通交换机"></a> 将“防火墙”变为“普通交换机”</h2>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">下文来自<a target="_blank" href="/wiki/more/实验室网络/配置中新网安防火墙/">「配置中新网安防火墙为普通交换机」</a></span>      <div class="divider-line"></div>    </div>  <p>说是防火墙，但我需要的更多是它的交换功能。</p><p>首先连接0号网口，再配置电脑IP为<code>192.168.1.2</code>，默认路由<code>192.168.1.1</code>。随后访问<code>https://192.168.1.1</code>。</p><blockquote><p>注意，默认不开放80端口访问</p></blockquote><p>使用默认账号密码（admin/admin）访问，在接口设置中将全部接口并入网桥（我这里保留了一个接口用于管理）</p><p><img src="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69d9db9b246db.png" alt="image.png" /></p><p><img src="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69d9dbdb0cddf.png" alt="image.png" /></p><p>随后在<code>console</code>执行以下命令，开启IPv6支持，然后就可以把线全部接过去了</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ZX-NGFW-6130&gt; enable</span><br><span class="line">ZX-NGFW-6130# configure terminal</span><br><span class="line">ZX-NGFW-6130(config)# ipv6 enable</span><br></pre></td></tr></table></figure>  <div class="divider-container">    <div class="divider-line"></div>    <span class="divider-text">外部引用截止</span>    <div class="divider-line"></div>  </div>  <p>当然，实际上还是不能和二层交换机划等号</p><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>利用多余的千兆口，绕开原有的百兆口限制</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1137/1200;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg?fmt=avif" alt="原网络拓扑" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="原网络拓扑" href="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_09-50-47-69dafa7d409d9.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">原网络拓扑</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1046/1228;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_15-54-34-69da03c64fbcb.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_15-54-34-69da03c64fbcb.jpg?fmt=avif" alt="新网络拓扑" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="新网络拓扑" href="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_15-54-34-69da03c64fbcb.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">新网络拓扑</span></div></div>    </div>    </div><p>实际效果（ipv6，ipv4上游有限速；上传也有几百兆）：</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1876/1056;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69db048a29f90.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69db048a29f90.png?fmt=avif" alt="image.png" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="image.png" href="https://onep.hzchu.top/mount/pic/myself/2026/04/image-69db048a29f90.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">image.png</span></div></div><p>内网测速：</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1232/547;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_11-42-57-69db1b0b84ca2.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_11-42-57-69db1b0b84ca2.jpg?fmt=avif" alt="Snipaste_2026-04-12_11-42-57.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-04-12_11-42-57.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-12_11-42-57-69db1b0b84ca2.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-04-12_11-42-57.jpg</span></div></div><h2 id="另外"><a class="markdownIt-Anchor" href="#另外"></a> 另外</h2><p>同时也试了下把干线直接接电脑上，发现获取不到ipv4，只有ipv6</p><p>查了下资料想了想，应该是默认情况下电脑发出的数据包不带<code>VLAN标签</code>，上游归到<code>Native VLAN</code>，刚好又没有DHCP就获取不到ipv4地址</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <p>手动配置VLAN ID后可以正常获取IP，不过速度很慢，<s>可能是因为网卡要从一堆数据里过滤出自己想要的吧</s></p>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:723/769;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_16-24-35-69db051881614.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_16-24-35-69db051881614.jpg?fmt=avif" alt="Snipaste_2026-04-11_16-24-35.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-04-11_16-24-35.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/04/Snipaste_2026-04-11_16-24-35-69db051881614.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-04-11_16-24-35.jpg</span></div></div>    </div>    </div><p>至于ipv6，我校是<span class="annotated" data-tippy-content="SLAAC">无状态自动配置方式</span>，可能是<code>Native VLAN</code>和别的VLAN间没有严格隔离，甚至由于接收到多个RA，同时生成了多个前缀的ipv6地址。上网的时候又默认把数据发给RA报文发送方，刚好就畅通无阻。不过在实际使用中不会用这个VLAN的，所以问题不大。</p>]]>
    </content>
    <id>https://blog.hzchu.top/2026/opt-lab-net/</id>
    <link href="https://blog.hzchu.top/2026/opt-lab-net/"/>
    <published>2026-04-11T13:11:53.000Z</published>
    <summary>达成成就：全学校网速最快的地方</summary>
    <title>优化实验室网络</title>
    <updated>2026-04-11T13:11:53.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>thun888</name>
    </author>
    <category term="瞎折腾" scheme="https://blog.hzchu.top/categories/%E7%9E%8E%E6%8A%98%E8%85%BE/"/>
    <category term="修修修" scheme="https://blog.hzchu.top/tags/%E4%BF%AE%E4%BF%AE%E4%BF%AE/"/>
    <content>
      <![CDATA[<h2 id="背景"><a class="markdownIt-Anchor" href="#背景"></a> 背景</h2><p>电脑接3D打印机调试被反噬了，干炸CPU后更换主板</p><h2 id="问题"><a class="markdownIt-Anchor" href="#问题"></a> 问题</h2><h3 id="bitlocker-正在加密"><a class="markdownIt-Anchor" href="#bitlocker-正在加密"></a> BitLocker 正在加密</h3><p>电脑有两个硬盘，维修前我取了一个装有资料的出来，剩下一块硬盘在不知道售后维修时进行了什么操作，给我硬盘挂了两把锁</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:843/357;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699fd8fee180f.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699fd8fee180f.png?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699fd8fee180f.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1689/1080;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-16-36-699fd90ab5c8d.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-16-36-699fd90ab5c8d.jpg?fmt=avif" alt="Snipaste_2026-02-01_21-16-36.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-02-01_21-16-36.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-16-36-699fd90ab5c8d.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-02-01_21-16-36.jpg</span></div></div>    </div>    </div><p>“正在加密”，但旁边的按钮显示“启用”，还没有取消键<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/capoo_world/5.webp"/></span></p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <p>在终端里输入<code>manage-bde -status</code>可以查看加密状态</p>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:622/782;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-21-51-699fd9b2929cf.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-21-51-699fd9b2929cf.jpg?fmt=avif" alt="Snipaste_2026-02-01_21-21-51.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-02-01_21-21-51.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-21-51-699fd9b2929cf.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-02-01_21-21-51.jpg</span></div></div>    </div>    </div><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <p>输入<code>manage-bde -off {盘符}:</code>停止加密</p>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1759/463;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-22-18-699fd9fc539d2.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-22-18-699fd9fc539d2.jpg?fmt=avif" alt="Snipaste_2026-02-01_21-22-18.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-02-01_21-22-18.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-01_21-22-18-699fd9fc539d2.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-02-01_21-22-18.jpg</span></div></div>    </div>    </div><h3 id="tailscale-无法启动"><a class="markdownIt-Anchor" href="#tailscale-无法启动"></a> Tailscale 无法启动</h3><p>始终处于<code>Starting..</code>或<code>Needs authentication</code>状态，登录依旧卡住</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:670/246;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-38-34-699fda96e4ca9.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-38-34-699fda96e4ca9.jpg?fmt=avif" alt="Snipaste_2026-02-02_14-38-34.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-02-02_14-38-34.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-38-34-699fda96e4ca9.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-02-02_14-38-34.jpg</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:623/225;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-51-11-699fda26c5719.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-51-11-699fda26c5719.jpg?fmt=avif" alt="Snipaste_2026-02-02_14-51-11.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2026-02-02_14-51-11.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-02_14-51-11-699fda26c5719.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2026-02-02_14-51-11.jpg</span></div></div>    </div>    </div><p>手动启动服务进程查看输出：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">PS</span> C:\Users\thun888&gt; tailscaled</span><br><span class="line">TPM: successfully read all properties</span><br><span class="line">...</span><br><span class="line">store.New failed: failed to migrate existing TPM<span class="literal">-sealed</span> state file to plaintext format: newTPMStore(<span class="string">&quot;C:\\ProgramData\\Tailscale\\server-state.conf&quot;</span>): failed to unseal state file: failed to unseal encryption key with TPM: tpm2.Load: TPM_RC_INTEGRITY (handle <span class="number">0</span>): integrity check failed; starting with <span class="keyword">in</span><span class="literal">-memory</span> store with a health warning</span><br><span class="line">logpolicy: <span class="keyword">using</span> dir C:\ProgramData\Tailscale</span><br><span class="line">linkChange: <span class="keyword">in</span> state NoState; PAC or proxyConfig changed; updating routes</span><br><span class="line">got LocalBackend <span class="keyword">in</span> <span class="number">1.632</span>s</span><br><span class="line"><span class="built_in">Start</span></span><br><span class="line">desktop<span class="literal">-sessions</span>: registered <span class="keyword">for</span> session notifications</span><br><span class="line">ipnext: active extensions: desktop<span class="literal">-sessions</span>, conn25, portlist, posture, clientupdate, relayserver, taildrop, auditlog</span><br><span class="line"><span class="built_in">Start</span>: serverMode=false</span><br><span class="line">auditlog: created</span><br><span class="line">auditlog: [<span class="type">unexpected</span>] failed to restore logs: empty key</span><br><span class="line">Backend: logs: be:d610ce8d98f125b70fd624b1f33f9d34cd1695c1d28675d6bcca7f2a666aff83 fe:</span><br><span class="line">health(warnable=wantrunning<span class="literal">-false</span>): error: Tailscale is stopped.</span><br><span class="line">pm: failed to migrate from legacy preferences: load legacy prefs: profile migration already completed</span><br><span class="line">client connected (THUN888<span class="literal">-LAPTOP</span>\thun888): disconnecting Tailscale</span><br><span class="line"><span class="built_in">Start</span></span><br><span class="line">control: client.Shutdown ...</span><br><span class="line">control: authRoutine: exiting</span><br><span class="line">auditlog: stopped <span class="keyword">for</span> profileID:</span><br><span class="line">control: updateRoutine: exiting</span><br><span class="line">control: mapRoutine: exiting</span><br><span class="line"><span class="built_in">Start</span>: serverMode=false</span><br><span class="line">control: Client.Shutdown done.</span><br><span class="line">auditlog: created</span><br><span class="line">auditlog: [<span class="type">unexpected</span>] failed to restore logs: empty key</span><br><span class="line">Backend: logs: be:................................................. fe:</span><br><span class="line">client disconnected (THUN888<span class="literal">-LAPTOP</span>\thun888): disconnecting Tailscale</span><br><span class="line"><span class="built_in">Start</span></span><br><span class="line">control: client.Shutdown ...</span><br><span class="line">auditlog: stopped <span class="keyword">for</span> profileID:</span><br><span class="line">control: mapRoutine: exiting</span><br><span class="line">control: authRoutine: exiting</span><br><span class="line">control: updateRoutine: exiting</span><br><span class="line">control: Client.Shutdown done.</span><br><span class="line"><span class="built_in">Start</span>: serverMode=false</span><br><span class="line">auditlog: created</span><br><span class="line">auditlog: [<span class="type">unexpected</span>] failed to restore logs: empty key</span><br><span class="line">Backend: logs: be:...................................................... fe:</span><br><span class="line">pm: failed to migrate from legacy preferences: load legacy prefs: profile migration already completed</span><br><span class="line">client connected (THUN888<span class="literal">-LAPTOP</span>\thun888): disconnecting Tailscale</span><br><span class="line"><span class="built_in">Start</span></span><br><span class="line">control: client.Shutdown ...</span><br><span class="line">auditlog: stopped <span class="keyword">for</span> profileID:</span><br><span class="line"><span class="built_in">Start</span>: serverMode=false</span><br><span class="line">control: updateRoutine: exiting</span><br><span class="line">control: mapRoutine: exiting</span><br><span class="line">auditlog: created</span><br><span class="line">auditlog: [<span class="type">unexpected</span>] failed to restore logs: empty key</span><br><span class="line">Backend: logs: be:..................................................... fe:</span><br><span class="line">control: authRoutine: exiting</span><br><span class="line">client disconnected (THUN888<span class="literal">-LAPTOP</span>\thun888): disconnecting Tailscale</span><br><span class="line">control: Client.Shutdown done.</span><br><span class="line">control: client.Shutdown ...</span><br><span class="line">auditlog: stopped <span class="keyword">for</span> profileID:</span><br><span class="line">control: authRoutine: exiting</span><br><span class="line"><span class="built_in">Start</span></span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;Start&quot;</span>)</span><br><span class="line">control: mapRoutine: exiting</span><br><span class="line">control: updateRoutine: exiting</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;[v\x00JSON]%d%s&quot;</span>)</span><br><span class="line"><span class="built_in">Start</span>: serverMode=false</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;Start: serverMode=%v&quot;</span>)</span><br><span class="line">control: Client.Shutdown done.</span><br><span class="line">auditlog: created</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;auditlog: created&quot;</span>)</span><br><span class="line">auditlog: [<span class="type">unexpected</span>] failed to restore logs: empty key</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;auditlog: [unexpected] failed to restore logs: %v&quot;</span>)</span><br><span class="line">Backend: logs: be:d610ce8d98f125b70fd624b1f33f9d34cd1695c1d28675d6bcca7f2a666aff83 fe:</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;Backend: logs: be:%v fe:%v&quot;</span>)</span><br><span class="line">pm: failed to migrate from legacy preferences: load legacy prefs: profile migration already completed</span><br><span class="line">client connected (THUN888<span class="literal">-LAPTOP</span>\thun888): disconnecting Tailscale</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;%s: disconnecting Tailscale&quot;</span>)</span><br><span class="line">control: client.Shutdown ...</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;control: client.Shutdown ...&quot;</span>)</span><br><span class="line">control: authRoutine: exiting</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;control: authRoutine: exiting&quot;</span>)</span><br><span class="line">control: updateRoutine: exiting</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;control: updateRoutine: exiting&quot;</span>)</span><br><span class="line">control: mapRoutine: exiting</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;control: mapRoutine: exiting&quot;</span>)</span><br><span class="line">auditlog: stopped <span class="keyword">for</span> profileID:</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;auditlog: stopped for profileID: %v&quot;</span>)</span><br><span class="line">control: Client.Shutdown done.</span><br><span class="line"><span class="function">[<span class="type">RATELIMIT</span>] <span class="title">format</span></span>(<span class="string">&quot;control: Client.Shutdown done.&quot;</span>)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>从这里可见密钥解密失败</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">store.New failed: failed to migrate existing TPM-sealed state file to plaintext format: newTPMStore(&quot;C:\\ProgramData\\Tailscale\\server-state.conf&quot;): failed to unseal state file: failed to unseal encryption key with TPM: tpm2.Load: TPM_RC_INTEGRITY (handle 0): integrity check failed; starting with in-memory store with a health warning</span><br></pre></td></tr></table></figure><p>密钥是使用TPM加密的，更换主板后集成的TPM模块发生变化，自然无法解密了</p><p>清除<code>C:\ProgramData\Tailscale\</code>目录后重启<code>tailscaled</code>服务，重新登录即可</p><h3 id="杂项"><a class="markdownIt-Anchor" href="#杂项"></a> 杂项</h3><p>其实问题还是挺多的，不过好在直接砸脸上，没有那么隐蔽。一开机就是Windows Hello失效，PIN、指纹和<span class="annotated" data-tippy-content="Passkeys">通行密钥</span>都要重新设置，工作账户（E5订阅）重新登录，系统也要激活，机哥的控制中心的各个功能要切换一下才能同步状态</p><p>而且我是双系统（Windows 11 + Ubuntu），并且用GRUB管理，所以需要设置Ubuntu为UEFI启动首选项</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:2048/1536;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213854-699fe24366b17.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213854-699fe24366b17.jpg?fmt=avif" alt="IMG_20260201_213854.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="IMG_20260201_213854.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213854-699fe24366b17.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">IMG_20260201_213854.jpg</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:2048/1536;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213857-69a04a4d22a99.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213857-69a04a4d22a99.jpg?fmt=avif" alt="IMG_20260201_213857.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="IMG_20260201_213857.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/IMG_20260201_213857-69a04a4d22a99.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">IMG_20260201_213857.jpg</span></div></div>    </div>    </div><p>同时由于MT7922的蓝牙在Ubuntu下有问题，我之前手动打了<a href="https://lkml.org/lkml/2024/3/15/325">这个补丁</a>后编译了内核，没有签名，还需要关闭安全启动，不然就会报错“无效的shim签名&amp;您需要先加载内核”</p><blockquote><p>更新：在开启安全启动时，它不会加载某些未经签名的驱动，如N卡驱动</p></blockquote><h2 id="最后"><a class="markdownIt-Anchor" href="#最后"></a> 最后</h2><p>电脑烧的时候倒不是很慌，<span class="annotated" data-tippy-content="耀中广场那有家黑店，张口就要2699😅">换主板和USB小板共1100</span>，找老师报销就完了。真正担心的是我数据，一堆代码都在本地，比如出事当天刚给组里写了两个模块都没推送。搞好上面的问题后火速装了syncthing同步到两台服务器上。</p><p>不过同步之前要配置“忽略模式”去掉依赖等可重新构建的文件，这里放一份让AI概括的版本：</p><figure class="highlight text"><figcaption><span>.stignore</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br></pre></td><td class="code"><pre><span class="line">// ------------------------------------------------</span><br><span class="line">// 1. 系统与元数据 (System &amp; Metadata)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// Windows</span><br><span class="line">Thumbs.db</span><br><span class="line">desktop.ini</span><br><span class="line">$RECYCLE.BIN</span><br><span class="line">System Volume Information</span><br><span class="line"></span><br><span class="line">// macOS</span><br><span class="line">.DS_Store</span><br><span class="line">.AppleDouble</span><br><span class="line">.LSOverride</span><br><span class="line">._*</span><br><span class="line">.Trashes</span><br><span class="line">.fseventsd</span><br><span class="line"></span><br><span class="line">// Synology / QNAP NAS</span><br><span class="line">@eaDir</span><br><span class="line">.nomedia</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 2. 版本控制 (Version Control)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 注意：Syncthing 同步 .git 目录容易导致仓库损坏，</span><br><span class="line">// 建议忽略 .git，只同步工作区文件。</span><br><span class="line">.git</span><br><span class="line">.gitignore</span><br><span class="line">.gitattributes</span><br><span class="line">.gitmodules</span><br><span class="line">.svn</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 3. 临时文件与日志 (Temp &amp; Logs)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">*.log</span><br><span class="line">*.tmp</span><br><span class="line">*.bak</span><br><span class="line">*.cache</span><br><span class="line">*.swp</span><br><span class="line">*.swo</span><br><span class="line">*~</span><br><span class="line">logs/</span><br><span class="line">tmp/</span><br><span class="line">temp/</span><br><span class="line">npm-debug.log*</span><br><span class="line">yarn-debug.log*</span><br><span class="line">yarn-error.log*</span><br><span class="line">pnpm-debug.log*</span><br><span class="line">.eslintcache</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 4. IDE 与 编辑器配置 (IDE / Editors)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 避免不同设备间的路径配置冲突</span><br><span class="line">.vscode/</span><br><span class="line">.idea/</span><br><span class="line">*.iml</span><br><span class="line">*.ipr</span><br><span class="line">*.iws</span><br><span class="line">.settings/</span><br><span class="line">.project</span><br><span class="line">.classpath</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 5. Node.js / 前端开发 (Node &amp; Frontend)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">**/node_modules/</span><br><span class="line">bower_components/</span><br><span class="line">jspm_packages/</span><br><span class="line">.npm</span><br><span class="line">.yarn</span><br><span class="line"></span><br><span class="line">// 构建产物 (Build Artifacts)</span><br><span class="line">**/dist/</span><br><span class="line">**/build/</span><br><span class="line">out/</span><br><span class="line">target/</span><br><span class="line">.output/</span><br><span class="line">.storybook-out/</span><br><span class="line"></span><br><span class="line">// 框架特定缓存</span><br><span class="line">.next/</span><br><span class="line">.nuxt/</span><br><span class="line">.vite/</span><br><span class="line">.cache-loader/</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 6. Python / Django</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">__pycache__/</span><br><span class="line">*.pyc</span><br><span class="line">*.pyo</span><br><span class="line">*.pyd</span><br><span class="line">venv/</span><br><span class="line">.venv/</span><br><span class="line">env/</span><br><span class="line">ENV/</span><br><span class="line">.pytest_cache/</span><br><span class="line">.coverage</span><br><span class="line">htmlcov/</span><br><span class="line">*.egg-info/</span><br><span class="line">.mypy_cache/</span><br><span class="line"></span><br><span class="line">// Django 静态文件与媒体 (根据需求调整)</span><br><span class="line">staticfiles/</span><br><span class="line">// media/  &lt;-- 媒体文件通常建议同步，如果不需要请取消注释</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 7. Java / JVM</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">*.class</span><br><span class="line">*.jar</span><br><span class="line">*.war</span><br><span class="line">*.ear</span><br><span class="line">.gradle/</span><br><span class="line">.mvn/</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 8. PHP / Laravel</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">vendor/</span><br><span class="line">storage/framework/cache/</span><br><span class="line">storage/framework/sessions/</span><br><span class="line">storage/framework/views/</span><br><span class="line">storage/logs/</span><br><span class="line">.phpunit.result.cache</span><br><span class="line"></span><br><span class="line">// ------------------------------------------------</span><br><span class="line">// 9. Docker &amp; 数据库 (Docker &amp; Databases)</span><br><span class="line">// ------------------------------------------------</span><br><span class="line">docker-data/</span><br><span class="line">docker-volumes/</span><br><span class="line">*.pid</span><br><span class="line">*.sock</span><br><span class="line">db.sqlite3</span><br><span class="line">*.db</span><br><span class="line">*.sqlitedb</span><br><span class="line"></span><br><span class="line">(?size &gt; 1024M)</span><br></pre></td></tr></table></figure><p>现在想想，果然事教人一教就会<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/sharkgoa/44.png"/></span><br />之前想着体积太大了不好像其他资料一样全量备份，数量太多也不好像照片一样增量备份到云服务商。用syncthing备份到自己服务器上就两全其美了</p><p>btw，机哥新开机动画挺好看的</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/解决一些笔记本电脑更换主板后的问题/VID_20260201_214221_computer_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>]]>
    </content>
    <id>https://blog.hzchu.top/2026/solve-laptop-motherboard-problems/</id>
    <link href="https://blog.hzchu.top/2026/solve-laptop-motherboard-problems/"/>
    <published>2026-02-26T21:18:30.000Z</published>
    <summary>安全是够安全了，如果能再方便一点就更好了</summary>
    <title>解决一些笔记本电脑更换主板后的问题</title>
    <updated>2026-02-26T21:43:52.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>thun888</name>
    </author>
    <category term="瞎折腾" scheme="https://blog.hzchu.top/categories/%E7%9E%8E%E6%8A%98%E8%85%BE/"/>
    <category term="网站" scheme="https://blog.hzchu.top/tags/%E7%BD%91%E7%AB%99/"/>
    <content>
      <![CDATA[<p>前天下午上传图片时发现服务器OpenResty崩了，手动启动报错<code>Illegal instruction</code></p><p>说实话我看到这报错还是挺懵的，看了一眼CPU型号发现是二代E5</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:645/183;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9ca5e2771.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9ca5e2771.png?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9ca5e2771.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div><p>明明之前是四代的，还偷偷降配置了？<br />旧E5不支持AVX2指令集，原先编译好的无法运行<br />发工单问了下，说我这套餐“不承诺CPU型号”，虽然很不爽，但又拿他没办法，事已至此先重新编译下吧</p><h2 id="命令"><a class="markdownIt-Anchor" href="#命令"></a> 命令</h2><p>安装是在内置的”应用“里安装的，需要找到安装时执行了什么命令才好复原。我用<a href="https://deepwiki.com/acepanel/panel">DeepWiki</a>索引了该面板的仓库，找到了相关的api接口，获取到路径后下载脚本（<code>dl.acepanel.net/nginx/install.sh</code>）</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1693/1047;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9e927ef9a.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9e927ef9a.png?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699f9e927ef9a.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div><p>找到安装脚本就好办了，重新执行以下命令即可</p><figure class="highlight bash"><figcaption><span>install.sh</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="built_in">cd</span> /www/server/nginx/src/ngx_brotli/deps/brotli</span><br><span class="line"><span class="built_in">rm</span> -r out</span><br><span class="line"><span class="built_in">mkdir</span> out &amp;&amp; <span class="built_in">cd</span> out</span><br><span class="line">cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS=<span class="string">&quot;-Ofast -march=native -mtune=native -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections&quot;</span> -DCMAKE_CXX_FLAGS=<span class="string">&quot;-Ofast -march=native -mtune=native -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections&quot;</span> -DCMAKE_INSTALL_PREFIX=./installed ..</span><br><span class="line">cmake --build . --config Release --target brotlienc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> /www/server/nginx/src/</span><br><span class="line">make clean</span><br><span class="line">./configure --user=www --group=www \</span><br><span class="line">    --prefix=/www/server/nginx \</span><br><span class="line">    --add-module=/www/server/nginx/src/ngx_cache_purge \</span><br><span class="line">    --add-module=/www/server/nginx/src/nginx-sticky-module \</span><br><span class="line">    --with-openssl=/www/server/nginx/src/openssl \</span><br><span class="line">    --with-openssl-opt=<span class="string">&quot;no-tests enable-ktls&quot;</span> \</span><br><span class="line">    --with-pcre=/www/server/nginx/src/pcre2 --with-pcre-jit \</span><br><span class="line">    --with-ld-opt=<span class="string">&quot;-Wl,-s -Wl,-Bsymbolic -Wl,--gc-sections&quot;</span> \</span><br><span class="line">    --with-cc-opt=<span class="string">&quot;-march=native -mtune=native -Ofast -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections&quot;</span> \</span><br><span class="line">    --with-file-aio \</span><br><span class="line">    --with-threads \</span><br><span class="line">    --with-compat \</span><br><span class="line">    --with-http_v2_module --with-http_v3_module \</span><br><span class="line">    --with-http_slice_module \</span><br><span class="line">    --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module \</span><br><span class="line">    --with-http_stub_status_module \</span><br><span class="line">    --with-http_ssl_module \</span><br><span class="line">    --with-http_image_filter_module \</span><br><span class="line">    --with-http_gzip_static_module --with-http_gunzip_module \</span><br><span class="line">    --with-http_sub_module \</span><br><span class="line">    --with-http_flv_module \</span><br><span class="line">    --with-http_addition_module \</span><br><span class="line">    --with-http_realip_module \</span><br><span class="line">    --with-http_mp4_module \</span><br><span class="line">    --with-http_auth_request_module \</span><br><span class="line">    --with-http_secure_link_module \</span><br><span class="line">    --with-http_random_index_module \</span><br><span class="line">    --with-http_dav_module \</span><br><span class="line">    --add-module=/www/server/nginx/src/nginx-dav-ext-module \</span><br><span class="line">    --add-module=/www/server/nginx/src/ngx_http_security_headers_module \</span><br><span class="line">    --add-module=/www/server/nginx/src/ngx_http_trim_filter_module \</span><br><span class="line">    --add-module=/www/server/nginx/src/ngx_brotli \</span><br><span class="line">    --add-module=/www/server/nginx/src/ngx_http_zstd_module</span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><blockquote><p>注：安装脚本里与面板程序的交互已经是新版本的了，完全不兼容旧版本，换句话说旧版本里应用商店用不了了。天天破坏性变更<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/Blob/blobcatcomftears.png"/></span></p></blockquote>]]>
    </content>
    <id>https://blog.hzchu.top/2026/haozi-openresty-build/</id>
    <link href="https://blog.hzchu.top/2026/haozi-openresty-build/"/>
    <published>2026-02-26T11:41:14.000Z</published>
    <summary>重新编译以在旧处理器上运行</summary>
    <title>手动编译耗子面板安装的OpenResty</title>
    <updated>2026-02-26T11:41:14.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>thun888</name>
    </author>
    <category term="瞎折腾" scheme="https://blog.hzchu.top/categories/%E7%9E%8E%E6%8A%98%E8%85%BE/"/>
    <category term="工具" scheme="https://blog.hzchu.top/tags/%E5%B7%A5%E5%85%B7/"/>
    <content>
      <![CDATA[<h2 id="sam3"><a class="markdownIt-Anchor" href="#sam3"></a> SAM3</h2><p><em>SAM 3 is a unified foundation model for promptable segmentation in images and videos. It can detect, segment, and track objects using text or visual prompts such as points, boxes, and masks. Compared to its predecessor <a href="https://github.com/facebookresearch/sam2">SAM 2</a>, SAM 3 introduces the ability to exhaustively segment all instances of an open-vocabulary concept specified by a short text phrase or exemplars. Unlike prior work, SAM 3 can handle a vastly larger set of open-vocabulary prompts. It achieves 75-80% of human performance on our new <a href="https://github.com/facebookresearch/sam3?tab=readme-ov-file#sa-co-dataset">SA-CO benchmark</a> which contains 270K unique concepts, over 50 times more than existing benchmarks.</em></p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">摘自官方仓库</span>          </div>  <p>简单来说，它是一个具备多模态理解能力的“通用分割模型”，能够理解用户的话并分割出指定内容</p><p>前几个月我用来对数据进行预处理及标注，虽然说大部分都标注不出来吧，但是能标出来的的标注框边界还是挺准的</p><p>后来在剪宣传片的时候需要动态打码就整了个小工具</p><h2 id="效果"><a class="markdownIt-Anchor" href="#效果"></a> 效果</h2><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <p>源视频：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/12.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 <code>face</code>：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_face_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 <code>person</code>：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_person_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 “<code>arm</code>”：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_arm_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 <code>woman</code>：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_woman_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 <code>man</code>：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_man_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    <div class="cell" style="">    <p>追踪 <code>a green shirt</code>：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]12_a_green_shirt_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div>    </div>    </div><p>密集人群效果：</p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]15_person_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div><p>多目标：<br />追踪 <code>a white bottle,screen</code></p><div class="tag-plugin video-player" style="max-width:100%;">  <video controls preload playsinline webkit-playsinline>  <source src="https://onedrive-storge.hzchu.top/FIles/使用SAM3模型对视频元素动态跟踪打码/[SHANA]14_a-white-bottle_screen_blurred.mp4" type="video/mp4">Your browser does not support the video tag.  </video>  </div><h2 id="使用"><a class="markdownIt-Anchor" href="#使用"></a> 使用</h2><h3 id="配置环境"><a class="markdownIt-Anchor" href="#配置环境"></a> 配置环境</h3><p>推荐Linux加N卡，Windows下缺依赖，A卡没试过</p><p>具体操作参考：<a href="https://stable-learn.com/zh/sam3-segment-anything-model-tutorial/">StableLearn</a>，<a href="https://github.com/facebookresearch/sam3">官方仓库</a></p><blockquote><p>注：</p><ol><li>50系显卡如果遇到问题可以参考<a href="https://github.com/Scarfy-sysu/rtx5060-pytorch-cuda129">Scarfy-sysu/rtx5060-pytorch-cuda129</a></li><li>模型需要申请访问权限，建议填海外的大学，中国的秒拒</li></ol></blockquote><h3 id="使用-2"><a class="markdownIt-Anchor" href="#使用-2"></a> 使用</h3><p>放置该程序到仓库根目录：<a href="https://gist.github.com/thun888/d4cc22b4be4481f9f12c21b9d41e2a7c">sam3_blur.py</a></p><p>运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 sam3_blur.py &#123;视频文件路径或图片文件夹&#125; --prompt <span class="string">&quot;&#123;提示词&#125;&quot;</span> [--batch_size &#123;单次处理帧数量&#125;] [--overlap] [--invert] [--confirm]</span><br></pre></td></tr></table></figure><blockquote><p>注：</p><ol><li>提示词用英文，多目标用<code>,</code>分隔</li><li><code>--overlap</code>为可选参数，附带该参数时每批次会重叠上一批次16帧（可能有改善效果吧）</li><li><code>--invert</code>为可选参数，附带该参数时会反转模糊区域</li><li><code>--confirm</code>为可选参数，附带该参数时会在第一轮时显示检测到对象个数，并询问用户是否继续</li></ol></blockquote><h3 id="可选"><a class="markdownIt-Anchor" href="#可选"></a> 可选</h3><ol><li>降精度以节省显存占用：</li></ol><figure class="highlight diff"><figcaption><span>sam3/model/io_utils.py:304</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">-   frames_np = np.stack(frames, axis=0).astype(np.float32)  # (T, H, W, C)</span></span><br><span class="line"><span class="addition">+   frames_np = np.stack(frames, axis=0).astype(np.float16)  # (T, H, W, C)</span></span><br></pre></td></tr></table></figure><ol start="2"><li>设置合理的<code>batch_size</code>：现在的实现方法是计算了指定数量的帧时重置记忆，会破坏帧间关系，导致模型识别可能跑偏，在显存足够的前提下越大越好</li></ol><h2 id="缺点"><a class="markdownIt-Anchor" href="#缺点"></a> 缺点</h2><p>当生产力还是不太够，我觉得至少有以下问题：</p><ol><li>输入提示词麻烦：有时候特征没办法简单地描述，或者是描述出来模型无法理解，需要给他输入视觉提示词（正向标记）会方便很多。但那样就要搞个UI来可视化输入，让用户自己开个“画图”找坐标点太麻烦了。小工具嘛能用就行</li><li>识别不正确：偶尔会跑偏，需要用负向标记，同样需要UI。或者设置个阈值过滤下置信度低的结果</li><li>多类型分割：原生不支持。目前是多次处理后合并到一起</li><li>性能要求高：即便目前实现了“预测部分就清空记忆”来缓解显存压力，在计算<a href="#%E6%95%88%E6%9E%9C">效果</a>里的原视频（1080P）时达到了17g+的显存占用（因批次大小，分辨率，检测对象数量等差异，实际情况不好说），普通人用不起（还是可以优化下的，不过学校服务器，力大砖飞<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/azukisan/038.png"/></span> ）</li><li>声音：没有拷贝音频，要需要的话用ffmpeg合成</li></ol><h2 id="更多"><a class="markdownIt-Anchor" href="#更多"></a> 更多</h2><p>现在<a href="https://docs.ultralytics.com/zh/models/yolo26/#yoloe-26-open-vocabulary-instance-segmentation">YOLOE26</a>也有“开放词汇实例分割”能力，有相似需求可以试试</p>]]>
    </content>
    <id>https://blog.hzchu.top/2026/sam3-blur-video/</id>
    <link href="https://blog.hzchu.top/2026/sam3-blur-video/"/>
    <published>2026-02-25T22:48:20.000Z</published>
    <summary>说句话就能打码了</summary>
    <title>使用SAM3模型对视频元素动态打码</title>
    <updated>2026-02-25T22:48:20.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>thun888</name>
    </author>
    <category term="瞎折腾" scheme="https://blog.hzchu.top/categories/%E7%9E%8E%E6%8A%98%E8%85%BE/"/>
    <category term="网站" scheme="https://blog.hzchu.top/tags/%E7%BD%91%E7%AB%99/"/>
    <content>
      <![CDATA[<h2 id="背景"><a class="markdownIt-Anchor" href="#背景"></a> 背景</h2><p>面试完了没事干，刷到学院推文想着水下学分<br />要求如下：</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:994/497;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bb8294cd1a.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bb8294cd1a.png?fmt=avif" alt="要求内容" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="要求内容" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bb8294cd1a.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">要求内容</span></div></div><p>说实话第一眼我还以为他要搞AR，想了下应该是用虚拟文化的元素来装饰网页就够了。<br />参考里提到了《罗小黑战记2》，之前看过动漫，但是更新太慢了后面就没看了，电影的话可能是因为之前那些事没有大力宣发，看到推文才知道，下载下来看看，确实不错<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/azukisan/017.png"/></span></p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">内容上</span>          </div>  <p>电影看完了，想想怎么实现比较好。往年的作品很多都是富媒体的，或者说是用大量的文字、图片、视频来客观的叙述一部作品，内部细分到每个人物等等。</p><p>不对，这么玩我剪素材得累死，而且单纯的堆积素材，我觉得没有多大意义。所以我决定以小黑的第一视角来展开叙述，同时限定时间线为电影一二部。</p><p>既然是第一视角，那就简单自我介绍加叙述经历，省事</p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">技术上</span>          </div>  <p>比赛是叫设计大赛，但我也不是学平面设计的，想专业做UI/UX肯定不行。干脆参考国外的一些设计回国内降维打击了（最明显的反差可能是南孚电池官网了吧）</p><p>考虑能不能炫点技，好歹是计院的比赛，有技术含量的好点</p><p>点击访问：</p><div class="tag-plugin link dis-select"><a class="link-card plain" title="" href="https://lxh.hzchu.top/" target="_blank" rel="external nofollow noopener noreferrer" cardlink data-api="https://siteinfo.api.hzchu.top/api/v1?url=https://lxh.hzchu.top/" autofill="title,icon,desc"><div class="left"><span class="title">https://lxh.hzchu.top/</span><span class="cap link footnote">https://lxh.hzchu.top/</span></div><div class="right"><div class="lazy img" data-bg="https://static.hzchu.top/cdn-x/placeholder@1.0.12/link/8f277b4ee0ecd.svg"></div></div></a></div><h2 id="设计"><a class="markdownIt-Anchor" href="#设计"></a> 设计</h2><p>事后诸葛亮嘛，直接<span class="annotated" data-tippy-content="可恶，在线预览没字体和动画">放我当时答辩ppt</span>吧</p>  <iframe src="https://view.officeapps.live.com/op/embed.aspx?src=https://onedrive-storge.hzchu.top/FIles/罗小黑/设计部分.pptx&wdAr=1.78" width="480px" height="270px" frameborder="0">这是嵌入 <a target="_blank" href="https://office.com">Microsoft Office</a> 演示文稿，由 <a target="_blank" href="https://office.com/webapps">Office</a> 提供支持。</iframe>  <p>简单来说就是全屏滚动+横向滚动+视频播放绑定</p><p>经历有删改，不然篇幅太长太累赘，配文就随便写点了，刻意模仿角色的性格来写，很难做到完全贴合，写多错多。而且人家就几岁能说啥长篇大论，有点<span class="annotated" data-tippy-content="Out Of Character：脱离角色">OOC</span>了</p><p>因为中途解决了个问题，改了下方向，导致经历页面的背景不太合适现在这样，不过p半天了懒得改</p><p>结尾不知道放什么，就放个符合氛围的MV收尾吧</p><h2 id="技术"><a class="markdownIt-Anchor" href="#技术"></a> 技术</h2><p>预览：</p><ol><li>使用<a href="https://www.live2d.com/zh-CHS/sdk/about/">Live2d</a>进行渲染人物模型</li><li>使用<a href="http://brm.io/matter-js/">Matter.js</a>模拟背景中“精灵”的移动</li><li>使用<a href="https://github.com/regosen/Gapless-5">Gapless-5</a>实现背景音乐无缝循环播放</li><li>使用<a href="https://gsap.com/">Gsap</a>提供良好的动画反馈及横向滚动效果</li><li>使用<a href="https://lenis.darkroom.engineering/">Lenis</a>提供平滑滚动</li></ol><h3 id="全屏滚动"><a class="markdownIt-Anchor" href="#全屏滚动"></a> 全屏滚动</h3><figure class="highlight html"><figcaption><span>FullPageScroll.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> <span class="title class_">Lenis</span> <span class="keyword">from</span> <span class="string">&#x27;lenis&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> gsap <span class="keyword">from</span> <span class="string">&#x27;gsap&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; <span class="title class_">ScrollTrigger</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;gsap/ScrollTrigger&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">gsap.<span class="title function_">registerPlugin</span>(<span class="title class_">ScrollTrigger</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> lenis = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onMounted</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 初始化 Lenis 平滑滚动</span></span></span><br><span class="line"><span class="language-javascript">  lenis = <span class="keyword">new</span> <span class="title class_">Lenis</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">duration</span>: <span class="number">1.2</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">easing</span>: <span class="function">(<span class="params">t</span>) =&gt;</span> <span class="title class_">Math</span>.<span class="title function_">min</span>(<span class="number">1</span>, <span class="number">1.001</span> - <span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, -<span class="number">10</span> * t)),</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">smoothWheel</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">smoothTouch</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 连接 Lenis 和 GSAP ScrollTrigger</span></span></span><br><span class="line"><span class="language-javascript">  lenis.<span class="title function_">on</span>(<span class="string">&#x27;scroll&#x27;</span>, <span class="title class_">ScrollTrigger</span>.<span class="property">update</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gsap.<span class="property">ticker</span>.<span class="title function_">add</span>(<span class="function">(<span class="params">time</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    lenis.<span class="title function_">raf</span>(time * <span class="number">1000</span>)</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gsap.<span class="property">ticker</span>.<span class="title function_">lagSmoothing</span>(<span class="number">0</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 获取所有 section</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> sections = gsap.<span class="property">utils</span>.<span class="title function_">toArray</span>(<span class="string">&#x27;.fp-section&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 为每个 section 设置 ScrollTrigger</span></span></span><br><span class="line"><span class="language-javascript">  sections.<span class="title function_">forEach</span>(<span class="function">(<span class="params">section</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="title class_">ScrollTrigger</span>.<span class="title function_">create</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">trigger</span>: section,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">start</span>: <span class="string">&#x27;top top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">end</span>: <span class="string">&#x27;bottom top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">snap</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">snapTo</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">duration</span>: &#123; <span class="attr">min</span>: <span class="number">0.2</span>, <span class="attr">max</span>: <span class="number">0.6</span> &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">ease</span>: <span class="string">&#x27;power2.inOut&#x27;</span></span></span><br><span class="line"><span class="language-javascript">      &#125;,</span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// markers: true</span></span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onBeforeUnmount</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 清理 Lenis</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (lenis) &#123;</span></span><br><span class="line"><span class="language-javascript">    lenis.<span class="title function_">destroy</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 清理所有 ScrollTrigger 实例</span></span></span><br><span class="line"><span class="language-javascript">  <span class="title class_">ScrollTrigger</span>.<span class="title function_">getAll</span>().<span class="title function_">forEach</span>(<span class="function"><span class="params">st</span> =&gt;</span> st.<span class="title function_">kill</span>())</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gsap.<span class="property">ticker</span>.<span class="title function_">remove</span>(<span class="function">(<span class="params">time</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (lenis) lenis.<span class="title function_">raf</span>(time * <span class="number">1000</span>)</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;fp-container&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">slot</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.fp-container</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 全局样式，确保每个 section 占满视口 */</span></span></span><br><span class="line"><span class="language-css">:<span class="built_in">deep</span>(.fp-section) &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100vh</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">justify-content</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: relative;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>调用时：</p><figure class="highlight html"><figcaption><span>HomePage.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">......</span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">FullPageScroll</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">section</span> <span class="attr">class</span>=<span class="string">&quot;fp-section&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">FirstCard</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">SecondCard</span> <span class="attr">:slides</span>=<span class="string">&quot;secondCardSlides&quot;</span> <span class="attr">section-id</span>=<span class="string">&quot;second-card-1&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">section</span> <span class="attr">class</span>=<span class="string">&quot;fp-section&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">ThirdCard</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">SecondCard</span> <span class="attr">:slides</span>=<span class="string">&quot;FourthCardSlides&quot;</span> <span class="attr">section-id</span>=<span class="string">&quot;second-card-2&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">FifthCard</span> <span class="attr">:slides</span>=<span class="string">&quot;FifthCardSlides&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">SixthCard</span> <span class="attr">:slides</span>=<span class="string">&quot;SixthCardSlides&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">FullPageScroll</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>对于需要实现全屏吸附的页面，需用<code>&lt;section class=&quot;fp-section&quot;&gt;&lt;/section&gt;</code>包裹</p><h3 id="首页"><a class="markdownIt-Anchor" href="#首页"></a> 首页</h3><h4 id="背景-2"><a class="markdownIt-Anchor" href="#背景-2"></a> 背景</h4><p>起初背景打算用一个视频解决，后来觉得视频首末帧衔接太突兀就打算实现前端实时渲染</p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">分析</span>          </div>  <p>“灵”，或者说“<span class="annotated" data-tippy-content="TV动画十三集：生灵会自己汇聚，形成没有实体的精灵，精灵与物质灵结合，可以演变成妖精。">精灵</span>”，表现为<u>球状，能自运动，速度不一，会受阻力影响变为椭球状，不受重力影响，可融合/分裂</u></p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1112/469;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd41a0deba.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd41a0deba.png?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd41a0deba.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/1080;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd500a2aa0.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd500a2aa0.png?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699bd500a2aa0.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div>    </div>    </div><p>投影到平面上时呈<u>发光圆环状，内部半透明，外发光效果</u></p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">实操</span>          </div>  <p>不考虑其他形态，简单实现样式：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ctx.<span class="property">fillStyle</span> = <span class="string">&#x27;rgba(254, 254, 252, 0.6)&#x27;</span></span><br><span class="line">ctx.<span class="property">strokeStyle</span> = <span class="string">&#x27;rgba(255, 255, 255)&#x27;</span></span><br><span class="line">ctx.<span class="property">lineWidth</span> = <span class="number">1</span></span><br><span class="line">ctx.<span class="property">shadowColor</span> = <span class="string">&#x27;#b8d0bd&#x27;</span></span><br><span class="line">ctx.<span class="property">shadowBlur</span> = <span class="number">10</span></span><br></pre></td></tr></table></figure><p>使用<a href="https://www.brm.io/matter-js/">Matter.js</a>进行2D物理模拟</p><figure class="highlight html"><figcaption><span>FirstCard.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; ref, onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> <span class="title class_">Matter</span> <span class="keyword">from</span> <span class="string">&#x27;matter-js&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> live2dInitialized = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> cellAnimation = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> cells = [] <span class="comment">// 存储精灵数据供 Live2D 追踪</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> targetCellIndex = <span class="literal">null</span> <span class="comment">// 当前追踪的精灵索引</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> eyeTrackingInterval = <span class="literal">null</span> <span class="comment">// 眼神追踪定时器</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> idleTimer = <span class="literal">null</span> <span class="comment">// 空闲计时器</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> isTracking = <span class="literal">false</span> <span class="comment">// 是否正在追踪</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 初始化精灵动画</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">initCellAnimation</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;cell-canvas&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  canvas.<span class="property">width</span> = <span class="variable language_">window</span>.<span class="property">innerWidth</span></span></span><br><span class="line"><span class="language-javascript">  canvas.<span class="property">height</span> = <span class="variable language_">window</span>.<span class="property">innerHeight</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">&#x27;2d&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> &#123; <span class="title class_">Engine</span>, <span class="title class_">World</span>, <span class="title class_">Bodies</span>, <span class="title class_">Body</span>, <span class="title class_">Vector</span> &#125; = <span class="title class_">Matter</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 创建引擎并关闭重力</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> engine = <span class="title class_">Engine</span>.<span class="title function_">create</span>()</span></span><br><span class="line"><span class="language-javascript">  engine.<span class="property">gravity</span>.<span class="property">scale</span> = <span class="number">0</span></span></span><br><span class="line"><span class="language-javascript">  engine.<span class="property">gravity</span>.<span class="property">x</span> = <span class="number">0</span></span></span><br><span class="line"><span class="language-javascript">  engine.<span class="property">gravity</span>.<span class="property">y</span> = <span class="number">0</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> world = engine.<span class="property">world</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 默认值</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">let</span> numCells = <span class="number">15</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">let</span> speedModifier = <span class="number">0</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 请求天气数据</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">try</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://generate-cloud-image.hzchu.top/v1/image?format=json&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> data = <span class="keyword">await</span> response.<span class="title function_">json</span>()</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> weatherCode = data.<span class="property">weather_code</span> || <span class="number">0</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`天气代码: <span class="subst">$&#123;weatherCode&#125;</span>`</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 根据天气代码调整参数</span></span></span><br><span class="line"><span class="language-javascript">    numCells = <span class="title class_">Math</span>.<span class="title function_">max</span>(<span class="number">0</span>, <span class="number">15</span> - weatherCode)</span></span><br><span class="line"><span class="language-javascript">    speedModifier = <span class="number">0.03</span> * weatherCode</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`精灵数量: <span class="subst">$&#123;numCells&#125;</span>, 速度修正: +<span class="subst">$&#123;speedModifier.toFixed(<span class="number">3</span>)&#125;</span>`</span>)</span></span><br><span class="line"><span class="language-javascript">  &#125; <span class="keyword">catch</span> (error) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">&#x27;天气数据获取失败，使用默认值:&#x27;</span>, error)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  cells = [] <span class="comment">// 使用全局变量</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> radius = <span class="number">10</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 初始化精灵</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; numCells; i++) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> body = <span class="title class_">Bodies</span>.<span class="title function_">circle</span>(</span></span><br><span class="line"><span class="language-javascript">      <span class="title class_">Math</span>.<span class="title function_">random</span>() * canvas.<span class="property">width</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="title class_">Math</span>.<span class="title function_">random</span>() * canvas.<span class="property">height</span>,</span></span><br><span class="line"><span class="language-javascript">      radius,</span></span><br><span class="line"><span class="language-javascript">      &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">frictionAir</span>: <span class="number">0</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">friction</span>: <span class="number">0</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">restitution</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">      &#125;</span></span><br><span class="line"><span class="language-javascript">    )</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> speed = <span class="number">0.001</span> + <span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">0.2</span> + speedModifier</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> angle = <span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="title class_">Math</span>.<span class="property">PI</span> * <span class="number">2</span></span></span><br><span class="line"><span class="language-javascript">    <span class="title class_">Body</span>.<span class="title function_">setVelocity</span>(body, &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">x</span>: <span class="title class_">Math</span>.<span class="title function_">cos</span>(angle) * speed,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">y</span>: <span class="title class_">Math</span>.<span class="title function_">sin</span>(angle) * speed</span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript">    <span class="title class_">World</span>.<span class="title function_">add</span>(world, body)</span></span><br><span class="line"><span class="language-javascript">    cells.<span class="title function_">push</span>(&#123; body, speed, angle &#125;)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 更新运动</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">updateMovement</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    cells.<span class="title function_">forEach</span>(<span class="function"><span class="params">cellData</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> cell = cellData.<span class="property">body</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// 保持恒定速度</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> currentSpeed = <span class="title class_">Vector</span>.<span class="title function_">magnitude</span>(cell.<span class="property">velocity</span>)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (currentSpeed &lt; cellData.<span class="property">speed</span> * <span class="number">0.9</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">const</span> normalizedVel = <span class="title class_">Vector</span>.<span class="title function_">normalise</span>(cell.<span class="property">velocity</span>)</span></span><br><span class="line"><span class="language-javascript">        <span class="title class_">Body</span>.<span class="title function_">setVelocity</span>(cell, &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="attr">x</span>: normalizedVel.<span class="property">x</span> * cellData.<span class="property">speed</span>,</span></span><br><span class="line"><span class="language-javascript">          <span class="attr">y</span>: normalizedVel.<span class="property">y</span> * cellData.<span class="property">speed</span></span></span><br><span class="line"><span class="language-javascript">        &#125;)</span></span><br><span class="line"><span class="language-javascript">      &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// Wrap-around 边界穿越</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (cell.<span class="property">position</span>.<span class="property">x</span> &lt; -radius) <span class="title class_">Body</span>.<span class="title function_">setPosition</span>(cell, &#123;<span class="attr">x</span>: canvas.<span class="property">width</span> + radius, <span class="attr">y</span>: cell.<span class="property">position</span>.<span class="property">y</span>&#125;)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (cell.<span class="property">position</span>.<span class="property">x</span> &gt; canvas.<span class="property">width</span> + radius) <span class="title class_">Body</span>.<span class="title function_">setPosition</span>(cell, &#123;<span class="attr">x</span>: -radius, <span class="attr">y</span>: cell.<span class="property">position</span>.<span class="property">y</span>&#125;)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (cell.<span class="property">position</span>.<span class="property">y</span> &lt; -radius) <span class="title class_">Body</span>.<span class="title function_">setPosition</span>(cell, &#123;<span class="attr">x</span>: cell.<span class="property">position</span>.<span class="property">x</span>, <span class="attr">y</span>: canvas.<span class="property">height</span> + radius&#125;)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (cell.<span class="property">position</span>.<span class="property">y</span> &gt; canvas.<span class="property">height</span> + radius) <span class="title class_">Body</span>.<span class="title function_">setPosition</span>(cell, &#123;<span class="attr">x</span>: cell.<span class="property">position</span>.<span class="property">x</span>, <span class="attr">y</span>: -radius&#125;)</span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 绘制</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">render</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    ctx.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, canvas.<span class="property">width</span>, canvas.<span class="property">height</span>)</span></span><br><span class="line"><span class="language-javascript">    cells.<span class="title function_">forEach</span>(<span class="function"><span class="params">cellData</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> cell = cellData.<span class="property">body</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> vx = cell.<span class="property">velocity</span>.<span class="property">x</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> vy = cell.<span class="property">velocity</span>.<span class="property">y</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> speed = <span class="title class_">Math</span>.<span class="title function_">sqrt</span>(vx*vx + vy*vy)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> stretch = <span class="number">1</span> + <span class="title class_">Math</span>.<span class="title function_">min</span>(speed / <span class="number">5</span>, <span class="number">0.3</span>)</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> angle = <span class="title class_">Math</span>.<span class="title function_">atan2</span>(vy, vx)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">save</span>()</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">translate</span>(cell.<span class="property">position</span>.<span class="property">x</span>, cell.<span class="property">position</span>.<span class="property">y</span>)</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">rotate</span>(angle)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">beginPath</span>()</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">ellipse</span>(<span class="number">0</span>, <span class="number">0</span>, radius * stretch, radius / stretch, <span class="number">0</span>, <span class="number">0</span>, <span class="title class_">Math</span>.<span class="property">PI</span> * <span class="number">2</span>)</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="property">fillStyle</span> = <span class="string">&#x27;rgba(254, 254, 252, 0.6)&#x27;</span></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="property">strokeStyle</span> = <span class="string">&#x27;rgba(255, 255, 255)&#x27;</span></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="property">lineWidth</span> = <span class="number">1</span></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="property">shadowColor</span> = <span class="string">&#x27;#b8d0bd&#x27;</span></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="property">shadowBlur</span> = <span class="number">10</span></span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">fill</span>()</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">stroke</span>()</span></span><br><span class="line"><span class="language-javascript">      ctx.<span class="title function_">restore</span>()</span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 主循环</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">update</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">updateMovement</span>()</span></span><br><span class="line"><span class="language-javascript">    <span class="title class_">Engine</span>.<span class="title function_">update</span>(engine, <span class="number">1000</span> / <span class="number">60</span>)</span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">render</span>()</span></span><br><span class="line"><span class="language-javascript">    cellAnimation = <span class="title function_">requestAnimationFrame</span>(update)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="title function_">update</span>()</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 窗口大小改变时更新 canvas</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">handleResize</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    canvas.<span class="property">width</span> = <span class="variable language_">window</span>.<span class="property">innerWidth</span></span></span><br><span class="line"><span class="language-javascript">    canvas.<span class="property">height</span> = <span class="variable language_">window</span>.<span class="property">innerHeight</span></span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;resize&#x27;</span>, handleResize)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">return</span> <span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;resize&#x27;</span>, handleResize)</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (cellAnimation) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="title function_">cancelAnimationFrame</span>(cellAnimation)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>同时补充了个设定：天气恶劣时精灵数量减少，运动速度加快，符合逻辑一点。天气信息获取参考<a href="https://blog.hzchu.top/wiki/more/%E7%BD%91%E7%AB%99%E6%9B%B4%E6%96%B0/%E4%BE%A7%E6%A0%8F%E8%83%8C%E6%99%AF%E5%9B%BE/weatherapi/">使用小米天气接口获取天气信息</a></p><figure class="highlight html"><figcaption><span>FirstCard.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;video-background&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">img</span></span></span><br><span class="line"><span class="tag">        <span class="attr">v-webp</span></span></span><br><span class="line"><span class="tag">        <span class="attr">src</span>=<span class="string">&quot;/assets/background.png&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">alt</span>=<span class="string">&quot;背景&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">class</span>=<span class="string">&quot;background-image&quot;</span></span></span><br><span class="line"><span class="tag">      /&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 精灵动画层 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">&quot;cell-canvas&quot;</span> <span class="attr">class</span>=<span class="string">&quot;cell-canvas&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 遮罩层,增强内容可读性 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;video-overlay&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure><p>样式</p><figure class="highlight html"><figcaption><span>FirstCard.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 视频背景容器 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.video-background</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">overflow</span>: hidden;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 背景图片 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.background-image</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">min-width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">min-height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: auto;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: auto;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>, -<span class="number">50%</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">object-fit</span>: cover;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 精灵动画层 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.cell-canvas</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">1</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">pointer-events</span>: none;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 视频遮罩层 - 增强文字可读性 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.video-overlay</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.90</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 深色模式下的遮罩 */</span></span></span><br><span class="line"><span class="language-css">:<span class="built_in">global</span>(.dark) .video-overlay &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.85</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>PS：其实写的时候考虑过鸟群算法，后来发现效果不太好就去掉了</p></blockquote><h4 id="live2d"><a class="markdownIt-Anchor" href="#live2d"></a> Live2D</h4><p>起初是想加点互动性，在考虑到实际情况后用了Live2D，模型来自<a href="https://space.bilibili.com/33372405">盒装现烤奕潞</a>和<a href="https://space.bilibili.com/11407406">躺师傅轻食小炒</a>，感谢</p><p>这部分还是搞了挺久，首先模型是经过<a href="https://www.live2d.com/zh-CHS/showcase/title/live2dviewerex/">Live2DViewerEX</a>打包后的lpk文件，加密后无法直接使用，解密后模型是Live2D 3.0 版本，网上大多数都是2.0为基础的。找了好久找到<a href="https://github.com/LSTM-Kirigaya/Live2dRender">LSTM-Kirigaya/Live2dRender</a>，但是不太符合我的实际需求就二次开发了一些功能</p><p>具体就不展开讲了，前几天<a href="https://www.zzzzhi.com/#/">土土</a>推荐了个<a href="https://github.com/guansss/pixi-live2d-display">guansss/pixi-live2d-display</a>，好家伙又白造轮子了<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/Blob/blobcatcomftears.png"/></span><br />效果可以参考<a href="https://blog.xyy.world/">学游渊的博客</a></p><blockquote><p>LPK解密相关：尽管网上确实能找到相关资料，为了保护版权，这里不展开叙述</p></blockquote><p>有个小彩蛋：长时间无动作时人物会盯着页面里的“精灵”看。计算坐标有点麻烦，我简单实现了下，没过多琢磨</p><h4 id="背景音乐"><a class="markdownIt-Anchor" href="#背景音乐"></a> 背景音乐</h4><p>同样考虑到衔接问题，使用了<a href="https://github.com/regosen/Gapless-5?tab=readme-ov-file#1-demos">regosen/Gapless-5</a>来实现无缝播放音频，同时使用<span class="annotated" data-tippy-content="Adobe Audition">AU</span>处理音频得到可循环音乐片段</p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">处理音频</span>          </div>  <p>具体操作参考：</p><div class="tag-plugin ds-biliinfo" data-api="https://biliinfo.api.hzchu.top/api/v1/get_video_info?bvid=BV15J411k7LW""></div><p>首先要选一个纯音乐，有人声的话不太好，我这里选的是<a href="https://www.bilibili.com/video/BV1fE411h7EU/">嘿咻狂想曲</a>前半段</p><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1185/898;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beedd6abec.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beedd6abec.png?fmt=avif" alt="原始音频" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="原始音频" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beedd6abec.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">原始音频</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:2002/598;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beee46e8d1.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beee46e8d1.png?fmt=avif" alt="处理后的最小重复片段" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="处理后的最小重复片段" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699beee46e8d1.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">处理后的最小重复片段</span></div></div>    </div>    </div>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">调用</span>          </div>  <p>注意与其他播放事件联动，避免一起播放影响听感</p><figure class="highlight html"><figcaption><span>GlobalMusicPlayer.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; ref, onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; <span class="title class_">Gapless5</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@regosen/gapless-5&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> isPlaying = <span class="title function_">ref</span>(<span class="literal">false</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> gapless = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> wasPlayingBeforeLivePhoto = <span class="literal">false</span> <span class="comment">// 记录实况照片播放前的状态</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> wasPlayingBeforeDPlayer = <span class="literal">false</span> <span class="comment">// 记录 DPlayer 播放前的状态</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 状态图片</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> imgIdle = <span class="string">&#x27;https://emoticons.hzchu.top/emoticons/luo-xiao-hei/10.png&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> imgPlaying = <span class="string">&#x27;https://emoticons.hzchu.top/emoticons/shen-tan-luo-xiao-hei/4.png&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 播放/暂停控制</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">togglePlay</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (!gapless) <span class="keyword">return</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (isPlaying.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    gapless.<span class="title function_">pause</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    gapless.<span class="title function_">play</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onMounted</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 初始化 Gapless 5</span></span></span><br><span class="line"><span class="language-javascript">  gapless = <span class="keyword">new</span> <span class="title class_">Gapless5</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">tracks</span>: [<span class="string">&#x27;/assets/sound/background-single.wav&#x27;</span>],</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">loop</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">loadLimit</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">useHTML5Audio</span>: <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 监听播放状态</span></span></span><br><span class="line"><span class="language-javascript">  gapless.<span class="property">onplayerload</span> = <span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Gapless 5 加载完成&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gapless.<span class="property">onplay</span> = <span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    isPlaying.<span class="property">value</span> = <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gapless.<span class="property">onpause</span> = <span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    isPlaying.<span class="property">value</span> = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  gapless.<span class="property">onerror</span> = <span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;Gapless 5 播放错误:&#x27;</span>, error)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 监听 DPlayer 播放/暂停事件</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">onDPlayerPlay</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (gapless &amp;&amp; isPlaying.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      wasPlayingBeforeDPlayer = <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">      gapless.<span class="title function_">pause</span>()</span></span><br><span class="line"><span class="language-javascript">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;DPlayer 播放，暂停背景音乐&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">onDPlayerPause</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (gapless &amp;&amp; !isPlaying.<span class="property">value</span> &amp;&amp; wasPlayingBeforeDPlayer) &#123;</span></span><br><span class="line"><span class="language-javascript">      wasPlayingBeforeDPlayer = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">      gapless.<span class="title function_">play</span>()</span></span><br><span class="line"><span class="language-javascript">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;DPlayer 暂停，恢复背景音乐&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;dplayer-play&#x27;</span>, onDPlayerPlay)</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;dplayer-pause&#x27;</span>, onDPlayerPause)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 监听 LivePhoto 播放/暂停事件</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">onLivePhotoPlay</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (gapless &amp;&amp; isPlaying.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      wasPlayingBeforeLivePhoto = <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">      gapless.<span class="title function_">pause</span>()</span></span><br><span class="line"><span class="language-javascript">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;实况照片播放，暂停背景音乐&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> <span class="title function_">onLivePhotoPause</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (gapless &amp;&amp; !isPlaying.<span class="property">value</span> &amp;&amp; wasPlayingBeforeLivePhoto) &#123;</span></span><br><span class="line"><span class="language-javascript">      wasPlayingBeforeLivePhoto = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">      gapless.<span class="title function_">play</span>()</span></span><br><span class="line"><span class="language-javascript">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;实况照片结束，恢复背景音乐&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;livephoto-play&#x27;</span>, onLivePhotoPlay)</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;livephoto-pause&#x27;</span>, onLivePhotoPause)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="title function_">onBeforeUnmount</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;dplayer-play&#x27;</span>, onDPlayerPlay)</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;dplayer-pause&#x27;</span>, onDPlayerPause)</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;livephoto-play&#x27;</span>, onLivePhotoPlay)</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;livephoto-pause&#x27;</span>, onLivePhotoPause)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 清理 Gapless 5</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (gapless) &#123;</span></span><br><span class="line"><span class="language-javascript">      gapless.<span class="title function_">stop</span>()</span></span><br><span class="line"><span class="language-javascript">      gapless = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;global-music-player&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">&quot;music-btn&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;togglePlay&quot;</span> <span class="attr">:title</span>=<span class="string">&quot;isPlaying ? &#x27;暂停背景音乐&#x27; : &#x27;播放背景音乐&#x27;&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">img</span> <span class="attr">:src</span>=<span class="string">&quot;isPlaying ? imgPlaying : imgIdle&quot;</span> <span class="attr">alt</span>=<span class="string">&quot;音乐状态&quot;</span> <span class="attr">class</span>=<span class="string">&quot;music-status&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.global-music-player</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: fixed;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">bottom</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">100</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: flex-end;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.music-btn</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: none;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border</span>: none;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">padding</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">cursor</span>: pointer;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">outline</span>: none;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.music-status</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">2.5rem</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: auto;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: block;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="顶栏"><a class="markdownIt-Anchor" href="#顶栏"></a> 顶栏</h4><p>小彩蛋：滚动页面时icon同步滚动，且变成另一个状态</p>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">处理素材</span>          </div>  <ol><li>使用PS抠出主体</li><li>使用<span class="annotated" data-tippy-content="Adobe illustrator">AI</span>的图像临摹<span class="annotated" data-tippy-content="好像没啥用">转换为svg</span></li></ol><div class="tag-plugin grid"  style="grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));"><div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:640/640;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_21-41-24-699bfc2f400f7.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_21-41-24-699bfc2f400f7.jpg?fmt=avif" alt="原图" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="原图" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_21-41-24-699bfc2f400f7.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">原图</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:637/428;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-15_23-15-31-699c0f0d50835.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-15_23-15-31-699c0f0d50835.jpg?fmt=avif" alt="PS大法" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="PS大法" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-15_23-15-31-699c0f0d50835.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">PS大法</span></div></div>    </div>    <div class="cell" style="">    <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:2560/1600;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_23-22-18-699bfc864e5b5.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_23-22-18-699bfc864e5b5.jpg?fmt=avif" alt="图像描摹" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="图像描摹" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-04_23-22-18-699bfc864e5b5.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">图像描摹</span></div></div>    </div>    </div>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">调用</span>          </div>  <figure class="highlight html"><figcaption><span>HeaderToolBar.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; ref, onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> logoSrc = <span class="title function_">ref</span>(<span class="string">&#x27;/assets/logo-256.svg&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> logoRotation = <span class="title function_">ref</span>(<span class="number">0</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> isMobileMenuOpen = <span class="title function_">ref</span>(<span class="literal">false</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> scrollTimeout = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> isScrolling = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">handleScroll</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> currentScrollY = <span class="variable language_">window</span>.<span class="property">scrollY</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 滚动开始时切换为奔跑 logo</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (currentScrollY &gt; <span class="number">50</span> &amp;&amp; !isScrolling) &#123;</span></span><br><span class="line"><span class="language-javascript">    isScrolling = <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">    logoSrc.<span class="property">value</span> = <span class="string">&#x27;/assets/logo-run.svg&#x27;</span></span></span><br><span class="line"><span class="language-javascript">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentScrollY &lt;= <span class="number">50</span> &amp;&amp; !isScrolling) &#123;</span></span><br><span class="line"><span class="language-javascript">    logoSrc.<span class="property">value</span> = <span class="string">&#x27;/assets/logo-256.svg&#x27;</span></span></span><br><span class="line"><span class="language-javascript">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentScrollY &gt; <span class="number">50</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    logoSrc.<span class="property">value</span> = <span class="string">&#x27;/assets/logo-run.svg&#x27;</span></span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 根据滚动距离计算旋转角度</span></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 每滚动 300px 旋转 360 度</span></span></span><br><span class="line"><span class="language-javascript">  logoRotation.<span class="property">value</span> = (currentScrollY / <span class="number">300</span>) * <span class="number">360</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 清除之前的定时器</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (scrollTimeout) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="built_in">clearTimeout</span>(scrollTimeout)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 停止滚动 500ms 后切换回静态 logo</span></span></span><br><span class="line"><span class="language-javascript">  scrollTimeout = <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    isScrolling = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">scrollY</span> &gt; <span class="number">50</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      logoSrc.<span class="property">value</span> = <span class="string">&#x27;/assets/logo-256.svg&#x27;</span></span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;, <span class="number">500</span>)</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 打开移动端菜单</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">openMobileMenu</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  isMobileMenuOpen.<span class="property">value</span> = <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 关闭移动端菜单</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">closeMobileMenu</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  isMobileMenuOpen.<span class="property">value</span> = <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onMounted</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;scroll&#x27;</span>, handleScroll, &#123; <span class="attr">passive</span>: <span class="literal">true</span> &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onBeforeUnmount</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;scroll&#x27;</span>, handleScroll)</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (scrollTimeout) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="built_in">clearTimeout</span>(scrollTimeout)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="故事页"><a class="markdownIt-Anchor" href="#故事页"></a> 故事页</h3><p>这部分耗时最久是排查“滚动页面时视频联动卡顿，要等运动完全静止才加载的出来”的问题</p><p>最后在youtube评论里找到了答案</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1416/115;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c0cab9dbf0.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c0cab9dbf0.png?fmt=avif" alt="感谢大佬救急" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="感谢大佬救急" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c0cab9dbf0.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">感谢大佬救急</span></div></div><p>想想也是，视频压缩算法中，非关键帧（预测帧）只记录画面中变化的部分，导致在读取该帧时需要依赖上一个关键帧到该预测帧间的所有消息。可能变化速度超过了解码速度或者是浏览器出于性能考虑优化了吧</p><div class="tag-plugin colorful folders" ><details class="folder" index="0"><summary><p>注</p></summary><div class="body"><p>在 H.264/H.265 等压缩编码中，视频帧分为三种类型：</p><ul><li><strong>I帧（关键帧/Intra-frame）：</strong> 它是<strong>唯一包含完整画面信息</strong>的帧。你可以把它看作一张完整的 .jpg 图片。</li><li><strong>P帧（预测帧/Predicted frame）：</strong> 它<strong>不包含</strong>完整的画面，只记录了“与前一帧相比变化了哪里”。</li><li><strong>B帧（双向预测帧/Bi-directional predicted frame）：</strong> 记录“与前一帧和后一帧相比的变化”。因此严谨来说可能还需要后面的帧</li></ul></div></details></div>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">处理素材</span>          </div>  <ol><li>黑边</li></ol><p>不知道为什么，下载的第二部有几个像素高的黑边，第一部更是大黑框，使用<code>ffmpeg</code>裁剪</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg <span class="literal">-ss</span> <span class="number">00</span>:<span class="number">01</span>:<span class="number">00</span> <span class="literal">-to</span> <span class="number">00</span>:<span class="number">05</span>:<span class="number">00</span> <span class="literal">-i</span> <span class="string">&quot;罗小黑战记.mkv&quot;</span> `</span><br><span class="line"><span class="literal">-vf</span> <span class="string">&quot;crop=in_w:in_h-276:0:138&quot;</span> `</span><br><span class="line"><span class="literal">-c</span>:v h264_nvenc <span class="literal">-preset</span> slow <span class="literal">-rc</span> vbr <span class="literal">-cq</span> <span class="number">18</span> <span class="literal">-b</span>:v <span class="number">5</span>M `</span><br><span class="line"><span class="literal">-c</span>:a aac <span class="literal">-b</span>:a <span class="number">192</span>k <span class="string">&quot;罗小黑战记_1-5min_h264_cuda.mp4&quot;</span></span><br><span class="line"></span><br><span class="line">ffmpeg <span class="literal">-ss</span> <span class="number">00</span>:<span class="number">10</span>:<span class="number">00</span> <span class="literal">-to</span> <span class="number">00</span>:<span class="number">20</span>:<span class="number">00</span> <span class="literal">-i</span> <span class="string">&quot;罗小黑战记2.mkv&quot;</span> `</span><br><span class="line"><span class="literal">-vf</span> <span class="string">&quot;crop=in_w:in_h-12:0:12&quot;</span> `</span><br><span class="line"><span class="literal">-c</span>:v h264_nvenc <span class="literal">-preset</span> slow <span class="literal">-rc</span> vbr <span class="literal">-cq</span> <span class="number">18</span> <span class="literal">-b</span>:v <span class="number">5</span>M `</span><br><span class="line"><span class="literal">-c</span>:a aac <span class="literal">-b</span>:a <span class="number">192</span>k <span class="string">&quot;罗小黑战记2_10-20_h264_cuda.mp4&quot;</span></span><br></pre></td></tr></table></figure><blockquote><p>1-5分钟，使用N卡加速<br />mkv转为mp4，不然导不进pr</p></blockquote><ol start="2"><li>剪辑&amp;导出</li></ol><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:713/863;width:200px;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c11f99df04.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c11f99df04.png?fmt=avif" alt="导出时设置" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="导出时设置" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c11f99df04.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">导出时设置</span></div></div><blockquote><p>注：由于我在解决卡顿问题前用的是序列帧（空间占用太恐怖了），为了沿用之前的剪辑成果，我使用pr导出序列帧再使用<code>ffmpeg</code>合成</p></blockquote><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:731/1184;width:200px;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-23_16-36-54-699c12a09dd31.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-23_16-36-54-699c12a09dd31.jpg?fmt=avif" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2026-02-23_16-36-54-699c12a09dd31.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div></div><blockquote><p>导出时可压低分辨率，再高的分辨率缩放后也没用</p></blockquote><p><code>ffmpeg</code>命令：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg <span class="literal">-hwaccel</span> cuda <span class="literal">-framerate</span> <span class="number">5</span> <span class="literal">-i</span> <span class="string">&quot;%d.jpg&quot;</span> <span class="literal">-c</span>:v h264_nvenc <span class="literal">-bf</span> <span class="number">0</span> <span class="literal">-g</span> <span class="number">2</span> <span class="literal">-forced-idr</span> <span class="number">1</span> <span class="literal">-pix_fmt</span> yuv420p output.mp4</span><br></pre></td></tr></table></figure><blockquote><p>将此文件夹下所有jpg图片按照顺序拼接<br /><code>-g 2</code> 关键帧距离为2（解决报错：Gop Length should be greater than number of B frames + 1）<br /><code>-forced-idr 1</code> 使用<a href="https://www.cnblogs.com/yinxiangpei/articles/3889865.html">IDR帧</a>（Instantaneous Decoding Refresh，即时解码刷新帧）<br /><code>-bf 0</code> 禁用 B 帧（双向预测帧）<br />使几乎每一帧都接近关键帧</p></blockquote>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">调用</span>          </div>  <p>使用<a href="https://gsap.com/docs/v3/Plugins/ScrollTrigger/">GSAP ScrollTrigger</a>实现横向滚动</p><figure class="highlight html"><figcaption><span>SecondCard.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">/**</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 横向滚动卡片组件</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 使用 GSAP ScrollTrigger 实现垂直滚动驱动横向内容移动</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 支持视频随滚动进度播放</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * <span class="doctag">@example</span></span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * &lt;SecondCard :slides=&quot;slidesData&quot; /&gt;</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * slidesData 格式:</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * [</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *   &#123;</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *     id: 1,                    // 唯一标识</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *     title: &#x27;标题&#x27;,            // 可选，标题文字</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *     description: &#x27;描述&#x27;,      // 可选，描述文字</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *     image: &#x27;/path/to/bg.jpg&#x27;, // 背景图片路径</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *     video: &#x27;/path/to/video.mp4&#x27; // 视频路径</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> *   &#125;</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * ]</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> */</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; ref, onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> gsap <span class="keyword">from</span> <span class="string">&#x27;gsap&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; <span class="title class_">ScrollTrigger</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;gsap/ScrollTrigger&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">gsap.<span class="title function_">registerPlugin</span>(<span class="title class_">ScrollTrigger</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 定义 props</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> props = <span class="title function_">defineProps</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="attr">slides</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">type</span>: <span class="title class_">Array</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">required</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">validator</span>: <span class="function">(<span class="params">value</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">return</span> value.<span class="title function_">every</span>(<span class="function"><span class="params">slide</span> =&gt;</span></span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">id</span> !== <span class="literal">undefined</span> &amp;&amp;</span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">image</span> !== <span class="literal">undefined</span> &amp;&amp;</span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">video</span> !== <span class="literal">undefined</span></span></span><br><span class="line"><span class="language-javascript">      )</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;,</span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 添加唯一标识符，用于区分多个实例</span></span></span><br><span class="line"><span class="language-javascript">  <span class="attr">sectionId</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">type</span>: <span class="title class_">String</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">default</span>: <span class="function">() =&gt;</span> <span class="string">`section-<span class="subst">$&#123;<span class="built_in">Math</span>.random().toString(<span class="number">36</span>).substr(<span class="number">2</span>, <span class="number">9</span>)&#125;</span>`</span></span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> currentSlide = <span class="title function_">ref</span>(<span class="number">0</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> videoRefs = <span class="title function_">ref</span>(&#123;&#125;)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> wrapperElement = <span class="title function_">ref</span>(<span class="literal">null</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> cardsboxElement = <span class="title function_">ref</span>(<span class="literal">null</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> scrollTriggerInstance = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> resizeObserver = <span class="literal">null</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> distance = <span class="number">0</span><span class="comment">// 计算横向滚动距离</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">calculateDistance</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (cardsboxElement.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    distance = cardsboxElement.<span class="property">value</span>.<span class="property">scrollWidth</span> - <span class="variable language_">window</span>.<span class="property">innerWidth</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (wrapperElement.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      wrapperElement.<span class="property">value</span>.<span class="property">style</span>.<span class="property">height</span> = <span class="string">`<span class="subst">$&#123;distance + <span class="variable language_">window</span>.innerHeight&#125;</span>px`</span></span></span><br><span class="line"><span class="language-javascript">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;横向滚动距离:&#x27;</span>, distance, <span class="string">&#x27;wrapper高度:&#x27;</span>, wrapperElement.<span class="property">value</span>.<span class="property">style</span>.<span class="property">height</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 更新当前 slide 和视频进度</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">updateSlideAndVideos</span> = (<span class="params">progress</span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> slideCount = props.<span class="property">slides</span>.<span class="property">length</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> slideIndex = <span class="title class_">Math</span>.<span class="title function_">floor</span>(progress * slideCount)</span></span><br><span class="line"><span class="language-javascript">  currentSlide.<span class="property">value</span> = <span class="title class_">Math</span>.<span class="title function_">min</span>(slideIndex, slideCount - <span class="number">1</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 为每个slide更新视频进度</span></span></span><br><span class="line"><span class="language-javascript">  props.<span class="property">slides</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">slide, index</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> slideProgress = (progress * slideCount) - index</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> clampedProgress = <span class="title class_">Math</span>.<span class="title function_">max</span>(<span class="number">0</span>, <span class="title class_">Math</span>.<span class="title function_">min</span>(<span class="number">1</span>, slideProgress))</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 获取对应的视频元素并更新播放进度</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> videoElement = videoRefs.<span class="property">value</span>[slide.<span class="property">id</span>]</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (videoElement &amp;&amp; videoElement.<span class="property">duration</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> targetTime = clampedProgress * videoElement.<span class="property">duration</span></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// 只有当时间差异较大时才更新，避免频繁操作</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">if</span> (<span class="title class_">Math</span>.<span class="title function_">abs</span>(videoElement.<span class="property">currentTime</span> - targetTime) &gt; <span class="number">0.1</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        videoElement.<span class="property">currentTime</span> = targetTime</span></span><br><span class="line"><span class="language-javascript">      &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 点击指示器跳转</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">goToSlide</span> = (<span class="params">index</span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (scrollTriggerInstance) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> targetProgress = index / props.<span class="property">slides</span>.<span class="property">length</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> scrollPosition = scrollTriggerInstance.<span class="property">start</span> + (scrollTriggerInstance.<span class="property">end</span> - scrollTriggerInstance.<span class="property">start</span>) * targetProgress</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">top</span>: scrollPosition,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">behavior</span>: <span class="string">&#x27;smooth&#x27;</span></span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 初始化 ScrollTrigger</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">initScrollTrigger</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (!wrapperElement.<span class="property">value</span> || !cardsboxElement.<span class="property">value</span>) <span class="keyword">return</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="title function_">calculateDistance</span>()</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 创建横向移动动画</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> animation = gsap.<span class="title function_">to</span>(cardsboxElement.<span class="property">value</span>, &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">x</span>: <span class="function">() =&gt;</span> -distance,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">ease</span>: <span class="string">&#x27;none&#x27;</span></span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  scrollTriggerInstance = <span class="title class_">ScrollTrigger</span>.<span class="title function_">create</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">trigger</span>: wrapperElement.<span class="property">value</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">start</span>: <span class="string">&#x27;top top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">end</span>: <span class="string">&#x27;bottom bottom&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">animation</span>: animation,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">scrub</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">id</span>: props.<span class="property">sectionId</span>, <span class="comment">// 添加唯一 ID</span></span></span><br><span class="line"><span class="language-javascript">    <span class="attr">onUpdate</span>: <span class="function">(<span class="params">self</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="title function_">updateSlideAndVideos</span>(self.<span class="property">progress</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">onRefresh</span>: <span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="title function_">calculateDistance</span>() <span class="comment">// 刷新时重新计算距离</span></span></span><br><span class="line"><span class="language-javascript">    &#125;,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">invalidateOnRefresh</span>: <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 处理窗口大小变化</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">handleResize</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (scrollTriggerInstance) &#123;</span></span><br><span class="line"><span class="language-javascript">    scrollTriggerInstance.<span class="title function_">kill</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="title function_">initScrollTrigger</span>()</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onMounted</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="comment">// 延迟初始化，确保 DOM 已渲染</span></span></span><br><span class="line"><span class="language-javascript">  <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;初始化 ScrollTrigger...&#x27;</span>, &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">wrapper</span>: wrapperElement.<span class="property">value</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">cardsbox</span>: cardsboxElement.<span class="property">value</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">scrollWidth</span>: cardsboxElement.<span class="property">value</span>?.<span class="property">scrollWidth</span>,</span></span><br><span class="line"><span class="language-javascript">      <span class="attr">innerWidth</span>: <span class="variable language_">window</span>.<span class="property">innerWidth</span></span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">initScrollTrigger</span>()</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 监听窗口大小变化</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;resize&#x27;</span>, handleResize)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 使用 ResizeObserver 监听容器大小变化</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (cardsboxElement.<span class="property">value</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      resizeObserver = <span class="keyword">new</span> <span class="title class_">ResizeObserver</span>(handleResize)</span></span><br><span class="line"><span class="language-javascript">      resizeObserver.<span class="title function_">observe</span>(cardsboxElement.<span class="property">value</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;, <span class="number">100</span>)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onBeforeUnmount</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (scrollTriggerInstance) &#123;</span></span><br><span class="line"><span class="language-javascript">    scrollTriggerInstance.<span class="title function_">kill</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">&#x27;resize&#x27;</span>, handleResize)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (resizeObserver) &#123;</span></span><br><span class="line"><span class="language-javascript">    resizeObserver.<span class="title function_">disconnect</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">ref</span>=<span class="string">&quot;wrapperElement&quot;</span> <span class="attr">class</span>=<span class="string">&quot;horizontal-scroll-wrapper&quot;</span> <span class="attr">:data-section-id</span>=<span class="string">&quot;props.sectionId&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;horizontal-scroll-container&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">ref</span>=<span class="string">&quot;cardsboxElement&quot;</span> <span class="attr">class</span>=<span class="string">&quot;cardsbox&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 每个滑块 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-for</span>=<span class="string">&quot;slide in props.slides&quot;</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:key</span>=<span class="string">&quot;slide.id&quot;</span></span></span><br><span class="line"><span class="tag">          <span class="attr">class</span>=<span class="string">&quot;slide-item&quot;</span></span></span><br><span class="line"><span class="tag">        &gt;</span></span><br><span class="line">          <span class="comment">&lt;!-- 图片背景 --&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;slide-background&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">img</span></span></span><br><span class="line"><span class="tag">              <span class="attr">v-webp</span></span></span><br><span class="line"><span class="tag">              <span class="attr">:src</span>=<span class="string">&quot;slide.image&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">:alt</span>=<span class="string">&quot;slide.title&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">class</span>=<span class="string">&quot;background-image&quot;</span></span></span><br><span class="line"><span class="tag">            /&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;background-overlay&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">          <span class="comment">&lt;!-- 内容层 --&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;slide-content&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 左侧文字 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;w-1/2 px-6 flex items-center&quot;</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;dialog-box&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">h1</span> <span class="attr">v-if</span>=<span class="string">&quot;slide.title&quot;</span> <span class="attr">class</span>=<span class="string">&quot;mb-4 text-4xl font-bold leading-tight md:text-5xl lg:text-6xl text-neutral-700 dark:text-neutral-200&quot;</span> <span class="attr">v-html</span>=<span class="string">&quot;slide.title&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">&quot;slide.description&quot;</span> <span class="attr">class</span>=<span class="string">&quot;text-lg md:text-xl text-neutral-600 dark:text-neutral-300 leading-relaxed&quot;</span> <span class="attr">v-html</span>=<span class="string">&quot;slide.description&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">&lt;!-- 右侧视频 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;w-1/2 h-full flex items-center justify-center p-6&quot;</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;<span class="name">video</span></span></span><br><span class="line"><span class="tag">                <span class="attr">:ref</span>=<span class="string">&quot;el =&gt; &#123; if (el) videoRefs[slide.id] = el &#125;&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">:src</span>=<span class="string">&quot;slide.video&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">class</span>=<span class="string">&quot;max-w-full max-h-full object-contain rounded-lg shadow-2xl&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">muted</span></span></span><br><span class="line"><span class="tag">                <span class="attr">playsinline</span></span></span><br><span class="line"><span class="tag">                <span class="attr">preload</span>=<span class="string">&quot;auto&quot;</span></span></span><br><span class="line"><span class="tag">              /&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 滚动指示器 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;scroll-indicator&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">        <span class="attr">v-for</span>=<span class="string">&quot;(slide, index) in props.slides&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:key</span>=<span class="string">&quot;slide.id&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">class</span>=<span class="string">&quot;indicator-dot&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:class</span>=<span class="string">&quot;&#123; active: currentSlide === index &#125;&quot;</span></span></span><br><span class="line"><span class="tag">        @<span class="attr">click</span>=<span class="string">&quot;goToSlide(index)&quot;</span></span></span><br><span class="line"><span class="tag">      &gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 滚动提示 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;scroll-hint&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;text-sm text-neutral-500 dark:text-neutral-400&quot;</span>&gt;</span>滚动查看更多<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 横向滚动包装器 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.horizontal-scroll-wrapper</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: relative;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 横向滚动容器 - sticky 定位 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.horizontal-scroll-container</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: sticky;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">justify-content</span>: flex-start;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100vh</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">overflow</span>: hidden;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: transparent;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 卡片盒子 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.cardsbox</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">will-change</span>: transform;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 每个滑块 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.slide-item</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">flex-shrink</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100vw</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100vh</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">justify-content</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: relative;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 滑块背景 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.slide-background</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">overflow</span>: hidden;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.background-image</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">object-fit</span>: cover;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">object-position</span>: center;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.background-overlay</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">top</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.80</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 深色模式下的遮罩 */</span></span></span><br><span class="line"><span class="language-css">:<span class="built_in">global</span>(.dark) .background-overlay &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.80</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 内容层 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.slide-content</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: relative;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">1</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">justify-content</span>: center;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 对话框样式 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.dialog-box</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: relative;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border-radius</span>: <span class="number">20px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">padding</span>: <span class="number">2rem</span> <span class="number">2.5rem</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">10px</span> <span class="number">40px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.15</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border</span>: <span class="number">2px</span> solid <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.8</span>);</span></span><br><span class="line"><span class="language-css">  backdrop-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">10px</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">max-width</span>: <span class="number">600px</span>;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 深色模式下的对话框 */</span></span></span><br><span class="line"><span class="language-css">:<span class="built_in">global</span>(.dark) .dialog-box &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">30</span>, <span class="number">30</span>, <span class="number">30</span>, <span class="number">0.95</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border-color</span>: <span class="built_in">rgba</span>(<span class="number">60</span>, <span class="number">60</span>, <span class="number">60</span>, <span class="number">0.8</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 滚动指示器 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.scroll-indicator</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">bottom</span>: <span class="number">2rem</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">transform</span>: <span class="built_in">translateX</span>(-<span class="number">50%</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">gap</span>: <span class="number">0.5rem</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">10</span>;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.indicator-dot</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">8px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">height</span>: <span class="number">8px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(<span class="number">163</span>, <span class="number">163</span>, <span class="number">163</span>, <span class="number">0.5</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">cursor</span>: pointer;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.indicator-dot</span><span class="selector-class">.active</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">width</span>: <span class="number">24px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">border-radius</span>: <span class="number">4px</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(<span class="number">82</span>, <span class="number">82</span>, <span class="number">82</span>, <span class="number">0.9</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">:<span class="built_in">global</span>(.dark) .indicator-dot &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(<span class="number">163</span>, <span class="number">163</span>, <span class="number">163</span>, <span class="number">0.3</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">:<span class="built_in">global</span>(.dark) .indicator-dot.active &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(<span class="number">229</span>, <span class="number">229</span>, <span class="number">229</span>, <span class="number">0.9</span>);</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 滚动提示 */</span></span></span><br><span class="line"><span class="language-css"><span class="selector-class">.scroll-hint</span> &#123;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">position</span>: absolute;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">bottom</span>: <span class="number">4rem</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">left</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">transform</span>: <span class="built_in">translateX</span>(-<span class="number">50%</span>);</span></span><br><span class="line"><span class="language-css">  <span class="attribute">z-index</span>: <span class="number">10</span>;</span></span><br><span class="line"><span class="language-css">  <span class="attribute">animation</span>: fadeInOut <span class="number">3s</span> ease-in-out infinite;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="keyword">@keyframes</span> fadeInOut &#123;</span></span><br><span class="line"><span class="language-css">  <span class="number">0%</span>, <span class="number">100%</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">opacity</span>: <span class="number">0.3</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css">  <span class="number">50%</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">opacity</span>: <span class="number">1</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="comment">/* 移动端优化 */</span></span></span><br><span class="line"><span class="language-css"><span class="keyword">@media</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) &#123;</span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.slide-item</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.slide-content</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">flex-direction</span>: column;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.slide-content</span> &gt; <span class="selector-tag">div</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">width</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">max-height</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.dialog-box</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">padding</span>: <span class="number">1.5rem</span>;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">font-size</span>: <span class="number">0.9rem</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.scroll-hint</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">bottom</span>: <span class="number">3rem</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css">  <span class="selector-class">.scroll-indicator</span> &#123;</span></span><br><span class="line"><span class="language-css">    <span class="attribute">bottom</span>: <span class="number">1.5rem</span>;</span></span><br><span class="line"><span class="language-css">  &#125;</span></span><br><span class="line"><span class="language-css">&#125;</span></span><br><span class="line"><span class="language-css"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="相册页"><a class="markdownIt-Anchor" href="#相册页"></a> 相册页</h3>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">调用</span>          </div>  <p>之前在<a href="https://blog.twofei.com/1603/">测试不基于 LivePhotoKit JS 实现实况照片 - 陪她去流浪</a>看到了个简单的实况图片示例，我借来用一下<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/Blob/blobcatbox.png"/></span></p><p>具体实现逻辑我整理好了一个独立的库，vue版本也就多了个播放时与背景音乐联动，全屏查看（移动端），后续更新到库里</p><div class="tag-plugin ghcard"><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/thun888/LivePhoto"><img src="https://github-readme-stats.hzchu.top/api/pin/?username=thun888&repo=LivePhoto&&show_owner=true"/></a></div>    <div class="divider-container">      <div class="divider-line"></div>      <span class="divider-text">整理素材</span>          </div>  <ol><li>简单挡一下片头字幕，不然有点出戏</li></ol><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1243/562;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c1ca7d24c3.png?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c1ca7d24c3.png?fmt=avif" alt="image.png" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="image.png" href="https://onep.hzchu.top/mount/pic/myself/2026/02/image-699c1ca7d24c3.png"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">image.png</span></div></div><ol start="2"><li>对部分片段手动调整了音量增益</li></ol><h3 id="对比页"><a class="markdownIt-Anchor" href="#对比页"></a> 对比页</h3><p>沿用上面的视频滚动绑定逻辑，加入了文字渐变<br />主要是CSS的<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Reference/Values/gradient/linear-gradient">linear-gradient</a>函数实现</p><figure class="highlight html"><figcaption><span>FifthCard.vue</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; ref, onMounted, onBeforeUnmount &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> gsap <span class="keyword">from</span> <span class="string">&#x27;gsap&#x27;</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">import</span> &#123; <span class="title class_">ScrollTrigger</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;gsap/ScrollTrigger&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">gsap.<span class="title function_">registerPlugin</span>(<span class="title class_">ScrollTrigger</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> props = <span class="title function_">defineProps</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="attr">slides</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">type</span>: <span class="title class_">Array</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">required</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="attr">validator</span>: <span class="function">(<span class="params">value</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">return</span> value.<span class="title function_">every</span>(<span class="function"><span class="params">slide</span> =&gt;</span></span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">id</span> !== <span class="literal">undefined</span> &amp;&amp;</span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">title</span> !== <span class="literal">undefined</span> &amp;&amp;</span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">description</span> !== <span class="literal">undefined</span> &amp;&amp;</span></span><br><span class="line"><span class="language-javascript">        slide.<span class="property">video</span> !== <span class="literal">undefined</span></span></span><br><span class="line"><span class="language-javascript">      )</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> wrapperElement = <span class="title function_">ref</span>(<span class="literal">null</span>)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> videoRefs = <span class="title function_">ref</span>(&#123;&#125;)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> textRefs = <span class="title function_">ref</span>(&#123;&#125;)</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">let</span> scrollTriggers = []</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 为每个 slide 创建 ScrollTrigger</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">const</span> <span class="title function_">initScrollTriggers</span> = (<span class="params"></span>) =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">if</span> (!wrapperElement.<span class="property">value</span>) <span class="keyword">return</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> slides = props.<span class="property">slides</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">const</span> slideCount = slides.<span class="property">length</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  slides.<span class="title function_">forEach</span>(<span class="function">(<span class="params">slide, index</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> slideElement = wrapperElement.<span class="property">value</span>.<span class="title function_">querySelector</span>(<span class="string">`[data-slide-id=&quot;<span class="subst">$&#123;slide.id&#125;</span>&quot;]`</span>)</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (!slideElement) <span class="keyword">return</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 创建视频播放动画 - 视频中部到达底端时开始播放</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> videoElement = videoRefs.<span class="property">value</span>[slide.<span class="property">id</span>]</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (videoElement) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> videoTrigger = <span class="title class_">ScrollTrigger</span>.<span class="title function_">create</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">trigger</span>: slideElement,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">start</span>: <span class="string">&#x27;center bottom&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">end</span>: <span class="string">&#x27;bottom top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">scrub</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">onUpdate</span>: <span class="function">(<span class="params">self</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="comment">// 更新视频进度</span></span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">if</span> (videoElement.<span class="property">duration</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> targetTime = self.<span class="property">progress</span> * videoElement.<span class="property">duration</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (<span class="title class_">Math</span>.<span class="title function_">abs</span>(videoElement.<span class="property">currentTime</span> - targetTime) &gt; <span class="number">0.1</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">              videoElement.<span class="property">currentTime</span> = targetTime</span></span><br><span class="line"><span class="language-javascript">            &#125;</span></span><br><span class="line"><span class="language-javascript">          &#125;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">      &#125;)</span></span><br><span class="line"><span class="language-javascript">      scrollTriggers.<span class="title function_">push</span>(videoTrigger)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 创建文字渐变动画（基于页面滚动进度）</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> textElement = textRefs.<span class="property">value</span>[slide.<span class="property">id</span>]</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (textElement) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> textTrigger = <span class="title class_">ScrollTrigger</span>.<span class="title function_">create</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">trigger</span>: slideElement,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">start</span>: <span class="string">&#x27;top bottom&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">end</span>: <span class="string">&#x27;bottom top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">scrub</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">onUpdate</span>: <span class="function">(<span class="params">self</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="comment">// 根据滚动进度计算渐变</span></span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">const</span> progress = self.<span class="property">progress</span> * <span class="number">100</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">          <span class="comment">// 检测深色模式</span></span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">const</span> isDark = <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="property">classList</span>.<span class="title function_">contains</span>(<span class="string">&#x27;dark&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">          <span class="comment">// 深色模式：浅色 → 深色，浅色模式：深色 → 浅色</span></span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">const</span> color1 = isDark ? <span class="string">&#x27;#9ca7bb&#x27;</span> : <span class="string">&#x27;#2d3e4f&#x27;</span></span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">const</span> color2 = isDark ? <span class="string">&#x27;#2d3e4f&#x27;</span> : <span class="string">&#x27;#9ca7bb&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">          textElement.<span class="property">style</span>.<span class="property">background</span> = <span class="string">`linear-gradient(to right,</span></span></span><br><span class="line"><span class="string"><span class="language-javascript">            <span class="subst">$&#123;color1&#125;</span> 0%,</span></span></span><br><span class="line"><span class="string"><span class="language-javascript">            <span class="subst">$&#123;color1&#125;</span> <span class="subst">$&#123;progress&#125;</span>%,</span></span></span><br><span class="line"><span class="string"><span class="language-javascript">            <span class="subst">$&#123;color2&#125;</span> <span class="subst">$&#123;progress&#125;</span>%,</span></span></span><br><span class="line"><span class="string"><span class="language-javascript">            <span class="subst">$&#123;color2&#125;</span> 100%)`</span></span></span><br><span class="line"><span class="language-javascript">          textElement.<span class="property">style</span>.<span class="property">webkitBackgroundClip</span> = <span class="string">&#x27;text&#x27;</span></span></span><br><span class="line"><span class="language-javascript">          textElement.<span class="property">style</span>.<span class="property">webkitTextFillColor</span> = <span class="string">&#x27;transparent&#x27;</span></span></span><br><span class="line"><span class="language-javascript">          textElement.<span class="property">style</span>.<span class="property">backgroundClip</span> = <span class="string">&#x27;text&#x27;</span></span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">      &#125;)</span></span><br><span class="line"><span class="language-javascript">      scrollTriggers.<span class="title function_">push</span>(textTrigger)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 如果是最后一个 slide，添加淡出效果</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (index === slideCount - <span class="number">1</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">const</span> fadeOutTrigger = <span class="title class_">ScrollTrigger</span>.<span class="title function_">create</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">trigger</span>: slideElement,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">start</span>: <span class="string">&#x27;center top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">end</span>: <span class="string">&#x27;bottom top&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">scrub</span>: <span class="number">1</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">onUpdate</span>: <span class="function">(<span class="params">self</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">          slideElement.<span class="property">style</span>.<span class="property">opacity</span> = <span class="number">1</span> - self.<span class="property">progress</span></span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">      &#125;)</span></span><br><span class="line"><span class="language-javascript">      scrollTriggers.<span class="title function_">push</span>(fadeOutTrigger)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onMounted</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">initScrollTriggers</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;, <span class="number">100</span>)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">onBeforeUnmount</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">  scrollTriggers.<span class="title function_">forEach</span>(<span class="function"><span class="params">st</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (st) st.<span class="title function_">kill</span>()</span></span><br><span class="line"><span class="language-javascript">  &#125;)</span></span><br><span class="line"><span class="language-javascript">&#125;)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="视频页"><a class="markdownIt-Anchor" href="#视频页"></a> 视频页</h3><p>用<a href="https://github.com/DIYgod/DPlayer">DPlayer</a>好看一点，同时和背景音乐联动</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> dp = <span class="keyword">new</span> <span class="title class_">DPlayer</span>(&#123;</span><br><span class="line">  <span class="attr">container</span>: container,</span><br><span class="line">  <span class="attr">video</span>: &#123;</span><br><span class="line">    <span class="attr">url</span>: slide.<span class="property">video</span>,</span><br><span class="line">    <span class="attr">type</span>: <span class="string">&#x27;auto&#x27;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">autoplay</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="attr">loop</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="attr">preload</span>: <span class="string">&#x27;auto&#x27;</span>,</span><br><span class="line">  <span class="attr">volume</span>: <span class="number">0.7</span>,</span><br><span class="line">  <span class="attr">mutex</span>: <span class="literal">true</span>, <span class="comment">// 互斥，阻止多个播放器同时播放</span></span><br><span class="line">  <span class="attr">theme</span>: <span class="string">&#x27;#3b82f6&#x27;</span>,</span><br><span class="line">  <span class="attr">lang</span>: <span class="string">&#x27;zh-cn&#x27;</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">dplayers.<span class="title function_">push</span>(dp)</span><br><span class="line"></span><br><span class="line">dp.<span class="title function_">on</span>(<span class="string">&#x27;play&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">window</span>.<span class="title function_">dispatchEvent</span>(<span class="keyword">new</span> <span class="title class_">CustomEvent</span>(<span class="string">&#x27;dplayer-play&#x27;</span>))</span><br><span class="line">&#125;)</span><br><span class="line">dp.<span class="title function_">on</span>(<span class="string">&#x27;pause&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">window</span>.<span class="title function_">dispatchEvent</span>(<span class="keyword">new</span> <span class="title class_">CustomEvent</span>(<span class="string">&#x27;dplayer-pause&#x27;</span>))</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="杂项"><a class="markdownIt-Anchor" href="#杂项"></a> 杂项</h3><h4 id="鼠标指针"><a class="markdownIt-Anchor" href="#鼠标指针"></a> 鼠标指针</h4><p>使用由<a href="https://space.bilibili.com/1013625945">漓翎_cub</a>制作的鼠标指针，并使用<a href="https://www.axialis.com/cursorworkshop/">Axialis CursorWorkshop</a>调整大小</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1693/1008;"><img class="lazy" src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-05_11-00-20-699c217c4bb6d.jpg?fmt=avif" data-src="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-05_11-00-20-699c217c4bb6d.jpg?fmt=avif" alt="Snipaste_2025-11-05_11-00-20.jpg" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24' path fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><a class="image-download blur" style="opacity:0" target="_blank" download="Snipaste_2025-11-05_11-00-20.jpg" href="https://onep.hzchu.top/mount/pic/myself/2026/02/Snipaste_2025-11-05_11-00-20-699c217c4bb6d.jpg"><svg class="icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3734"><path d="M561.00682908 685.55838913a111.03077546 111.03077546 0 0 1-106.8895062 0L256.23182837 487.72885783a55.96309219 55.96309219 0 0 1 79.13181253-79.18777574L450.70357448 523.88101491V181.55477937a55.96309219 55.96309219 0 0 1 111.92618438 0v344.06109173l117.07478902-117.07478901a55.96309219 55.96309219 0 0 1 79.13181252 79.18777574zM282.81429711 797.1487951h447.70473912a55.96309219 55.96309219 0 0 1 0 111.92618438H282.81429711a55.96309219 55.96309219 0 0 1 0-111.92618438z" p-id="3735"></path></svg></a></div><div class="image-meta"><span class="image-caption center">Snipaste_2025-11-05_11-00-20.jpg</span></div></div><h4 id="字体"><a class="markdownIt-Anchor" href="#字体"></a> 字体</h4><p>使用<a href="https://github.com/lxgw/kose-font">小赖字体</a>，并使用<a href="https://pypi.org/project/fonttools/">fonttools</a>压缩</p><blockquote><p>安装fonttools：<code>pip install fonttools</code></p></blockquote><ol><li>计算子集：全选页面内容后进行<a href="https://toolbox.hzchu.top/html/%E5%AD%97%E7%AC%A6%E5%8E%BB%E9%87%8D/index.html">字符去重</a>，将结果保存到一个txt文件</li><li>根据子集提取：<code>fonttools subset &quot;.\Xiaolai-Regular.ttf&quot; --text-file=&quot;.\words.txt&quot; --output-file=&quot;.\Xiaolai-Regular1_subset.ttf&quot;</code></li><li>压缩为woff2格式：<code>fonttools ttLib.woff2 compress &quot;.\Xiaolai-Regular_subset.ttf&quot; -o .\Xiaolai-Regular.woff2</code></li></ol><h2 id="搁置"><a class="markdownIt-Anchor" href="#搁置"></a> 搁置</h2><p>搁置了部分功能没有实现：</p><ol><li>视觉跟踪：上文提到的“长时间不活动自动盯这精灵看”，本来计划调用摄像头实现视觉追踪，考虑到实际运行的软硬件环境就没有做</li><li>图片转换：整体开发轻功能性，且与主题联系不大</li></ol><div class="tag-plugin ds-biliinfo" data-api="https://biliinfo.api.hzchu.top/api/v1/get_video_info?bvid=BV1JCbpzWELb""></div><h2 id="ai"><a class="markdownIt-Anchor" href="#ai"></a> AI</h2><p>AI太好用了你知道吗<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/azukisan/038.png"/></span><br />项目里一些我可能一辈子用不上几次的库（比如Matter.js）就直接让AI写了<br />一些库国内资料不多，但国外资料还是很详尽的<br />文中代码有疑问也可以直接找AI</p><h2 id="开源及版权相关"><a class="markdownIt-Anchor" href="#开源及版权相关"></a> 开源及版权相关</h2><p>虽然本文也差不多开源完了，但是整体不开源，原因如下：</p><ol><li>版权问题：尽管文中没有涉及Live2D LPK解密的具体实现、模型内容等信息，开源仍会带来不必要的纷争</li><li>实际问题：如果完全开源，开箱即用，那明年比赛上可不就神仙大乱斗了吗，给评委找麻烦</li></ol><p>本项目版权相关参阅<a href="https://lxh.hzchu.top/copyright">版权声明</a>，感谢各位作者的辛勤付出<span class="tag-plugin emoji"><img no-lazy="" class="inline" src="https://emoticons.hzchu.top/emoticons/luo-xiao-hei-zhan-ji/1.png"/></span></p>]]>
    </content>
    <id>https://blog.hzchu.top/2026/lxh-websites/</id>
    <link href="https://blog.hzchu.top/2026/lxh-websites/"/>
    <published>2026-02-23T10:53:21.000Z</published>
    <summary>快半年前的参赛作品</summary>
    <title>罗小黑网站分析</title>
    <updated>2026-02-23T11:14:15.000Z</updated>
  </entry>
</feed>
