找回密码
 立即注册
点击图片立即观看
搜索
楼主: 风萧

论坛发帖可以使用的HTML代码

[复制链接]
 楼主| 发表于 2026-8-26 08:29:05 |香港| 显示全部楼层
带歌词的音频播放器代码
推荐版·自动滚动歌词
  1. <div style="width:100%;max-width:540px;margin:10px auto;border:1px solid #ddd;border-radius:14px;overflow:hidden;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);box-shadow:0 6px 20px rgba(0,0,0,0.15);font-family:'Microsoft YaHei','PingFang SC',Arial,sans-serif;color:#fff;font-size:14px;">

  2.   <!-- ========== 顶部:封面 + 歌曲信息 ========== -->
  3.   <div style="display:flex;align-items:center;padding:16px;background:rgba(0,0,0,0.18);">
  4.     <img src="你的封面图片URL.jpg" alt="专辑封面" style="width:84px;height:84px;border-radius:10px;object-fit:cover;border:2px solid rgba(255,255,255,0.35);margin-right:16px;flex-shrink:0;" onerror="this.style.display='none';">
  5.     <div style="flex:1;min-width:0;">
  6.       <div style="font-size:19px;font-weight:bold;margin-bottom:6px;letter-spacing:0.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">♫ 声声慢</div>
  7.       <div style="font-size:13px;opacity:0.8;margin-bottom:4px;">演唱:韩菁英</div>
  8.       <div style="font-size:12px;opacity:0.6;">专辑:最美女声</div>
  9.     </div>
  10.   </div>

  11.   <!-- ========== 中部:歌词滚动区 ========== -->
  12.   <div style="padding:14px 16px;background:rgba(255,255,255,0.08);">
  13.     <div style="text-align:center;font-size:12px;opacity:0.7;margin-bottom:10px;letter-spacing:2px;">━━━ 歌  词 ━━━</div>

  14.     <!-- marquee:HTML原生滚动标签,direction=up向上滚动,scrollamount控制速度 -->
  15.     <marquee direction="up" scrollamount="1" scrolldelay="130" height="180" style="text-align:center;line-height:2.1;font-size:15px;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,0.3);">
  16. <p>作词 : 崔开潮</p>
  17. <p>作曲 : 崔开潮</p>
  18. <p>青砖伴瓦漆</p>
  19. <p>白马踏新泥</p>
  20. <p>山花蕉叶暮色丛染红巾</p>
  21. <p>屋檐洒雨滴</p>
  22. <p>炊烟袅袅起</p>
  23. <p>蹉跎辗转宛然的你在哪里</p>
  24. <p>寻寻觅觅</p>
  25. <p>冷冷清清</p>
  26. <p>月落乌啼月牙落孤井</p>
  27. <p>零零碎碎</p>
  28. <p>点点滴滴</p>
  29. <p>梦里有花梦里青草地</p>
  30. <p>长发引涟漪</p>
  31. <p>白布展石矶</p>
  32. <p>河童撑杆摆长舟渡古稀</p>
  33. <p>屋檐洒雨滴</p>
  34. <p>炊烟袅袅起</p>
  35. <p>蹉跎辗转宛然的你在哪里</p>
  36. <p>寻寻觅觅</p>
  37. <p>冷冷清清</p>
  38. <p>月落乌啼月牙落孤井</p>
  39. <p>零零碎碎</p>
  40. <p>点点滴滴</p>
  41. <p>梦里有花梦里青草地</p>
  42. <p>青砖伴瓦漆</p>
  43. <p>白马踏新泥</p>
  44. <p>山花蕉叶暮色丛染红巾</p>
  45. <p>屋檐洒雨滴</p>
  46. <p>炊烟袅袅起</p>
  47. <p>蹉跎辗转宛然的你在哪里</p>

  48.       <span style="opacity:0.5;">—— 完 ——</span>
  49.     </marquee>
  50.   </div>

  51.   <!-- ========== 底部:原生音频控件 ========== -->
  52.   <div style="padding:12px 14px;background:rgba(0,0,0,0.25);">
  53.     <audio src="https://www.joy127.com/url/158097.mp3" controls="controls" loop="loop" autoplay preload="metadata" style="width:100%;height:44px;outline:none;border-radius:6px;"></audio>
  54.   </div>

  55.   <!-- 底部小提示条 -->
  56.   <div style="text-align:center;font-size:11px;opacity:0.55;padding:6px 0 10px;letter-spacing:1px;">
  57.     点击播放器 ▶ 按钮开始播放
  58.   </div>
  59. </div>
复制代码



手动滚动歌词
  1. <div style="width:100%;max-width:540px;margin:10px auto;border:1px solid #ddd;border-radius:14px;overflow:hidden;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);box-shadow:0 6px 20px rgba(0,0,0,0.15);font-family:'Microsoft YaHei','PingFang SC',Arial,sans-serif;color:#fff;font-size:14px;">

  2.   <div style="display:flex;align-items:center;padding:16px;background:rgba(0,0,0,0.18);">
  3.     <img src="你的封面图片URL.jpg" alt="专辑封面" style="width:84px;height:84px;border-radius:10px;object-fit:cover;border:2px solid rgba(255,255,255,0.35);margin-right:16px;flex-shrink:0;" onerror="this.style.display='none';">
  4.     <div style="flex:1;min-width:0;">
  5.       <div style="font-size:19px;font-weight:bold;margin-bottom:6px;letter-spacing:0.5px;">♫ 声声慢</div>
  6.       <div style="font-size:13px;opacity:0.8;margin-bottom:4px;">演唱:韩菁英</div>
  7.       <div style="font-size:12px;opacity:0.6;">专辑:最美女声</div>
  8.     </div>
  9.   </div>

  10.   <!-- 手动滚动歌词区:overflow:auto 自动生成滚动条 -->
  11.   <div style="padding:14px 16px;background:rgba(255,255,255,0.08);">
  12.     <div style="text-align:center;font-size:12px;opacity:0.7;margin-bottom:10px;letter-spacing:2px;">━━━ 歌  词(鼠标滚轮滑动) ━━━</div>
  13.     <div style="height:180px;overflow-y:auto;overflow-x:hidden;text-align:center;line-height:2.1;font-size:15px;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,0.3);scrollbar-width:thin;scrollbar-color:rgba(255,255,255,0.4) transparent;">
  14. <p>作词 : 崔开潮</p>
  15. <p>作曲 : 崔开潮</p>
  16. <p>青砖伴瓦漆</p>
  17. <p>白马踏新泥</p>
  18. <p>山花蕉叶暮色丛染红巾</p>
  19. <p>屋檐洒雨滴</p>
  20. <p>炊烟袅袅起</p>
  21. <p>蹉跎辗转宛然的你在哪里</p>
  22. <p>寻寻觅觅</p>
  23. <p>冷冷清清</p>
  24. <p>月落乌啼月牙落孤井</p>
  25. <p>零零碎碎</p>
  26. <p>点点滴滴</p>
  27. <p>梦里有花梦里青草地</p>
  28. <p>长发引涟漪</p>
  29. <p>白布展石矶</p>
  30. <p>河童撑杆摆长舟渡古稀</p>
  31. <p>屋檐洒雨滴</p>
  32. <p>炊烟袅袅起</p>
  33. <p>蹉跎辗转宛然的你在哪里</p>
  34. <p>寻寻觅觅</p>
  35. <p>冷冷清清</p>
  36. <p>月落乌啼月牙落孤井</p>
  37. <p>零零碎碎</p>
  38. <p>点点滴滴</p>
  39. <p>梦里有花梦里青草地</p>
  40. <p>青砖伴瓦漆</p>
  41. <p>白马踏新泥</p>
  42. <p>山花蕉叶暮色丛染红巾</p>
  43. <p>屋檐洒雨滴</p>
  44. <p>炊烟袅袅起</p>
  45. <p>蹉跎辗转宛然的你在哪里</p>
  46.       <span style="opacity:0.5;">—— 完 ——</span>
  47.     </div>
  48.   </div>

  49.   <div style="padding:12px 14px;background:rgba(0,0,0,0.25);">
  50.     <audio src="https://www.joy127.com/url/158097.mp3" controls="controls" loop="loop" autoplay preload="metadata" style="width:100%;height:44px;outline:none;border-radius:6px;"></audio>
  51.   </div>

  52.   <div style="text-align:center;font-size:11px;opacity:0.55;padding:6px 0 10px;letter-spacing:1px;">
  53.     点击播放器 ▶ 按钮开始播放
  54.   </div>
  55. </div>
复制代码
 楼主| 发表于 2026-8-26 10:19:40 |香港| 显示全部楼层
非常漂亮的多歌列表播放器
  1. <style type="text/css">
  2. /* ========== 外层容器(不影响外部页面) ========== */
  3. .audio-playlist-container {
  4.     max-width: 800px;
  5.     margin: 15px auto;
  6.     font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
  7.     background: #1a1a1a;
  8.     border-radius: 10px;
  9.     overflow: hidden;
  10.     box-shadow: 0 10px 40px rgba(0,0,0,0.5);
  11.     color: #fff;
  12.     box-sizing: border-box;
  13. }
  14. /* 容器内局部 reset —— 只影响播放器内部,不会改到论坛页面 */
  15. .audio-playlist-container, .audio-playlist-container * {
  16.     box-sizing: border-box;
  17. }
  18. .audio-playlist-container * {
  19.     margin: 0;
  20.     padding: 0;
  21. }

  22. /* ===== 主播放区(替代视频画面) ===== */
  23. .audio-playlist-container .audio-main {
  24.     position: relative;
  25.     background: linear-gradient(135deg, #1a1a1a 0%, #0d0d0d 100%);
  26.     padding: 30px 20px 20px;
  27.     min-height: 280px;
  28.     display: flex;
  29.     flex-direction: column;
  30.     align-items: center;
  31.     justify-content: center;
  32.     overflow: hidden;
  33. }
  34. /* 背景柔光 */
  35. .audio-playlist-container .audio-main::before {
  36.     content: "";
  37.     position: absolute;
  38.     width: 300px; height: 300px;
  39.     background: radial-gradient(circle, rgba(231,76,60,0.25) 0%, transparent 70%);
  40.     top: 50%; left: 50%;
  41.     transform: translate(-50%, -50%);
  42.     pointer-events: none;
  43. }
  44. .audio-playlist-container .audio-info-bar {
  45.     position: absolute;
  46.     top: 0; left: 0; right: 0;
  47.     background: linear-gradient(to bottom, rgba(0,0,0,0.75), transparent);
  48.     padding: 15px 20px;
  49.     z-index: 10;
  50. }
  51. .audio-playlist-container .audio-title {
  52.     font-size: 16px;
  53.     font-weight: bold;
  54.     margin-bottom: 5px;
  55.     text-shadow: 0 1px 3px rgba(0,0,0,0.6);
  56. }
  57. .audio-playlist-container .audio-index {
  58.     font-size: 12px;
  59.     opacity: 0.8;
  60. }

  61. /* 唱片封面 */
  62. .audio-playlist-container .disc-wrap {
  63.     position: relative;
  64.     width: 180px;
  65.     height: 180px;
  66.     margin-top: 20px;
  67.     z-index: 2;
  68. }
  69. .audio-playlist-container .disc {
  70.     width: 100%;
  71.     height: 100%;
  72.     border-radius: 50%;
  73.     background:
  74.         radial-gradient(circle at center, #e74c3c 0 18%, #111 18.5% 22%, #222 22.5% 50%, #111 50.5% 52%, #222 52.5% 100%);
  75.     box-shadow: 0 8px 30px rgba(0,0,0,0.7), inset 0 0 20px rgba(0,0,0,0.8);
  76.     position: relative;
  77.     animation: apc-spin 8s linear infinite;
  78.     animation-play-state: paused;
  79. }
  80. .audio-playlist-container .disc::after {
  81.     content: "♪";
  82.     position: absolute;
  83.     top: 50%; left: 50%;
  84.     transform: translate(-50%, -50%);
  85.     color: #fff;
  86.     font-size: 28px;
  87.     text-shadow: 0 0 10px rgba(255,255,255,0.3);
  88. }
  89. .audio-playlist-container .disc.playing { animation-play-state: running; }
  90. @keyframes apc-spin {
  91.     from { transform: rotate(0deg); }
  92.     to   { transform: rotate(360deg); }
  93. }
  94. /* 唱针 */
  95. .audio-playlist-container .needle {
  96.     position: absolute;
  97.     top: -10px; right: 10px;
  98.     width: 60px; height: 90px;
  99.     z-index: 3;
  100.     transform-origin: top right;
  101.     transform: rotate(-25deg);
  102.     transition: transform 0.4s;
  103. }
  104. .audio-playlist-container .needle.playing { transform: rotate(-5deg); }
  105. .audio-playlist-container .needle::before {
  106.     content: "";
  107.     position: absolute;
  108.     top: 0; right: 0;
  109.     width: 14px; height: 14px;
  110.     border-radius: 50%;
  111.     background: #666;
  112.     border: 2px solid #888;
  113. }
  114. .audio-playlist-container .needle::after {
  115.     content: "";
  116.     position: absolute;
  117.     top: 8px; right: 6px;
  118.     width: 3px; height: 75px;
  119.     background: linear-gradient(to bottom, #999, #ccc 30%, #555 100%);
  120.     border-radius: 2px;
  121.     transform: rotate(15deg);
  122.     transform-origin: top;
  123. }

  124. /* 音频波形 */
  125. .audio-playlist-container .waveform {
  126.     display: flex;
  127.     align-items: center;
  128.     gap: 3px;
  129.     height: 32px;
  130.     margin-top: 18px;
  131.     z-index: 2;
  132. }
  133. .audio-playlist-container .waveform span {
  134.     display: block;
  135.     width: 4px;
  136.     background: linear-gradient(to top, #c0392b, #e74c3c);
  137.     border-radius: 2px;
  138.     height: 6px;
  139.     transition: height 0.15s;
  140. }
  141. .audio-playlist-container .waveform.playing span {
  142.     animation: apc-wave 1s ease-in-out infinite;
  143. }
  144. .audio-playlist-container .waveform span:nth-child(1) { animation-delay: 0s; }
  145. .audio-playlist-container .waveform span:nth-child(2) { animation-delay: 0.1s; }
  146. .audio-playlist-container .waveform span:nth-child(3) { animation-delay: 0.2s; }
  147. .audio-playlist-container .waveform span:nth-child(4) { animation-delay: 0.3s; }
  148. .audio-playlist-container .waveform span:nth-child(5) { animation-delay: 0.4s; }
  149. .audio-playlist-container .waveform span:nth-child(6) { animation-delay: 0.3s; }
  150. .audio-playlist-container .waveform span:nth-child(7) { animation-delay: 0.2s; }
  151. .audio-playlist-container .waveform span:nth-child(8) { animation-delay: 0.1s; }
  152. .audio-playlist-container .waveform span:nth-child(9) { animation-delay: 0s; }
  153. @keyframes apc-wave {
  154.     0%, 100% { height: 6px; }
  155.     50%      { height: 28px; }
  156. }

  157. /* audio 元素 */
  158. .audio-playlist-container .audio-main audio {
  159.     width: 100%;
  160.     margin-top: 18px;
  161.     z-index: 2;
  162.     outline: none;
  163. }
  164. .audio-playlist-container .audio-main audio::-webkit-media-controls-panel {
  165.     background-color: #2a2a2a;
  166. }

  167. /* ===== 控制栏 ===== */
  168. .audio-playlist-container .audio-controls-bar {
  169.     display: flex;
  170.     justify-content: space-between;
  171.     align-items: center;
  172.     padding: 10px 15px;
  173.     background: #222;
  174.     color: #fff;
  175.     flex-wrap: wrap;
  176.     gap: 8px;
  177. }
  178. .audio-playlist-container .control-group {
  179.     display: flex;
  180.     gap: 10px;
  181.     align-items: center;
  182. }
  183. .audio-playlist-container .ctrl-btn {
  184.     background: #444;
  185.     border: none;
  186.     color: #fff;
  187.     padding: 6px 12px;
  188.     border-radius: 4px;
  189.     cursor: pointer;
  190.     font-size: 12px;
  191.     font-family: inherit;
  192.     transition: background 0.2s;
  193.     line-height: 1.4;
  194. }
  195. .audio-playlist-container .ctrl-btn:hover { background: #666; }
  196. .audio-playlist-container .ctrl-btn.active { background: #e74c3c; }

  197. /* ===== 播放列表 ===== */
  198. .audio-playlist-container .playlist {
  199.     max-height: 500px;
  200.     overflow-y: auto;
  201.     background: #1a1a1a;
  202. }
  203. /* 局部滚动条 —— 只改播放器内部列表,不影响论坛页面滚动条 */
  204. .audio-playlist-container .playlist::-webkit-scrollbar { width: 6px; }
  205. .audio-playlist-container .playlist::-webkit-scrollbar-track { background: #1a1a1a; }
  206. .audio-playlist-container .playlist::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }

  207. .audio-playlist-container .playlist-item {
  208.     display: flex;
  209.     align-items: center;
  210.     padding: 12px 15px;
  211.     border-bottom: 1px solid #333;
  212.     cursor: pointer;
  213.     transition: all 0.2s;
  214.     color: #ccc;
  215. }
  216. .audio-playlist-container .playlist-item:hover { background: #2a2a2a; }
  217. .audio-playlist-container .playlist-item.playing {
  218.     background: linear-gradient(90deg, #c0392b 0%, #e74c3c 100%);
  219.     color: #fff;
  220. }
  221. .audio-playlist-container .playlist-item.played { opacity: 0.6; }
  222. .audio-playlist-container .item-index {
  223.     width: 28px;
  224.     height: 28px;
  225.     border-radius: 50%;
  226.     background: #333;
  227.     display: flex;
  228.     align-items: center;
  229.     justify-content: center;
  230.     font-size: 12px;
  231.     margin-right: 12px;
  232.     flex-shrink: 0;
  233. }
  234. .audio-playlist-container .playlist-item.playing .item-index {
  235.     background: #fff;
  236.     color: #e74c3c;
  237. }
  238. .audio-playlist-container .item-info { flex: 1; min-width: 0; }
  239. .audio-playlist-container .item-title {
  240.     font-size: 14px;
  241.     margin-bottom: 3px;
  242.     white-space: nowrap;
  243.     overflow: hidden;
  244.     text-overflow: ellipsis;
  245. }
  246. .audio-playlist-container .item-duration {
  247.     font-size: 11px;
  248.     opacity: 0.7;
  249. }
  250. .audio-playlist-container .playing-indicator {
  251.     margin-left: 10px;
  252.     font-size: 12px;
  253.     flex-shrink: 0;
  254. }

  255. @media (max-width: 600px) {
  256.     .audio-playlist-container .audio-title { font-size: 14px; }
  257.     .audio-playlist-container .playlist-item { padding: 10px; }
  258.     .audio-playlist-container .disc-wrap { width: 140px; height: 140px; margin-top: 30px; }
  259.     .audio-playlist-container .audio-main { min-height: 260px; padding-top: 40px; }
  260.     .audio-playlist-container .ctrl-btn { padding: 6px 10px; font-size: 12px; }
  261. }
  262. </style>

  263. <div class="audio-playlist-container">
  264.     <div class="audio-main">
  265.         <div class="audio-info-bar">
  266.             <div class="audio-title" id="audioTitle">个人收藏的单曲合集</div>
  267.             <div class="audio-index" id="audioIndex">第 1 首 / 共 N 首</div>
  268.         </div>

  269.         <div class="disc-wrap">
  270.             <div class="needle" id="needle"></div>
  271.             <div class="disc" id="disc"></div>
  272.         </div>

  273.         <div class="waveform" id="waveform">
  274.             <span></span><span></span><span></span><span></span><span></span>
  275.             <span></span><span></span><span></span><span></span>
  276.         </div>

  277.         <audio id="mainAudio" controls preload="metadata" playsinline webkit-playsinline>
  278.             您的浏览器不支持HTML5音频播放,请升级浏览器。
  279.         </audio>
  280.     </div>

  281.     <div class="audio-controls-bar">
  282.         <div class="control-group">
  283.             <button class="ctrl-btn" onclick="prevAudio()">⏮ 上一首</button>
  284.             <button class="ctrl-btn" onclick="nextAudio()">下一首 ⏭</button>
  285.         </div>
  286.         <div class="control-group">
  287.             <button class="ctrl-btn active" id="autoPlayBtn" onclick="toggleAutoPlay()">🔁 连播: 开</button>
  288.             <button class="ctrl-btn" id="loopBtn" onclick="toggleLoop()">🔂 循环: 关</button>
  289.         </div>
  290.     </div>

  291.     <div class="playlist" id="playlist"></div>
  292. </div>

  293. <script type="text/javascript">
  294. // ======== 音频列表配置(替换为你自己的音频地址)========
  295. var audioList = [
  296.     {
  297.         title: "第一首:笑纳 - 花僮",
  298.         url: "https://www.joy127.com/url/158119.mp3",
  299.         duration: ""
  300.     },
  301.     {
  302.         title: "第二首:浪子闲话 - 花僮",
  303.         url: "https://www.joy127.com/url/158118.mp3",
  304.         duration: ""
  305.     },
  306.     {
  307.         title: "第三首:秒针 - 阿梨粤",
  308.         url: "https://www.joy127.com/url/158117.mp3",
  309.         duration: ""
  310.     },
  311.     {
  312.         title: "第四首:精忠报国 - 屠龙刚",
  313.         url: "https://www.joy127.com/url/158116.mp3",
  314.         duration: ""
  315.     },
  316.     {
  317.         title: "第五首:赤伶 - 谭晶",
  318.         url: "https://www.joy127.com/url/158115.mp3",
  319.         duration: ""
  320.     },
  321.     {
  322.         title: "第六首:客官不可以 - 麦小兜",
  323.         url: "https://www.joy127.com/url/158114.mp3",
  324.         duration: ""
  325.     },
  326.     {
  327.         title: "第七首:小道童 - 麦小兜",
  328.         url: "https://www.joy127.com/url/158113.mp3",
  329.         duration: ""
  330.     },
  331.     {
  332.         title: "第八首:情字最大 - 浅影阿",
  333.         url: "https://www.joy127.com/url/158112.mp3",
  334.         duration: ""
  335.     },
  336.     {
  337.         title: "第九首:万疆 - 李玉刚",
  338.         url: "https://www.joy127.com/url/158111.mp3",
  339.         duration: ""
  340.     },
  341.     {
  342.         title: "第十首:处处吻 - 杨千嬅",
  343.         url: "https://www.joy127.com/url/158110.mp3",
  344.         duration: ""
  345.     },
  346.     {
  347.         title: "第十一首:声声慢 - 韩菁英(最美女声)",
  348.         url: "https://www.joy127.com/url/158097.mp3",
  349.         duration: ""
  350.     },
  351.     {
  352.         title: "第十二首:声声慢 - 吴亮莹(吴侬软语)",
  353.         url: "https://www.joy127.com/url/158096.mp3",
  354.         duration: ""
  355.     },
  356.     {
  357.         title: "第三首:音频标题写这里",
  358.         url: "",
  359.         duration: ""
  360.     },
  361.     // 按此格式继续添加更多音频即可
  362. ];
  363. // ========================================================

  364. (function(){
  365.     var currentIndex = 0;
  366.     var autoPlayNext = true;
  367.     var loopAll = false;
  368.     var audio = document.getElementById('mainAudio');
  369.     var playlistEl = document.getElementById('playlist');
  370.     var disc = document.getElementById('disc');
  371.     var needle = document.getElementById('needle');
  372.     var waveform = document.getElementById('waveform');

  373.     function setPlayingVisual(isPlaying) {
  374.         if (isPlaying) {
  375.             disc.classList.add('playing');
  376.             needle.classList.add('playing');
  377.             waveform.classList.add('playing');
  378.         } else {
  379.             disc.classList.remove('playing');
  380.             needle.classList.remove('playing');
  381.             waveform.classList.remove('playing');
  382.         }
  383.     }

  384.     function formatTime(sec) {
  385.         if (!sec || isNaN(sec)) return '';
  386.         var m = Math.floor(sec / 60);
  387.         var s = Math.floor(sec % 60);
  388.         return m + ':' + (s < 10 ? '0' + s : s);
  389.     }

  390.     function initPlaylist() {
  391.         playlistEl.innerHTML = '';
  392.         for (var i = 0; i < audioList.length; i++) {
  393.             var item = document.createElement('div');
  394.             item.className = 'playlist-item' + (i === 0 ? ' playing' : '');
  395.             item.setAttribute('data-index', i);
  396.             item.innerHTML =
  397.                 '<div class="item-index">' + (i + 1) + '</div>' +
  398.                 '<div class="item-info">' +
  399.                     '<div class="item-title">' + audioList[i].title + '</div>' +
  400.                     '<div class="item-duration" data-dur-idx="' + i + '">' + (audioList[i].duration || '--:--') + '</div>' +
  401.                 '</div>' +
  402.                 '<div class="playing-indicator">' + (i === 0 ? '▶ 播放中' : '') + '</div>';
  403.             item.onclick = (function(idx) {
  404.                 return function() { playAudio(idx); };
  405.             })(i);
  406.             playlistEl.appendChild(item);
  407.         }
  408.     }

  409.     function updatePlaylistUI() {
  410.         var items = playlistEl.querySelectorAll('.playlist-item');
  411.         for (var i = 0; i < items.length; i++) {
  412.             items[i].className = 'playlist-item';
  413.             var indicator = items[i].querySelector('.playing-indicator');
  414.             indicator.textContent = '';
  415.             if (i < currentIndex) items[i].className += ' played';
  416.             if (i === currentIndex) {
  417.                 items[i].className += ' playing';
  418.                 indicator.textContent = '▶ 播放中';
  419.             }
  420.         }
  421.     }

  422.     function playAudio(index) {
  423.         if (index < 0) index = loopAll ? audioList.length - 1 : 0;
  424.         if (index >= audioList.length) index = loopAll ? 0 : audioList.length - 1;
  425.         currentIndex = index;
  426.         var item = audioList[index];
  427.         audio.src = item.url;
  428.         audio.load();
  429.         audio.play().then(function() {
  430.             setPlayingVisual(true);
  431.         }).catch(function(e) {
  432.             setPlayingVisual(false);
  433.         });
  434.         document.getElementById('audioTitle').textContent = item.title;
  435.         document.getElementById('audioIndex').textContent = '第 ' + (index + 1) + ' 首 / 共 ' + audioList.length + ' 首';
  436.         updatePlaylistUI();
  437.     }

  438.     window.nextAudio = function() {
  439.         if (currentIndex < audioList.length - 1) playAudio(currentIndex + 1);
  440.         else if (loopAll) playAudio(0);
  441.     };
  442.     window.prevAudio = function() {
  443.         if (currentIndex > 0) playAudio(currentIndex - 1);
  444.         else if (loopAll) playAudio(audioList.length - 1);
  445.     };
  446.     window.toggleAutoPlay = function() {
  447.         autoPlayNext = !autoPlayNext;
  448.         var btn = document.getElementById('autoPlayBtn');
  449.         btn.textContent = autoPlayNext ? '🔁 连播: 开' : '🔁 连播: 关';
  450.         btn.className = 'ctrl-btn' + (autoPlayNext ? ' active' : '');
  451.     };
  452.     window.toggleLoop = function() {
  453.         loopAll = !loopAll;
  454.         var btn = document.getElementById('loopBtn');
  455.         btn.textContent = loopAll ? '🔂 循环: 开' : '🔂 循环: 关';
  456.         btn.className = 'ctrl-btn' + (loopAll ? ' active' : '');
  457.     };

  458.     audio.addEventListener('play', function() { setPlayingVisual(true); });
  459.     audio.addEventListener('pause', function() { setPlayingVisual(false); });
  460.     audio.addEventListener('ended', function() {
  461.         setPlayingVisual(false);
  462.         if (autoPlayNext) {
  463.             if (currentIndex < audioList.length - 1) nextAudio();
  464.             else if (loopAll) playAudio(0);
  465.         }
  466.     });
  467.     audio.addEventListener('loadedmetadata', function() {
  468.         var durEl = playlistEl.querySelector('[data-dur-idx="' + currentIndex + '"]');
  469.         if (durEl && (!audioList[currentIndex].duration)) {
  470.             var t = formatTime(audio.duration);
  471.             durEl.textContent = t;
  472.             audioList[currentIndex].duration = t;
  473.         }
  474.     });

  475.     initPlaylist();
  476.     playAudio(0);
  477. })();
  478. </script>
复制代码
 楼主| 发表于 2026-8-28 06:29:09 |香港| 显示全部楼层
不带歌词版的单曲播发器
  1. <!--
  2.   Discuz 论坛发帖专用 - 现代化玻璃拟态音乐播放器
  3.   使用说明:
  4.   1. 复制以下所有代码。
  5.   2. 在 Discuz 发帖编辑器中切换到“纯文本/HTML源码”模式。
  6.   3. 替换 src 中的音频链接和 img 中的封面链接。
  7.   4. 确保论坛权限允许发布 HTML 代码。
  8. -->

  9. <div style="
  10.   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  11.   width: 320px;
  12.   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  13.   border-radius: 20px;
  14.   padding: 20px;
  15.   box-shadow: 0 10px 30px rgba(0,0,0,0.2);
  16.   color: white;
  17.   margin: 20px auto;
  18.   position: relative;
  19.   overflow: hidden;
  20.   text-align: center;
  21. ">

  22.   <!-- 背景装饰光斑 -->
  23.   <div style="
  24.     position: absolute;
  25.     top: -50%;
  26.     left: -50%;
  27.     width: 200%;
  28.     height: 200%;
  29.     background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 60%);
  30.     pointer-events: none;
  31.     z-index: 0;
  32.   "></div>

  33.   <!-- 内容区域 -->
  34.   <div style="position: relative; z-index: 1;">
  35.    
  36.     <!-- 唱片封面容器 -->
  37.     <div style="
  38.       width: 180px;
  39.       height: 180px;
  40.       margin: 0 auto 20px;
  41.       border-radius: 50%;
  42.       border: 4px solid rgba(255,255,255,0.2);
  43.       box-shadow: 0 8px 20px rgba(0,0,0,0.3);
  44.       overflow: hidden;
  45.       position: relative;
  46.       animation: rotate-disc 10s linear infinite;
  47.       /* 默认暂停动画,播放时通过JS添加 class 或手动控制较难,这里使用CSS常转模拟黑胶效果,
  48.          若需精确控制播放暂停动画,需JS支持,但Discuz限制JS,故采用视觉常驻旋转或静态美图为宜。
  49.          此处为了美观,设为缓慢旋转,若介意可删除 animation 属性 -->
  50.       animation-play-state: paused;
  51.     " id="disc-cover">
  52.       <!-- 封面图片:请替换下面的 src -->
  53.       <img src="https://i.ibb.co/nN5s9FYn/CG.jpg" alt="音乐封面" style="
  54.         width: 100%;
  55.         height: 100%;
  56.         object-fit: cover;
  57.         display: block;
  58.       ">
  59.       <!-- 中心圆点 -->
  60.       <div style="
  61.         position: absolute;
  62.         top: 50%;
  63.         left: 50%;
  64.         transform: translate(-50%, -50%);
  65.         width: 30px;
  66.         height: 30px;
  67.         background: #2d3436;
  68.         border-radius: 50%;
  69.         border: 2px solid rgba(255,255,255,0.5);
  70.       "></div>
  71.     </div>

  72.     <!-- 歌曲信息 -->
  73.     <h3 style="
  74.       margin: 0 0 5px;
  75.       font-size: 18px;
  76.       font-weight: 600;
  77.       text-shadow: 0 2px 4px rgba(0,0,0,0.2);
  78.       white-space: nowrap;
  79.       overflow: hidden;
  80.       text-overflow: ellipsis;
  81.     ">Simpa Pa Pa</h3>
  82.     <p style="
  83.       margin: 0 0 20px;
  84.       font-size: 14px;
  85.       opacity: 0.8;
  86.       font-weight: 300;
  87.     ">Yefium </p>

  88.     <!-- 自定义控制按钮区域 (视觉层) -->
  89.     <div style="
  90.       display: flex;
  91.       justify-content: center;
  92.       align-items: center;
  93.       gap: 20px;
  94.       margin-bottom: 15px;
  95.     ">
  96.       <!-- 播放/暂停按钮视觉提示 (实际功能由下方 audio controls 提供,此处仅为美化引导) -->
  97.       <div style="
  98.         font-size: 12px;
  99.         opacity: 0.6;
  100.         background: rgba(0,0,0,0.2);
  101.         padding: 4px 10px;
  102.         border-radius: 12px;
  103.       ">点击下方原生控件播放</div>
  104.     </div>

  105.     <!-- 核心音频标签 -->
  106.     <!--
  107.       controls: 显示浏览器原生控件(最稳定兼容 Discuz)
  108.       preload: 预加载元数据
  109.       注意:为了保持 UI 统一,我们可以尝试隐藏原生控件的大部分内容,只保留进度条,
  110.       但在不同浏览器下表现不一。最稳妥的“好看”方式是保留原生控件,但通过 CSS 美化容器。
  111.       
  112.       进阶技巧:如果 Discuz 允许少量 JS,可以添加 onclick 事件控制播放并旋转封面。
  113.       下面代码包含一段简单的内联 JS 尝试控制封面旋转(视论坛过滤规则而定,若被过滤则仅作为静态播放器)。
  114.     -->
  115.     <audio id="my-audio" controls autoplay loop preload="metadata" style="
  116.       width: 100%;
  117.       height: 40px;
  118.       outline: none;
  119.       filter: invert(1) hue-rotate(180deg); /* 简单反转颜色以适配深色背景,视浏览器支持情况 */
  120.       border-radius: 20px;
  121.       background: rgba(255,255,255,0.1);
  122.     "
  123.     onplay="document.getElementById('disc-cover').style.animationPlayState='running'"
  124.     onpause="document.getElementById('disc-cover').style.animationPlayState='paused'"
  125.     >
  126.       <!-- 请替换为你的音频文件直链 -->
  127.       <source src="https://www.joy127.com/url/158258.mp3" type="audio/mpeg">
  128.       您的浏览器不支持 audio 元素。
  129.     </audio>

  130.   </div>

  131.   <!-- 内部样式定义 (部分论坛可能过滤 style 标签,若被过滤,以上内联样式已足够) -->
  132.   <style>
  133.     @keyframes rotate-disc {
  134.       from { transform: rotate(0deg); }
  135.       to { transform: rotate(360deg); }
  136.     }
  137.     /* 针对 Webkit 浏览器的音频控件微调 */
  138.     audio::-webkit-media-controls-panel {
  139.       background-color: rgba(255, 255, 255, 0.2);
  140.     }
  141.     audio::-webkit-media-controls-current-time-display,
  142.     audio::-webkit-media-controls-time-remaining-display {
  143.       color: white;
  144.     }
  145.   </style>
  146. </div>
复制代码
 楼主| 发表于 2026-8-28 19:30:19 |香港| 显示全部楼层
B站多视频列表播放器
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>B站多视频播放器</title>
  7.     <style>
  8.         /* 全局样式重置与基础设定 */
  9.         .bili-player-container {
  10.             font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  11.             max-width: 800px;
  12.             margin: 20px auto;
  13.             background-color: #f4f5f7;
  14.             border-radius: 8px;
  15.             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  16.             overflow: hidden;
  17.             border: 1px solid #e3e5e7;
  18.         }

  19.         /* 视频显示区域 */
  20.         .video-stage {
  21.             position: relative;
  22.             width: 100%;
  23.             padding-bottom: 56.25%; /* 16:9 比例 */
  24.             background-color: #000;
  25.             height: 0;
  26.         }

  27.         .video-stage iframe {
  28.             position: absolute;
  29.             top: 0;
  30.             left: 0;
  31.             width: 100%;
  32.             height: 100%;
  33.             border: none;
  34.         }

  35.         /* 播放列表区域 */
  36.         .playlist-area {
  37.             padding: 15px;
  38.             background-color: #fff;
  39.         }

  40.         .playlist-header {
  41.             font-size: 16px;
  42.             font-weight: bold;
  43.             color: #333;
  44.             margin-bottom: 10px;
  45.             padding-bottom: 10px;
  46.             border-bottom: 1px solid #eee;
  47.             display: flex;
  48.             justify-content: space-between;
  49.             align-items: center;
  50.         }

  51.         .playlist-count {
  52.             font-size: 12px;
  53.             color: #999;
  54.             font-weight: normal;
  55.         }

  56.         /* 列表容器 */
  57.         .video-list {
  58.             list-style: none;
  59.             padding: 0;
  60.             margin: 0;
  61.             max-height: 300px;
  62.             overflow-y: auto;
  63.         }

  64.         /* 滚动条样式优化 */
  65.         .video-list::-webkit-scrollbar {
  66.             width: 6px;
  67.         }
  68.         .video-list::-webkit-scrollbar-thumb {
  69.             background-color: #ccc;
  70.             border-radius: 3px;
  71.         }

  72.         /* 单个视频项 */
  73.         .video-item {
  74.             display: flex;
  75.             align-items: center;
  76.             padding: 10px;
  77.             cursor: pointer;
  78.             transition: background-color 0.2s ease;
  79.             border-radius: 4px;
  80.             margin-bottom: 5px;
  81.         }

  82.         .video-item:hover {
  83.             background-color: #f0f2f5;
  84.         }

  85.         .video-item.active {
  86.             background-color: #e7f0ff;
  87.             border-left: 4px solid #00a1d6;
  88.         }

  89.         .video-item.active .video-title {
  90.             color: #00a1d6;
  91.             font-weight: 600;
  92.         }

  93.         /* 序号 */
  94.         .item-index {
  95.             width: 24px;
  96.             height: 24px;
  97.             background-color: #999;
  98.             color: #fff;
  99.             border-radius: 50%;
  100.             display: flex;
  101.             align-items: center;
  102.             justify-content: center;
  103.             font-size: 12px;
  104.             margin-right: 10px;
  105.             flex-shrink: 0;
  106.         }

  107.         .video-item.active .item-index {
  108.             background-color: #00a1d6;
  109.         }

  110.         /* 标题 */
  111.         .video-title {
  112.             font-size: 14px;
  113.             color: #333;
  114.             line-height: 1.4;
  115.             overflow: hidden;
  116.             text-overflow: ellipsis;
  117.             white-space: nowrap;
  118.             flex-grow: 1;
  119.         }

  120.         /* 提示信息 */
  121.         .tips {
  122.             font-size: 12px;
  123.             color: #999;
  124.             margin-top: 10px;
  125.             text-align: right;
  126.         }
  127.     </style>
  128. </head>
  129. <body>

  130. <div class="bili-player-container">
  131.     <!-- 视频播放舞台 -->
  132.     <div class="video-stage">
  133.         <iframe id="mainPlayer" src="" allowfullscreen="true" allow="autoplay; encrypted-media"></iframe>
  134.     </div>

  135.     <!-- 播放列表 -->
  136.     <div class="playlist-area">
  137.         <div class="playlist-header">
  138.             <span>精选视频列表</span>
  139.             <span class="playlist-count" id="videoCount">共 0 个视频</span>
  140.         </div>
  141.         <ul class="video-list" id="videoList">
  142.             <!-- 列表项将通过 JS 动态生成 -->
  143.         </ul>
  144.         <div class="tips">点击列表即可切换视频</div>
  145.     </div>
  146. </div>

  147. <script>
  148.     (function() {
  149.         // ================= 配置区域 =================
  150.         // 请在此处修改您的 B站视频 BV 号或 AV 号以及标题
  151.         // bvid: B站视频的 BV 号 (推荐)
  152.         // title: 显示在列表中的标题
  153.         const videoData = [
  154.             {
  155.                 bvid: "BV1GJ411x7h7",
  156.                 title: "示例视频1:B站首页宣传片"
  157.             },
  158.             {
  159.                 bvid: "BV1xx411c7mD",
  160.                 title: "示例视频2:如何制作高质量视频"
  161.             },
  162.             {
  163.                 bvid: "BV1Ft411k7P6",
  164.                 title: "示例视频3:Discuz论坛使用教程"
  165.             },
  166.             {
  167.                 bvid: "BV1Kb411W75N",
  168.                 title: "示例视频4:Web前端开发基础"
  169.             }
  170.         ];
  171.         // ===========================================

  172.         const playerIframe = document.getElementById('mainPlayer');
  173.         const listContainer = document.getElementById('videoList');
  174.         const countLabel = document.getElementById('videoCount');
  175.         let currentIndex = 0;

  176.         // 初始化播放器
  177.         function initPlayer() {
  178.             if (!videoData || videoData.length === 0) {
  179.                 listContainer.innerHTML = '<li style="padding:10px;color:#999;text-align:center;">暂无视频数据</li>';
  180.                 return;
  181.             }

  182.             countLabel.textContent = `共 ${videoData.length} 个视频`;
  183.             renderList();
  184.             loadVideo(0);
  185.         }

  186.         // 渲染列表
  187.         function renderList() {
  188.             listContainer.innerHTML = '';
  189.             videoData.forEach((video, index) => {
  190.                 const li = document.createElement('li');
  191.                 li.className = 'video-item';
  192.                 li.dataset.index = index;
  193.                
  194.                 // 构建 HTML 结构
  195.                 li.innerHTML = `
  196.                     <div class="item-index">${index + 1}</div>
  197.                     <div class="video-title" title="${video.title}">${video.title}</div>
  198.                 `;

  199.                 // 点击事件
  200.                 li.addEventListener('click', function() {
  201.                     loadVideo(index);
  202.                 });

  203.                 listContainer.appendChild(li);
  204.             });
  205.         }

  206.         // 加载指定索引的视频
  207.         function loadVideo(index) {
  208.             if (index < 0 || index >= videoData.length) return;

  209.             currentIndex = index;
  210.             const video = videoData[index];
  211.             
  212.             // 更新 iframe 源地址
  213.             // 使用 B站嵌入式播放器地址
  214.             playerIframe.src = `//player.bilibili.com/player.html?bvid=${video.bvid}&page=1&high_quality=1&danmaku=0`;

  215.             // 更新列表激活状态
  216.             const items = listContainer.querySelectorAll('.video-item');
  217.             items.forEach((item, i) => {
  218.                 if (i === index) {
  219.                     item.classList.add('active');
  220.                     // 确保激活项在可视区域
  221.                     item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  222.                 } else {
  223.                     item.classList.remove('active');
  224.                 }
  225.             });
  226.         }

  227.         // 启动
  228.         initPlayer();
  229.     })();
  230. </script>

  231. </body>
  232. </html>
复制代码
 楼主| 发表于 2026-8-29 19:23:47 |香港| 显示全部楼层
支持m3u8格式的多视频选集播放器代码
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Discuz M3U8 分集播放器</title>
  7.     <!-- 引入 TailwindCSS -->
  8.     <script src="https://cdn.tailwindcss.com"></script>
  9.     <script>
  10.         tailwind.config = {
  11.         corePlugins: {
  12.         preflight: false, // 关闭Tailwind的全局重置样式,避免覆盖Discuz原有页面样式
  13.                      }
  14.         }
  15.     </script>
  16.     <!-- 引入 Font Awesome -->
  17.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  18.     <!-- 引入 Hls.js -->
  19.     <script src="https://cdn.jsdelivr.net/npm/hls.js@latest/dist/hls.min.js"></script>
  20.     <style>
  21.         /* 自定义滚动条样式 */
  22.         .episode-scroll::-webkit-scrollbar {
  23.             width: 6px;
  24.             height: 6px;
  25.         }
  26.         .episode-scroll::-webkit-scrollbar-track {
  27.             background: #f1f1f1;
  28.             border-radius: 3px;
  29.         }
  30.         .episode-scroll::-webkit-scrollbar-thumb {
  31.             background: #cbd5e1;
  32.             border-radius: 3px;
  33.         }
  34.         .episode-scroll::-webkit-scrollbar-thumb:hover {
  35.             background: #94a3b8;
  36.         }
  37.         /* 选中状态动画 */
  38.         .episode-btn.active {
  39.             background-color: #3b82f6;
  40.             color: white;
  41.             border-color: #3b82f6;
  42.         }
  43.         /* 加载动画 */
  44.         .loader {
  45.             border: 3px solid #f3f3f3;
  46.             border-radius: 50%;
  47.             border-top: 3px solid #3b82f6;
  48.             width: 24px;
  49.             height: 24px;
  50.             -webkit-animation: spin 1s linear infinite; /* Safari */
  51.             animation: spin 1s linear infinite;
  52.         }
  53.         @keyframes spin {
  54.             0% { transform: rotate(0deg); }
  55.             100% { transform: rotate(360deg); }
  56.         }
  57.     </style>
  58. </head>
  59. <body class="bg-gray-100 p-4 font-sans">

  60.     <!-- 播放器容器 -->
  61.     <div id="dz-video-player-container" class="max-w-4xl mx-auto bg-white rounded-xl shadow-lg overflow-hidden border border-gray-200" style="isolation: isolate;">
  62.         
  63.         <!-- 视频区域 -->
  64.         <div class="relative w-full aspect-video bg-black group">
  65.             <video id="dz-video-element" class="w-full h-full object-contain" controls playsinline poster="/data/attachment/album/202608/31/200711vfnhzq7opn8dqp00.jpg"></video>
  66.             
  67.             <!-- 加载指示器 -->
  68.             <div id="dz-loading-indicator" class="absolute inset-0 flex items-center justify-center bg-black/50 hidden z-10">
  69.                 <div class="loader"></div>
  70.             </div>

  71.             <!-- 错误提示层 -->
  72.             <div id="dz-error-overlay" class="absolute inset-0 flex flex-col items-center justify-center bg-black/80 text-white hidden z-20">
  73.                 <i class="fas fa-exclamation-triangle text-4xl mb-2 text-red-500"></i>
  74.                 <p class="text-sm">视频加载失败,请检查网络或链接有效性</p>
  75.                 <button onclick="retryLoad()" class="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded text-sm transition">重试</button>
  76.             </div>
  77.         </div>

  78.         <!-- 控制面板:标题与分集 -->
  79.         <div class="p-4 bg-white">
  80.             <div class="flex justify-between items-center mb-4 border-b pb-2">
  81.                 <h2 id="dz-video-title" class="text-lg font-bold text-gray-800 truncate pr-4">正在播放:第 1 集</h2>
  82.                 <!-- div class="text-xs text-gray-500 flex items-center gap-2">
  83.                     <span id="dz-quality-badge" class="px-2 py-1 bg-gray-100 rounded text-gray-600">HD</span>
  84.                     <span id="dz-duration">00:00</span>
  85.                 </div -->
  86.             </div>

  87.             <!-- 分集列表 -->
  88.             <div class="mb-2 flex justify-between items-center">
  89.                 <span class="text-sm font-semibold text-gray-700"><i class="fas fa-list-ul mr-1"></i> 选集</span>
  90.                 <div class="flex gap-2">
  91.                      <button onclick="toggleOrder()" class="text-xs text-blue-600 hover:text-blue-800 flex items-center gap-1">
  92.                         <i class="fas fa-sort-amount-down" id="sort-icon"></i> <span id="sort-text">倒序</span>
  93.                     </button>
  94.                 </div>
  95.             </div>
  96.             
  97.             <div id="dz-episode-list" class="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 gap-2 max-h-40 overflow-y-auto episode-scroll p-1">
  98.                 <!-- 分集按钮将通过 JS 动态生成 -->
  99.             </div>
  100.         </div>
  101.     </div>

  102.     <script>
  103.         /**
  104.          * Discuz M3U8 Player Configuration
  105.          * 在 Discuz 发帖时,只需修改此处的 videoData 数组即可
  106.          */
  107.         const videoData = [
  108.             { title: "HD国语版", url: "https://yzzy21.playback-speed.com/20240410/29373_4da7f681/index.m3u8" }
  109.         ];

  110.         // 全局变量
  111.         let hls = null;
  112.         let currentEpisodeIndex = 0;
  113.         let isReversed = false;
  114.         let displayedData = [...videoData];
  115.         const videoElement = document.getElementById('dz-video-element');
  116.         const episodeListContainer = document.getElementById('dz-episode-list');
  117.         const loadingIndicator = document.getElementById('dz-loading-indicator');
  118.         const errorOverlay = document.getElementById('dz-error-overlay');
  119.         const titleElement = document.getElementById('dz-video-title');
  120.         const durationElement = document.getElementById('dz-duration');

  121.         /**
  122.          * 初始化播放器
  123.          */
  124.         function initPlayer() {
  125.             renderEpisodes();
  126.             loadEpisode(0); // 默认加载第一集
  127.             
  128.             // 监听视频元数据加载完成,更新时长显示
  129.             videoElement.addEventListener('loadedmetadata', () => {
  130.                 durationElement.innerText = formatTime(videoElement.duration);
  131.             });

  132.             // 监听播放结束,自动下一集
  133.             videoElement.addEventListener('ended', () => {
  134.                 playNext();
  135.             });
  136.         }

  137.         /**
  138.          * 渲染分集列表
  139.          */
  140.         function renderEpisodes() {
  141.             episodeListContainer.innerHTML = '';
  142.             displayedData.forEach((item, index) => {
  143.                 // 找到原始数据中的真实索引,用于点击事件
  144.                 const originalIndex = videoData.indexOf(item);
  145.                
  146.                 const btn = document.createElement('button');
  147.                 btn.className = `episode-btn px-3 py-2 text-xs font-medium rounded border border-gray-200 text-gray-700 hover:bg-blue-50 hover:border-blue-200 transition-all truncate ${originalIndex === currentEpisodeIndex ? 'active' : ''}`;
  148.                 btn.innerText = item.title;
  149.                 btn.onclick = () => loadEpisode(originalIndex);
  150.                 episodeListContainer.appendChild(btn);
  151.             });
  152.         }

  153.         /**
  154.          * 加载指定集数
  155.          * @param {number} index - 原始数据数组中的索引
  156.          */
  157.         function loadEpisode(index) {
  158.             if (index < 0 || index >= videoData.length) return;

  159.             currentEpisodeIndex = index;
  160.             const data = videoData[index];
  161.             
  162.             // 更新UI状态
  163.             updateActiveButton();
  164.             titleElement.innerText = `正在播放:${data.title}`;
  165.             showLoading(true);
  166.             hideError();

  167.             // 销毁旧的 HLS 实例
  168.             if (hls) {
  169.                 hls.destroy();
  170.             }

  171.             // 创建新的 HLS 实例
  172.             if (Hls.isSupported()) {
  173.                 hls = new Hls({
  174.                     debug: false,
  175.                     enableWorker: true,
  176.                     lowLatencyMode: true,
  177.                     backBufferLength: 90
  178.                 });

  179.                 hls.loadSource(data.url);
  180.                 hls.attachMedia(videoElement);

  181.                 hls.on(Hls.Events.MANIFEST_PARSED, function() {
  182.                     videoElement.play().catch(e => console.log("Auto-play prevented:", e));
  183.                     showLoading(false);
  184.                 });

  185.                 hls.on(Hls.Events.ERROR, function(event, data) {
  186.                     if (data.fatal) {
  187.                         switch(data.type) {
  188.                             case Hls.ErrorTypes.NETWORK_ERROR:
  189.                                 console.log("fatal network error encountered, try to recover");
  190.                                 hls.startLoad();
  191.                                 break;
  192.                             case Hls.ErrorTypes.MEDIA_ERROR:
  193.                                 console.log("fatal media error encountered, try to recover");
  194.                                 hls.recoverMediaError();
  195.                                 break;
  196.                             default:
  197.                                 showError();
  198.                                 hls.destroy();
  199.                                 break;
  200.                         }
  201.                     }
  202.                 });

  203.             } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
  204.                 // Safari 原生支持
  205.                 videoElement.src = data.url;
  206.                 videoElement.addEventListener('loadedmetadata', function() {
  207.                     videoElement.play();
  208.                     showLoading(false);
  209.                 });
  210.             } else {
  211.                 showError();
  212.             }
  213.         }

  214.         /**
  215.          * 更新按钮激活状态
  216.          */
  217.         function updateActiveButton() {
  218.             const buttons = episodeListContainer.querySelectorAll('.episode-btn');
  219.             buttons.forEach((btn, idx) => {
  220.                 // 注意:displayedData 可能是反转的,所以需要比对内容或重新映射
  221.                 // 这里简单起见,我们重新渲染或者通过文本匹配,但更优解是重新渲染
  222.                 // 为了性能,我们直接重新渲染整个列表是最安全的,因为列表通常不长
  223.                 renderEpisodes();
  224.             });
  225.         }

  226.         /**
  227.          * 播放下一集
  228.          */
  229.         function playNext() {
  230.             if (currentEpisodeIndex < videoData.length - 1) {
  231.                 loadEpisode(currentEpisodeIndex + 1);
  232.                 // 滚动到当前选中的按钮
  233.                 setTimeout(() => {
  234.                     const activeBtn = document.querySelector('.episode-btn.active');
  235.                     if(activeBtn) activeBtn.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  236.                 }, 100);
  237.             }
  238.         }

  239.         /**
  240.          * 切换排序(正序/倒序)
  241.          */
  242.         function toggleOrder() {
  243.             isReversed = !isReversed;
  244.             const sortIcon = document.getElementById('sort-icon');
  245.             const sortText = document.getElementById('sort-text');
  246.             
  247.             if (isReversed) {
  248.                 displayedData = [...videoData].reverse();
  249.                 sortIcon.className = "fas fa-sort-amount-up";
  250.                 sortText.innerText = "正序";
  251.             } else {
  252.                 displayedData = [...videoData];
  253.                 sortIcon.className = "fas fa-sort-amount-down";
  254.                 sortText.innerText = "倒序";
  255.             }
  256.             renderEpisodes();
  257.         }

  258.         /**
  259.          * 重试加载
  260.          */
  261.         function retryLoad() {
  262.             loadEpisode(currentEpisodeIndex);
  263.         }

  264.         /**
  265.          * UI 辅助函数
  266.          */
  267.         function showLoading(show) {
  268.             loadingIndicator.classList.toggle('hidden', !show);
  269.         }

  270.         function showError() {
  271.             errorOverlay.classList.remove('hidden');
  272.             showLoading(false);
  273.         }

  274.         function hideError() {
  275.             errorOverlay.classList.add('hidden');
  276.         }

  277.         function formatTime(seconds) {
  278.             if (isNaN(seconds)) return "00:00";
  279.             const m = Math.floor(seconds / 60);
  280.             const s = Math.floor(seconds % 60);
  281.             return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
  282.         }

  283.         // 启动
  284.         document.addEventListener('DOMContentLoaded', initPlayer);

  285.     </script>
  286. </body>
  287. </html>
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|B.K 网络工作室

GMT+8, 2026-9-14 20:06 , Processed in 0.031012 second(s), 22 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表