{"id":3414,"date":"2025-06-29T20:52:34","date_gmt":"2025-06-29T12:52:34","guid":{"rendered":"https:\/\/blog.kangyue.pro\/?p=3414"},"modified":"2025-06-29T21:09:05","modified_gmt":"2025-06-29T13:09:05","slug":"%e5%8f%af%e4%bb%a5%e9%80%89%e6%8b%a9%e6%9c%ac%e5%9c%b0%e9%9f%b3%e4%b9%90%e5%92%8c%e6%ad%8c%e8%af%8d%e7%9a%84html%e5%8f%af%e8%a7%86%e5%8c%96%e6%92%ad%e6%94%be%e5%99%a8","status":"publish","type":"post","link":"https:\/\/blog.kangyue.pro\/?p=3414","title":{"rendered":"\u53ef\u4ee5\u9009\u62e9\u672c\u5730\u97f3\u4e50\u548c\u6b4c\u8bcd\u7684html\u53ef\u89c6\u5316\u64ad\u653e\u5668"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">\u4e0d\u542b\u64ad\u653e\u5217\u8868\u529f\u80fd<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u6d4b\u8bd5\uff1a<a href=\"https:\/\/kangyue.pro\/html\/VisualMusic\/local.html\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/kangyue.pro\/html\/VisualMusic\/local.html<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1278\" height=\"887\" src=\"https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-161.png\" alt=\"\" class=\"wp-image-3416\" srcset=\"https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-161.png 1278w, https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-161-768x533.png 768w\" sizes=\"auto, (max-width: 1278px) 100vw, 1278px\" \/><\/figure>\n\n\n\t\t<div class='wp-block-bch-code-highlight  align' id='bhcCodeHighlight-2dee96e7-c' data-attributes='{&quot;cId&quot;:&quot;2dee96e7-c&quot;,&quot;language&quot;:&quot;html&quot;,&quot;code&quot;:&quot;&lt;!DOCTYPE html&gt;\\n\\n &lt;html lang=\\&quot;zh\\&quot;&gt;\\n\\n &lt;head&gt;\\n\\n &lt;meta charset=\\&quot;UTF-8\\&quot;&gt;\\n\\n &lt;title&gt;\\u672c\\u5730\\u97f3\\u4e50\\u6b4c\\u8bcd\\u64ad\\u653e\\u5668&lt;\\\/title&gt;\\n\\n &lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1.0\\&quot;&gt;\\n\\n &lt;style&gt;\\n\\n html, body {\\n\\n margin: 0;\\n\\n padding: 0;\\n\\n overflow: hidden;\\n\\n background: black;\\n\\n color: white;\\n\\n height: 100vh;\\n\\n display: flex;\\n\\n flex-direction: column;\\n\\n font-family: sans-serif;\\n\\n }\\n\\n canvas {\\n\\n flex: 1;\\n\\n display: block;\\n\\n cursor: pointer;\\n\\n }\\n\\n #controls, #bottomBar {\\n\\n position: absolute;\\n\\n z-index: 10;\\n\\n }\\n\\n #controls {\\n\\n top: 15px;\\n\\n left: 15px;\\n\\n display: flex;\\n\\n gap: 8px;\\n\\n flex-wrap: wrap;\\n\\n }\\n\\n .effect-btn {\\n\\n background: linear-gradient(45deg, #444, #888);\\n\\n color: white;\\n\\n padding: 10px 16px;\\n\\n border: none;\\n\\n border-radius: 20px;\\n\\n cursor: pointer;\\n\\n font-size: 14px;\\n\\n }\\n\\n .effect-btn.active {\\n\\n background: linear-gradient(45deg, #4facfe, #00f2fe);\\n\\n color: black;\\n\\n }\\n\\n #bottomBar {\\n\\n bottom: 20px;\\n\\n left: 20px;\\n\\n right: 20px;\\n\\n display: flex;\\n\\n align-items: center;\\n\\n gap: 10px;\\n\\n }\\n\\n #progress {\\n\\n flex: 1;\\n\\n height: 8px;\\n\\n background: #555;\\n\\n border-radius: 5px;\\n\\n overflow: hidden;\\n\\n cursor: pointer;\\n\\n }\\n\\n #progressFill {\\n\\n height: 100%;\\n\\n background: linear-gradient(to right, #00f2fe, #4facfe);\\n\\n width: 0%;\\n\\n }\\n\\n #lyrics {\\n\\n position: absolute;\\n\\n right: 20px;\\n\\n top: 60px;\\n\\n bottom: 100px;\\n\\n width: 300px;\\n\\n overflow-y: auto;\\n\\n background: rgba(0,0,0,0.4);\\n\\n border-radius: 10px;\\n\\n padding: 10px;\\n\\n font-size: 18px;\\n\\n line-height: 1.8;\\n\\n }\\n\\n #lyrics .current {\\n\\n color: #00f2fe;\\n\\n font-weight: bold;\\n\\n font-size: 24px;\\n\\n }\\n\\n #playPauseBtn {\\n\\n background: #222;\\n\\n color: white;\\n\\n border: none;\\n\\n padding: 8px 14px;\\n\\n border-radius: 12px;\\n\\n cursor: pointer;\\n\\n transition: 0.2s;\\n\\n width: 60px;\\n\\n font-size: 14px;\\n\\n }\\n\\n #playPauseBtn:hover {\\n\\n background: #00f2fe;\\n\\n color: black;\\n\\n }\\n\\n #selectBtn {\\n\\n position: fixed;\\n\\n top: 50%;\\n\\n left: 50%;\\n\\n transform: translate(-50%, -50%);\\n\\n background: linear-gradient(45deg, #00f2fe, #4facfe);\\n\\n color: black;\\n\\n padding: 14px 24px;\\n\\n border: none;\\n\\n border-radius: 20px;\\n\\n font-size: 16px;\\n\\n cursor: pointer;\\n\\n box-shadow: 0 0 10px #00f2fe;\\n\\n z-index: 30;\\n\\n }\\n\\n #selectBtn:hover {\\n\\n transform: translate(-50%, -50%) scale(1.05);\\n\\n background: linear-gradient(45deg, #4facfe, #00f2fe);\\n\\n }\\n\\n &lt;\\\/style&gt;\\n\\n &lt;\\\/head&gt;\\n\\n &lt;body&gt;\\n\\n &lt;div id=\\&quot;controls\\&quot;&gt;\\n\\n &lt;input type=\\&quot;file\\&quot; id=\\&quot;fileInput\\&quot; accept=\\&quot;.mp3,.wav,.ogg,.m4a,.lrc\\&quot; multiple style=\\&quot;display:none\\&quot; \\\/&gt;\\n\\n &lt;button id=\\&quot;selectBtn\\&quot; onclick=\\&quot;fileInput.click()\\&quot;&gt;\\u9009\\u62e9\\u97f3\\u4e50\\u548c\\u6b4c\\u8bcd&lt;\\\/button&gt;\\n\\n &lt;button class=\\&quot;effect-btn active\\&quot; data-effect=\\&quot;bars\\&quot;&gt;\\u6761\\u5f62\\u56fe&lt;\\\/button&gt;\\n\\n &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;wave\\&quot;&gt;\\u6ce2\\u6d6a\\u56fe&lt;\\\/button&gt;\\n\\n &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;circle\\&quot;&gt;\\u7ec4\\u4e50\\u5708&lt;\\\/button&gt;\\n\\n &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;particles\\&quot;&gt;\\u7c92\\u5b50\\u6ce1\\u6ce1&lt;\\\/button&gt;\\n\\n &lt;\\\/div&gt;\\n\\n \\n\\n &lt;div id=\\&quot;bottomBar\\&quot;&gt;\\n\\n &lt;button id=\\&quot;playPauseBtn\\&quot;&gt;\\u64ad\\u653e&lt;\\\/button&gt;\\n\\n &lt;div id=\\&quot;progress\\&quot;&gt;&lt;div id=\\&quot;progressFill\\&quot;&gt;&lt;\\\/div&gt;&lt;\\\/div&gt;\\n\\n &lt;\\\/div&gt;\\n\\n \\n\\n &lt;canvas id=\\&quot;visualizer\\&quot;&gt;&lt;\\\/canvas&gt;\\n\\n &lt;audio id=\\&quot;audio\\&quot; style=\\&quot;display:none;\\&quot;&gt;&lt;\\\/audio&gt;\\n\\n &lt;div id=\\&quot;lyrics\\&quot;&gt;&lt;\\\/div&gt;\\n\\n \\n\\n &lt;script&gt;\\n\\n const canvas = document.getElementById(&#039;visualizer&#039;);\\n\\n const ctx = canvas.getContext(&#039;2d&#039;);\\n\\n let W = canvas.width = window.innerWidth;\\n\\n let H = canvas.height = window.innerHeight;\\n\\n \\n\\n const audio = document.getElementById(&#039;audio&#039;);\\n\\n const fileInput = document.getElementById(&#039;fileInput&#039;);\\n\\n const playPauseBtn = document.getElementById(&#039;playPauseBtn&#039;);\\n\\n const progress = document.getElementById(&#039;progress&#039;);\\n\\n const progressFill = document.getElementById(&#039;progressFill&#039;);\\n\\n const lyricsDiv = document.getElementById(&#039;lyrics&#039;);\\n\\n const effectButtons = document.querySelectorAll(&#039;.effect-btn&#039;);\\n\\n const selectBtn = document.getElementById(&#039;selectBtn&#039;);\\n\\n \\n\\n let audioCtx, analyser, source, dataArray, bufferLength;\\n\\n let lyrics = [], currentLyricIndex = -1;\\n\\n let isPlaying = false;\\n\\n let currentEffect = &#039;bars&#039;;\\n\\n let particles = [];\\n\\n \\n\\n function initAudio() {\\n\\n if (audioCtx) return;\\n\\n audioCtx = new (window.AudioContext || window.webkitAudioContext)();\\n\\n analyser = audioCtx.createAnalyser();\\n\\n source = audioCtx.createMediaElementSource(audio);\\n\\n source.connect(analyser);\\n\\n analyser.connect(audioCtx.destination);\\n\\n analyser.fftSize = 256;\\n\\n bufferLength = analyser.frequencyBinCount;\\n\\n dataArray = new Uint8Array(bufferLength);\\n\\n particles = new Array(80).fill(0).map(() =&gt; ({\\n\\n x: Math.random() * W,\\n\\n y: Math.random() * H,\\n\\n r: Math.random() * 3 + 1,\\n\\n speed: Math.random() * 1 + 0.5\\n\\n }));\\n \\n\\tfunction resizeCanvas() {\\n\\t  W = canvas.width = window.innerWidth;\\n\\t  H = canvas.height = window.innerHeight;\\n\\n\\t  \\\/\\\/ \\u91cd\\u65b0\\u521d\\u59cb\\u5316\\u7c92\\u5b50\\uff08\\u5982\\u679c\\u5f53\\u524d\\u6548\\u679c\\u662f\\u7c92\\u5b50\\uff09\\n\\t  if (currentEffect === &#039;particles&#039;) {\\n\\t\\tparticles = new Array(80).fill(0).map(() =&gt; ({\\n\\t\\t  x: Math.random() * W,\\n\\t\\t  y: Math.random() * H,\\n\\t\\t  r: Math.random() * 3 + 1,\\n\\t\\t  speed: Math.random() * 1 + 0.5\\n\\t\\t}));\\n\\t  }\\n\\t}\\n\\n\\t\\\/\\\/ \\u521d\\u59cb\\u6267\\u884c\\u4e00\\u6b21\\n\\tresizeCanvas();\\n\\n\\t\\\/\\\/ \\u76d1\\u542c\\u7a97\\u53e3\\u5927\\u5c0f\\u53d8\\u5316\\n\\twindow.addEventListener(&#039;resize&#039;, resizeCanvas);\\n\\t\\n render();\\n\\n }\\n\\n \\n\\n async function startPlay() {\\n\\n if (!audio.src) return;\\n\\n initAudio();\\n\\n if (audioCtx.state === &#039;suspended&#039;) await audioCtx.resume();\\n\\n try {\\n\\n await audio.play();\\n\\n } catch (err) {\\n\\n console.error(\\&quot;\\u64ad\\u653e\\u5931\\u8d25\\&quot;, err);\\n\\n }\\n\\n }\\n\\n \\n\\n playPauseBtn.onclick = () =&gt; {\\n\\n if (audio.paused) startPlay();\\n\\n else audio.pause();\\n\\n };\\n\\n \\n\\n canvas.addEventListener(&#039;click&#039;, () =&gt; {\\n\\n playPauseBtn.click();\\n\\n });\\n\\n \\n\\n document.addEventListener(&#039;keydown&#039;, e =&gt; {\\n\\n if (e.code === &#039;Space&#039;) {\\n\\n e.preventDefault();\\n\\n playPauseBtn.click();\\n\\n } else if (e.code === &#039;ArrowLeft&#039;) {\\n\\n audio.currentTime = Math.max(0, audio.currentTime - 5);\\n\\n } else if (e.code === &#039;ArrowRight&#039;) {\\n\\n audio.currentTime = Math.min(audio.duration, audio.currentTime + 5);\\n\\n }\\n\\n });\\n\\n \\n\\n audio.onplay = () =&gt; {\\n\\n isPlaying = true;\\n\\n playPauseBtn.textContent = &#039;\\u6682\\u505c&#039;;\\n\\n };\\n\\n \\n\\n audio.onpause = () =&gt; {\\n\\n isPlaying = false;\\n\\n playPauseBtn.textContent = &#039;\\u64ad\\u653e&#039;;\\n\\n };\\n\\n \\n\\n fileInput.addEventListener(&#039;change&#039;, (e) =&gt; {\\n\\n const files = Array.from(e.target.files);\\n\\n const audioFile = files.find(f =&gt; \\\/\\\\.(mp3|wav|ogg|m4a)$\\\/i.test(f.name));\\n\\n const lrcFile = files.find(f =&gt; \\\/\\\\.lrc$\\\/i.test(f.name));\\n\\n \\n\\n if (audioFile) {\\n\\n audio.src = URL.createObjectURL(audioFile);\\n\\n lyrics = [];\\n\\n lyricsDiv.innerHTML = &#039;&lt;div style=\\&quot;color:#999;\\&quot;&gt;\\u6b63\\u5728\\u52a0\\u8f7d\\u6b4c\\u8bcd...&lt;\\\/div&gt;&#039;;\\n\\n selectBtn.style.display = &#039;none&#039;;\\n\\n \\n\\n if (lrcFile) {\\n\\n const reader = new FileReader();\\n\\n reader.onload = () =&gt; {\\n\\n const text = reader.result;\\n\\n lyrics = parseLRC(text);\\n\\n renderLyrics();\\n\\n };\\n\\n reader.readAsText(lrcFile, &#039;utf-8&#039;);\\n\\n } else {\\n\\n lyricsDiv.innerHTML = &#039;&lt;div style=\\&quot;color:#999;\\&quot;&gt;\\u672a\\u9009\\u62e9\\u6b4c\\u8bcd\\u6587\\u4ef6&lt;\\\/div&gt;&#039;;\\n\\n }\\n\\n \\n\\n startPlay();\\n\\n }\\n\\n });\\n\\n \\n\\n progress.onclick = (e) =&gt; {\\n\\n if (!audio.duration) return;\\n\\n const rect = progress.getBoundingClientRect();\\n\\n const ratio = (e.clientX - rect.left) \\\/ rect.width;\\n\\n audio.currentTime = ratio * audio.duration;\\n\\n };\\n\\n \\n\\n function updateProgress() {\\n\\n if (!audio.duration) return;\\n\\n const percent = (audio.currentTime \\\/ audio.duration) * 100;\\n\\n progressFill.style.width = percent + &#039;%&#039;;\\n\\n }\\n\\n \\n\\n function render() {\\n\\n requestAnimationFrame(render);\\n\\n if (!analyser) return;\\n\\n analyser.getByteFrequencyData(dataArray);\\n\\n ctx.clearRect(0, 0, W, H);\\n\\n \\n\\n if (currentEffect === &#039;bars&#039;) drawBars();\\n\\n else if (currentEffect === &#039;wave&#039;) drawWave();\\n\\n else if (currentEffect === &#039;circle&#039;) drawCircle();\\n\\n else if (currentEffect === &#039;particles&#039;) drawParticles();\\n\\n \\n\\n if (isPlaying) {\\n\\n updateProgress();\\n\\n highlightLyric(audio.currentTime);\\n\\n }\\n\\n }\\n\\n \\n\\n function drawBars() {\\n\\n const barWidth = W \\\/ bufferLength;\\n\\n for (let i = 0; i &lt; bufferLength; i++) {\\n\\n const barHeight = dataArray[i] * (H \\\/ 255);\\n\\n ctx.fillStyle = `hsl(${i \\\/ bufferLength * 360}, 100%, 50%)`;\\n\\n ctx.fillRect(i * barWidth, H - barHeight, barWidth, barHeight);\\n\\n }\\n\\n }\\n\\n \\n\\n function drawWave() {\\n\\n analyser.getByteTimeDomainData(dataArray);\\n\\n ctx.beginPath();\\n\\n ctx.lineWidth = 2;\\n\\n ctx.strokeStyle = &#039;#00ffcc&#039;;\\n\\n const sliceWidth = W \\\/ bufferLength;\\n\\n let x = 0;\\n\\n for (let i = 0; i &lt; bufferLength; i++) {\\n\\n let v = dataArray[i] \\\/ 128.0;\\n\\n let y = v * H \\\/ 2;\\n\\n if (i === 0) ctx.moveTo(x, y);\\n\\n else ctx.lineTo(x, y);\\n\\n x += sliceWidth;\\n\\n }\\n\\n ctx.stroke();\\n\\n }\\n\\n \\n\\n function drawCircle() {\\n\\n let centerX = W \\\/ 2;\\n\\n let centerY = H \\\/ 2;\\n\\n let radius = Math.min(W, H) * 0.15;\\n\\n for (let i = 0; i &lt; bufferLength; i++) {\\n\\n let angle = (i \\\/ bufferLength) * 2 * Math.PI;\\n\\n let len = dataArray[i] * 0.8;\\n\\n let x1 = centerX + Math.cos(angle) * radius;\\n\\n let y1 = centerY + Math.sin(angle) * radius;\\n\\n let x2 = centerX + Math.cos(angle) * (radius + len);\\n\\n let y2 = centerY + Math.sin(angle) * (radius + len);\\n\\n ctx.strokeStyle = `hsl(${i \\\/ bufferLength * 360}, 100%, 50%)`;\\n\\n ctx.beginPath();\\n\\n ctx.moveTo(x1, y1);\\n\\n ctx.lineTo(x2, y2);\\n\\n ctx.stroke();\\n\\n }\\n\\n }\\n\\n \\n\\n function drawParticles() {\\n\\n ctx.shadowColor = &#039;#00ffff&#039;;\\n\\n ctx.shadowBlur = 10;\\n\\n for (const p of particles) {\\n\\n let index = Math.floor((p.x \\\/ W) * bufferLength);\\n\\n let volume = dataArray[index] \\\/ 255;\\n\\n let size = p.r + volume * 12;\\n\\n p.y -= p.speed + volume * 2;\\n\\n if (p.y &lt; -size) {\\n\\n p.y = H + size;\\n\\n p.x = Math.random() * W;\\n\\n }\\n\\n ctx.beginPath();\\n\\n ctx.fillStyle = `rgba(0, 255, 255, ${0.3 + volume})`;\\n\\n ctx.arc(p.x, p.y, size, 0, Math.PI * 2);\\n\\n ctx.fill();\\n\\n }\\n\\n ctx.shadowBlur = 0;\\n\\n }\\n\\n \\n\\n effectButtons.forEach(btn =&gt; {\\n\\n btn.onclick = () =&gt; {\\n\\n currentEffect = btn.dataset.effect;\\n\\n effectButtons.forEach(b =&gt; b.classList.remove(&#039;active&#039;));\\n\\n btn.classList.add(&#039;active&#039;);\\n\\n };\\n\\n });\\n\\n \\n\\n function parseLRC(text) {\\n\\n const lines = text.split(&#039;\\\\n&#039;);\\n\\n const result = [];\\n\\n const timeRegexp = \\\/\\\\[(\\\\d{2}):(\\\\d{2})\\\\.(\\\\d{2,3})\\\\]\\\/g;\\n\\n for (const line of lines) {\\n\\n const content = line.replace(timeRegexp, &#039;&#039;).trim();\\n\\n if (!content) continue;\\n\\n let match;\\n\\n timeRegexp.lastIndex = 0;\\n\\n while ((match = timeRegexp.exec(line)) !== null) {\\n\\n const min = parseInt(match[1]);\\n\\n const sec = parseInt(match[2]);\\n\\n const ms = parseInt(match[3].padEnd(3, &#039;0&#039;));\\n\\n result.push({ time: min * 60 + sec + ms \\\/ 1000, text: content });\\n\\n }\\n\\n }\\n\\n return result.sort((a, b) =&gt; a.time - b.time);\\n\\n }\\n\\n \\n\\n function renderLyrics() {\\n\\n lyricsDiv.innerHTML = lyrics.map(line =&gt; `&lt;div&gt;${line.text}&lt;\\\/div&gt;`).join(&#039;&#039;);\\n\\n currentLyricIndex = -1;\\n\\n }\\n\\n \\n\\n function highlightLyric(currentTime) {\\n\\n if (!lyrics.length) return;\\n\\n const newIndex = lyrics.findIndex((line, i) =&gt; {\\n\\n const next = lyrics[i + 1];\\n\\n return currentTime &gt;= line.time &amp;&amp; (!next || currentTime &lt; next.time);\\n\\n });\\n\\n if (newIndex !== -1 &amp;&amp; newIndex !== currentLyricIndex) {\\n\\n if (lyricsDiv.children[currentLyricIndex]) {\\n\\n lyricsDiv.children[currentLyricIndex].classList.remove(&#039;current&#039;);\\n\\n }\\n\\n const currentEl = lyricsDiv.children[newIndex];\\n\\n currentEl.classList.add(&#039;current&#039;);\\n\\n lyricsDiv.scrollTop = currentEl.offsetTop - lyricsDiv.clientHeight \\\/ 2;\\n\\n currentLyricIndex = newIndex;\\n\\n }\\n\\n }\\n\\n &lt;\\\/script&gt;\\n\\n &lt;\\\/body&gt;\\n\\n &lt;\\\/html&gt;&quot;,&quot;codeTypo&quot;:{&quot;desktop&quot;:10,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;height&quot;:{&quot;desktop&quot;:&quot;300px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;theme&quot;:&quot;default&quot;,&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[],&quot;alignment&quot;:&quot;center&quot;,&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;}}'><\/div>\r\n\r\n\t\t\n\n\n<p class=\"wp-block-paragraph\">\u6b64\u7248\u672c\u51e0\u4e4e\u65e0\u5f02\u5e38\uff0c\u4f46\u65e0\u64ad\u653e\u5217\u8868\u529f\u80fd\uff0c\u540c\u65f6\u4ec5\u652f\u6301UTF-8\u7f16\u7801\u7684\u6b4c\u8bcd\u6587\u4ef6\u3002\u53e6\u5916\uff0c\u79fb\u52a8\u7aef\u5e03\u5c40\u8f83\u5dee\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">\u542b\u64ad\u653e\u5217\u8868\u529f\u80fd<\/h1>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1278\" height=\"928\" src=\"https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-160.png\" alt=\"\" class=\"wp-image-3415\" srcset=\"https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-160.png 1278w, https:\/\/blog.kangyue.pro\/wp-content\/uploads\/2025\/06\/image-160-768x558.png 768w\" sizes=\"auto, (max-width: 1278px) 100vw, 1278px\" \/><\/figure>\n\n\n\t\t<div class='wp-block-bch-code-highlight  align' id='bhcCodeHighlight-372eed56-7' data-attributes='{&quot;cId&quot;:&quot;372eed56-7&quot;,&quot;language&quot;:&quot;html&quot;,&quot;code&quot;:&quot;&lt;!DOCTYPE html&gt;\\n&lt;html lang=\\&quot;zh\\&quot;&gt;\\n&lt;head&gt;\\n  &lt;meta charset=\\&quot;UTF-8\\&quot;&gt;\\n  &lt;title&gt;\\u672c\\u5730\\u97f3\\u4e50\\u6b4c\\u8bcd\\u64ad\\u653e\\u5668&lt;\\\/title&gt;\\n  &lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1.0\\&quot;&gt;\\n  &lt;style&gt;\\n    html, body {\\n      margin: 0; padding: 0; height: 100vh;\\n      background: black; color: white;\\n      display: flex; flex-direction: column;\\n      font-family: sans-serif;\\n      overflow: hidden;\\n    }\\n    canvas {\\n      flex: 1;\\n      display: block;\\n      cursor: pointer;\\n    }\\n    #controls {\\n      position: absolute;\\n      top: 15px;\\n      left: 50%;\\n      transform: translateX(-50%);\\n      display: flex;\\n      flex-wrap: wrap;\\n      gap: 8px;\\n      z-index: 10;\\n    }\\n    .effect-btn {\\n      background: linear-gradient(45deg, #444, #888);\\n      color: white; padding: 10px 16px;\\n      border: none; border-radius: 20px;\\n      cursor: pointer; font-size: 14px;\\n    }\\n    .effect-btn.active {\\n      background: linear-gradient(45deg, #4facfe, #00f2fe);\\n      color: black;\\n    }\\n    #bottomBar {\\n      position: absolute;\\n      bottom: 20px;\\n      left: 20px; right: 20px;\\n      display: flex; align-items: center;\\n      gap: 10px; z-index: 10;\\n    }\\n    #progress {\\n      flex: 1;\\n      height: 8px;\\n      background: #555;\\n      border-radius: 5px;\\n      overflow: hidden;\\n      cursor: pointer;\\n    }\\n    #progressFill {\\n      height: 100%;\\n      background: linear-gradient(to right, #00f2fe, #4facfe);\\n      width: 0%;\\n    }\\n    #playPauseBtn {\\n      background: #222;\\n      color: white;\\n      border: none;\\n      padding: 8px 14px;\\n      border-radius: 12px;\\n      cursor: pointer;\\n      transition: 0.2s;\\n      width: 60px;\\n      font-size: 14px;\\n    }\\n    #playPauseBtn:hover {\\n      background: #00f2fe;\\n      color: black;\\n    }\\n    #lyrics {\\n      position: absolute;\\n      top: 80px;\\n      bottom: 100px;\\n      right: 20px;\\n      width: 300px;\\n      overflow-y: auto;\\n      background: rgba(0,0,0,0.4);\\n      border-radius: 10px;\\n      padding: 10px;\\n      font-size: 18px;\\n    }\\n    #lyrics .current {\\n      color: #00f2fe;\\n      font-weight: bold;\\n      font-size: 22px;\\n    }\\n    #playlist {\\n      position: absolute;\\n      bottom: 100px;\\n      left: 20px;\\n      max-height: 300px;\\n      overflow-y: auto;\\n      width: 260px;\\n      background: rgba(0,0,0,0.4);\\n      border-radius: 10px;\\n      padding: 10px;\\n      font-size: 14px;\\n      z-index: 10;\\n    }\\n    #playlist div {\\n      padding: 6px 10px;\\n      cursor: pointer;\\n      position: relative;\\n    }\\n    #playlist div:hover {\\n      background: rgba(255,255,255,0.1);\\n    }\\n    #playlist .remove-btn {\\n      position: absolute;\\n      right: 5px; top: 50%;\\n      transform: translateY(-50%);\\n      color: rgba(255,255,255,0.5);\\n      font-weight: bold;\\n      cursor: pointer;\\n      display: none;\\n    }\\n    #playlist div:hover .remove-btn {\\n      display: block;\\n    }\\n    #selectBtn {\\n      background: linear-gradient(45deg, #00f2fe, #4facfe);\\n      color: black;\\n      padding: 10px 20px;\\n      border: none;\\n      border-radius: 20px;\\n      font-size: 14px;\\n      cursor: pointer;\\n      box-shadow: 0 0 10px #00f2fe;\\n    }\\n  &lt;\\\/style&gt;\\n&lt;\\\/head&gt;\\n&lt;body&gt;\\n  &lt;canvas id=\\&quot;visualizer\\&quot;&gt;&lt;\\\/canvas&gt;\\n\\n  &lt;div id=\\&quot;controls\\&quot;&gt;\\n    &lt;input type=\\&quot;file\\&quot; id=\\&quot;fileInput\\&quot; accept=\\&quot;.mp3,.wav,.ogg,.m4a,.lrc\\&quot; multiple style=\\&quot;display:none\\&quot;&gt;\\n    &lt;button id=\\&quot;selectBtn\\&quot; onclick=\\&quot;fileInput.click()\\&quot;&gt;\\u9009\\u62e9\\u672c\\u5730\\u97f3\\u4e50\\u548c\\u6b4c\\u8bcd&lt;\\\/button&gt;\\n    &lt;button class=\\&quot;effect-btn active\\&quot; data-effect=\\&quot;bars\\&quot;&gt;\\u6761\\u5f62\\u56fe&lt;\\\/button&gt;\\n    &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;wave\\&quot;&gt;\\u6ce2\\u6d6a\\u56fe&lt;\\\/button&gt;\\n    &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;circle\\&quot;&gt;\\u7ec4\\u4e50\\u5708&lt;\\\/button&gt;\\n    &lt;button class=\\&quot;effect-btn\\&quot; data-effect=\\&quot;particles\\&quot;&gt;\\u7c92\\u5b50\\u6ce1\\u6ce1&lt;\\\/button&gt;\\n  &lt;\\\/div&gt;\\n\\n  &lt;div id=\\&quot;bottomBar\\&quot;&gt;\\n    &lt;button id=\\&quot;playPauseBtn\\&quot;&gt;\\u64ad\\u653e&lt;\\\/button&gt;\\n    &lt;div id=\\&quot;progress\\&quot;&gt;&lt;div id=\\&quot;progressFill\\&quot;&gt;&lt;\\\/div&gt;&lt;\\\/div&gt;\\n  &lt;\\\/div&gt;\\n\\n  &lt;div id=\\&quot;lyrics\\&quot;&gt;&lt;\\\/div&gt;\\n  &lt;div id=\\&quot;playlist\\&quot;&gt;&lt;\\\/div&gt;\\n  &lt;audio id=\\&quot;audio\\&quot; style=\\&quot;display:none;\\&quot; loop&gt;&lt;\\\/audio&gt;\\n\\n  &lt;script&gt;\\n    const canvas = document.getElementById(&#039;visualizer&#039;);\\n    const ctx = canvas.getContext(&#039;2d&#039;);\\n    const audio = document.getElementById(&#039;audio&#039;);\\n    const playPauseBtn = document.getElementById(&#039;playPauseBtn&#039;);\\n    const fileInput = document.getElementById(&#039;fileInput&#039;);\\n    const lyricsDiv = document.getElementById(&#039;lyrics&#039;);\\n    const playlistDiv = document.getElementById(&#039;playlist&#039;);\\n    const progress = document.getElementById(&#039;progress&#039;);\\n    const progressFill = document.getElementById(&#039;progressFill&#039;);\\n    const effectButtons = document.querySelectorAll(&#039;.effect-btn&#039;);\\n\\n    let W = canvas.width = window.innerWidth;\\n    let H = canvas.height = window.innerHeight;\\n\\n    let audioCtx, analyser, source, dataArray, bufferLength;\\n    let currentEffect = &#039;bars&#039;;\\n    let particles = [];\\n    let lyrics = [];\\n    let currentLyricIndex = -1;\\n    let playlist = [];\\n\\n    canvas.addEventListener(&#039;click&#039;, () =&gt; playPauseBtn.click());\\n\\n    window.addEventListener(&#039;resize&#039;, () =&gt; {\\n      W = canvas.width = window.innerWidth;\\n      H = canvas.height = window.innerHeight;\\n    });\\n\\n    function initAudio() {\\n      if (audioCtx) return;\\n      audioCtx = new (window.AudioContext || window.webkitAudioContext)();\\n      analyser = audioCtx.createAnalyser();\\n      source = audioCtx.createMediaElementSource(audio);\\n      source.connect(analyser);\\n      analyser.connect(audioCtx.destination);\\n      analyser.fftSize = 256;\\n      bufferLength = analyser.frequencyBinCount;\\n      dataArray = new Uint8Array(bufferLength);\\n      particles = Array.from({length: 80}, () =&gt; ({\\n        x: Math.random() * W,\\n        y: Math.random() * H,\\n        r: Math.random() * 3 + 1,\\n        speed: Math.random() * 1 + 0.5\\n      }));\\n      render();\\n    }\\n\\n    function startPlay() {\\n      if (!audio.src) return;\\n      initAudio();\\n      audioCtx.resume();\\n      audio.play();\\n    }\\n\\n    playPauseBtn.onclick = () =&gt; {\\n      audio.paused ? startPlay() : audio.pause();\\n    };\\n\\n    document.addEventListener(&#039;keydown&#039;, e =&gt; {\\n      if (e.code === &#039;Space&#039;) {\\n        e.preventDefault();\\n        playPauseBtn.click();\\n      } else if (e.code === &#039;ArrowLeft&#039;) {\\n        audio.currentTime = Math.max(0, audio.currentTime - 5);\\n      } else if (e.code === &#039;ArrowRight&#039;) {\\n        audio.currentTime = Math.min(audio.duration, audio.currentTime + 5);\\n      }\\n    });\\n\\n    audio.onplay = () =&gt; playPauseBtn.textContent = &#039;\\u6682\\u505c&#039;;\\n    audio.onpause = () =&gt; playPauseBtn.textContent = &#039;\\u64ad\\u653e&#039;;\\n\\n    progress.onclick = e =&gt; {\\n      if (!audio.duration) return;\\n      const rect = progress.getBoundingClientRect();\\n      const ratio = (e.clientX - rect.left) \\\/ rect.width;\\n      audio.currentTime = ratio * audio.duration;\\n    };\\n\\n\\tfileInput.addEventListener(&#039;change&#039;, (e) =&gt; {\\n\\t  const files = Array.from(e.target.files);\\n\\n\\t  \\\/\\\/ \\u627e\\u51fa\\u6240\\u6709\\u97f3\\u9891\\u6587\\u4ef6\\n\\t  const audioFiles = files.filter(f =&gt; \\\/\\\\.(mp3|wav|ogg|m4a)$\\\/i.test(f.name));\\n\\n\\t  \\\/\\\/ \\u751f\\u6210\\u4e00\\u4e2a map\\uff1a\\u6b4c\\u540d =&gt; \\u6b4c\\u8bcd\\u6587\\u4ef6\\n\\t  const lrcMap = new Map();\\n\\t  files.filter(f =&gt; \\\/\\\\.lrc$\\\/i.test(f.name)).forEach(lrc =&gt; {\\n\\t\\tconst base = lrc.name.replace(\\\/\\\\.[^\\\/.]+$\\\/, &#039;&#039;);\\n\\t\\tlrcMap.set(base, lrc);\\n\\t  });\\n\\n\\t  audioFiles.forEach((audioFile, index) =&gt; {\\n\\t\\tconst audioUrl = URL.createObjectURL(audioFile);\\n\\t\\tconst title = audioFile.name;\\n\\t\\tconst base = title.replace(\\\/\\\\.[^\\\/.]+$\\\/, &#039;&#039;);\\n\\t\\tconst lrcFile = lrcMap.get(base) || null;\\n\\n\\t\\tplaylist.push({ title, url: audioUrl, lrcFile });\\n\\n\\t\\t\\\/\\\/ \\u5982\\u679c\\u662f\\u7b2c\\u4e00\\u4e2a\\u97f3\\u9891\\uff0c\\u81ea\\u52a8\\u64ad\\u653e\\n\\t\\tif (index === 0) {\\n\\t\\t  playFromIndex(playlist.length - 1);\\n\\t\\t}\\n\\t  });\\n\\n\\t  renderPlaylist();\\n\\t});\\n\\n    function playFromIndex(index) {\\n      const item = playlist[index];\\n      if (!item) return;\\n      audio.src = item.url;\\n      if (item.lrcFile) {\\n\\n\\t\\t\\\/\\\/ \\u81ea\\u52a8\\u8bc6\\u522b\\u7f16\\u7801\\uff08UTF-8\\\/GBK\\uff09\\n\\t\\tconst reader = new FileReader();\\n\\t\\treader.onload = async () =&gt; {\\n\\t\\t  const buffer = reader.result;\\n\\t\\t  let text;\\n\\n\\t\\t  try {\\n\\t\\t\\ttext = new TextDecoder(&#039;utf-8&#039;, { fatal: true }).decode(buffer);\\n\\t\\t  } catch {\\n\\t\\t\\ttry {\\n\\t\\t\\t  text = new TextDecoder(&#039;gb18030&#039;).decode(buffer);\\n\\t\\t\\t} catch {\\n\\t\\t\\t  text = &#039;\\uff08\\u6b4c\\u8bcd\\u89e3\\u7801\\u5931\\u8d25\\uff09&#039;;\\n\\t\\t\\t}\\n\\t\\t  }\\n\\n\\t\\t  lyrics = parseLRC(text);\\n\\t\\t  renderLyrics();\\n\\t\\t};\\n\\t\\treader.readAsArrayBuffer(lrcFile);\\n\\n\\n\\n      } else {\\n        lyrics = [];\\n        lyricsDiv.innerHTML = &#039;&lt;div style=\\&quot;color:#aaa;\\&quot;&gt;\\u65e0\\u6b4c\\u8bcd&lt;\\\/div&gt;&#039;;\\n      }\\n      startPlay();\\n    }\\n\\n    function renderPlaylist() {\\n      playlistDiv.innerHTML = &#039;&#039;;\\n      playlist.forEach((item, i) =&gt; {\\n        const div = document.createElement(&#039;div&#039;);\\n        div.textContent = item.title;\\n        div.onclick = () =&gt; playFromIndex(i);\\n        const removeBtn = document.createElement(&#039;span&#039;);\\n        removeBtn.textContent = &#039;\\u00d7&#039;;\\n        removeBtn.className = &#039;remove-btn&#039;;\\n        removeBtn.onclick = (e) =&gt; {\\n          e.stopPropagation();\\n          playlist.splice(i, 1);\\n          renderPlaylist();\\n        };\\n        div.appendChild(removeBtn);\\n        playlistDiv.appendChild(div);\\n      });\\n    }\\n\\n    function updateProgress() {\\n      if (!audio.duration) return;\\n      const percent = (audio.currentTime \\\/ audio.duration) * 100;\\n      progressFill.style.width = percent + &#039;%&#039;;\\n    }\\n\\n    function render() {\\n      requestAnimationFrame(render);\\n      if (!analyser) return;\\n      analyser.getByteFrequencyData(dataArray);\\n      ctx.clearRect(0, 0, W, H);\\n      if (currentEffect === &#039;bars&#039;) drawBars();\\n      else if (currentEffect === &#039;wave&#039;) drawWave();\\n      else if (currentEffect === &#039;circle&#039;) drawCircle();\\n      else drawParticles();\\n      updateProgress();\\n      highlightLyric(audio.currentTime);\\n    }\\n\\n    function drawBars() {\\n      const barWidth = W \\\/ bufferLength;\\n      for (let i = 0; i &lt; bufferLength; i++) {\\n        const h = dataArray[i] * (H \\\/ 255);\\n        ctx.fillStyle = `hsl(${i \\\/ bufferLength * 360}, 100%, 50%)`;\\n        ctx.fillRect(i * barWidth, H - h, barWidth, h);\\n      }\\n    }\\n\\n    function drawWave() {\\n      analyser.getByteTimeDomainData(dataArray);\\n      ctx.beginPath();\\n      ctx.lineWidth = 2;\\n      ctx.strokeStyle = &#039;#00ffcc&#039;;\\n      const slice = W \\\/ bufferLength;\\n      let x = 0;\\n      for (let i = 0; i &lt; bufferLength; i++) {\\n        const v = dataArray[i] \\\/ 128;\\n        const y = v * H \\\/ 2;\\n        if (i === 0) ctx.moveTo(x, y);\\n        else ctx.lineTo(x, y);\\n        x += slice;\\n      }\\n      ctx.stroke();\\n    }\\n\\n    function drawCircle() {\\n      const cx = W \\\/ 2, cy = H \\\/ 2;\\n      const r = Math.min(W, H) * 0.15;\\n      for (let i = 0; i &lt; bufferLength; i++) {\\n        const angle = i \\\/ bufferLength * 2 * Math.PI;\\n        const len = dataArray[i] * 0.8;\\n        const x1 = cx + Math.cos(angle) * r;\\n        const y1 = cy + Math.sin(angle) * r;\\n        const x2 = cx + Math.cos(angle) * (r + len);\\n        const y2 = cy + Math.sin(angle) * (r + len);\\n        ctx.strokeStyle = `hsl(${i \\\/ bufferLength * 360}, 100%, 50%)`;\\n        ctx.beginPath();\\n        ctx.moveTo(x1, y1);\\n        ctx.lineTo(x2, y2);\\n        ctx.stroke();\\n      }\\n    }\\n\\n    function drawParticles() {\\n      ctx.shadowColor = &#039;#00ffff&#039;;\\n      ctx.shadowBlur = 10;\\n      for (const p of particles) {\\n        const idx = Math.floor((p.x \\\/ W) * bufferLength);\\n        const volume = dataArray[idx] \\\/ 255;\\n        const size = p.r + volume * 12;\\n        p.y -= p.speed + volume * 2;\\n        if (p.y &lt; -size) {\\n          p.y = H + size;\\n          p.x = Math.random() * W;\\n        }\\n        ctx.beginPath();\\n        ctx.fillStyle = `rgba(0, 255, 255, ${0.3 + volume})`;\\n        ctx.arc(p.x, p.y, size, 0, Math.PI * 2);\\n        ctx.fill();\\n      }\\n      ctx.shadowBlur = 0;\\n    }\\n\\n    effectButtons.forEach(btn =&gt; {\\n      btn.onclick = () =&gt; {\\n        currentEffect = btn.dataset.effect;\\n        effectButtons.forEach(b =&gt; b.classList.remove(&#039;active&#039;));\\n        btn.classList.add(&#039;active&#039;);\\n      };\\n    });\\n\\n    function parseLRC(text) {\\n      const lines = text.split(&#039;\\\\n&#039;);\\n      const result = [];\\n      const timeRegexp = \\\/\\\\[(\\\\d{2}):(\\\\d{2})\\\\.(\\\\d{2,3})\\\\]\\\/g;\\n      for (const line of lines) {\\n        const content = line.replace(timeRegexp, &#039;&#039;).trim();\\n        if (!content) continue;\\n        let match;\\n        while ((match = timeRegexp.exec(line)) !== null) {\\n          const min = +match[1], sec = +match[2], ms = +match[3].padEnd(3, &#039;0&#039;);\\n          result.push({ time: min * 60 + sec + ms \\\/ 1000, text: content });\\n        }\\n      }\\n      return result.sort((a, b) =&gt; a.time - b.time);\\n    }\\n\\n    function renderLyrics() {\\n      lyricsDiv.innerHTML = lyrics.map(l =&gt; `&lt;div&gt;${l.text}&lt;\\\/div&gt;`).join(&#039;&#039;);\\n      currentLyricIndex = -1;\\n    }\\n\\n    function highlightLyric(currentTime) {\\n      if (!lyrics.length) return;\\n      const idx = lyrics.findIndex((line, i) =&gt; {\\n        const next = lyrics[i + 1];\\n        return currentTime &gt;= line.time &amp;&amp; (!next || currentTime &lt; next.time);\\n      });\\n      if (idx !== -1 &amp;&amp; idx !== currentLyricIndex) {\\n        if (lyricsDiv.children[currentLyricIndex]) {\\n          lyricsDiv.children[currentLyricIndex].classList.remove(&#039;current&#039;);\\n        }\\n        const el = lyricsDiv.children[idx];\\n        el.classList.add(&#039;current&#039;);\\n        lyricsDiv.scrollTop = el.offsetTop - lyricsDiv.clientHeight \\\/ 2;\\n        currentLyricIndex = idx;\\n      }\\n    }\\n  &lt;\\\/script&gt;\\n&lt;\\\/body&gt;\\n&lt;\\\/html&gt;\\n&quot;,&quot;codeTypo&quot;:{&quot;desktop&quot;:10,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;height&quot;:{&quot;desktop&quot;:&quot;300px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;theme&quot;:&quot;default&quot;,&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[],&quot;alignment&quot;:&quot;center&quot;,&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;}}'><\/div>\r\n\r\n\t\t\n\n\n<p class=\"wp-block-paragraph\">\u5f85\u6539\u8fdb\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u6b4c\u8bcd\u672a\u663e\u793a<\/li>\n\n\n\n<li>\u6709\u7684\u6b4c\u66f2\u70b9\u51fb\u4e4b\u540e\u5e76\u672a\u7acb\u523b\u5f00\u59cb\u64ad\u653e\uff0c\u9700\u8981\u70b9\u51fb\u6682\u505c\u624d\u53ef\u64ad\u653e<\/li>\n\n\n\n<li>\u5de6\u4fa7\u7684\u6587\u4ef6\u5217\u8868\uff0c\u6539\u6210\u81ea\u52a8\u9ad8\u5ea6\u3002\u5982\u679c\u6587\u4ef6\u592a\u591a\uff0c\u53ef\u4ee5\u7528\u6eda\u52a8\u6761\uff0c\u4f46\u8981\u7f8e\u89c2\u7684\u3002\u53f3\u4fa7\u6b4c\u8bcd\u533a\u57df\u4e5f\u4e00\u6837<\/li>\n\n\n\n<li>\u5de6\u4fa7\u7684\u6587\u4ef6\u5217\u8868\u8fd8\u8fd8\u8981\u80fd\u591f\u6536\u56de\/\u5c55\u793a<\/li>\n\n\n\n<li>\u6587\u4ef6\u5217\u8868\u4e0b\u65b9\uff0c\u589e\u52a0\u300c\u5355\u66f2\u5faa\u73af\u300d\u300c\u5217\u8868\u5faa\u73af\u300d\u6309\u94ae\uff0c\u5e76\u5b9e\u73b0\u76f8\u5e94\u529f\u80fd\uff0c\u9ed8\u8ba4\u9009\u62e9\u540e\u8005\uff0c\u4f46\u8981\u901a\u8fc7cookies\u8bb0\u4f4f\u7528\u6237\u9009\u62e9\u5207\u6362\u7684\u6309\u94ae<\/li>\n\n\n\n<li>\u9009\u62e9\u672c\u5730\u6587\u4ef6\u548c\u6b4c\u8bcd\u8fd9\u4e2a\u6309\u94ae\uff0c\u663e\u793a\u5728\u5de6\u4e0a\u89d2\u3002\u5e76\u66f4\u6539\u8fd9\u4e2a\u6309\u94ae\u7684\u98ce\u683c<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>\u4e0d\u542b\u64ad\u653e\u5217\u8868\u529f\u80fd \u6d4b\u8bd5\uff1ahttps:\/\/kangyue.pro\/html\/VisualMusic\/local. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3414","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/posts\/3414","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3414"}],"version-history":[{"count":7,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/posts\/3414\/revisions"}],"predecessor-version":[{"id":3423,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=\/wp\/v2\/posts\/3414\/revisions\/3423"}],"wp:attachment":[{"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3414"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3414"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.kangyue.pro\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3414"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}