基于howler.js写了个音频播放器组件

这篇文章发布于 2022年03月15日,星期二,23:53,归类于 JS实例。 阅读 18970 次, 今日 3 次 17 条评论

 

封面图

一、简述

抽时间写了个音频播放器组件,支持序列播放,支持倍速控制,桌面端和移动端通用,兼容现代浏览器,不支持 IE 浏览器。

演示地址:https://zhangxinxu.gitee.io/howlerjs-player/

音频播放组件示意图

项目地址

地址:https://gitee.com/zhangxinxu/howlerjs-player

现在我的开源小玩意全部都放在 Gitee 上了,因为开源实际上也是有国界的,放在里面安全点,访问速度也更快。

也欢迎关注我的 Gitee 账号

关注我的gitee

快速使用

本组件采用 Web Components 组件开发,使用的时候引入一个 JS,然后就可以使用了,如下所示:

  1. 引入 ui-audio.js
    <script type="module" src="./src/ui-audio.js"></script>
  2. 此时就可以使用 <ui-audio> 元素进行音频播放处理了,相关的属性和 API 基本上和原生的 <audio> 元素一致。
    <ui-audio src="your.mp3" controls></ui-audio>

    移动端显示

接下来详细介绍下 API 和一些细节知识。

二、API 详细介绍

支持几乎所有 HTML <audio> 元素支持的 API。

HTML 属性

所谓 HTML 属性,指的是可以在 HTML 元素上设置并生效的属性。

loop
支持值 0, 1, 2,分别表示顺序播放,随机播放和循环播放。

播放按钮

如果不设置,音频播放完毕即停止,也不会显示播放顺序设置按钮,如下 GIF 示意。

loop false 隐藏循环按钮

muted
布尔属性值,表示是否静音,此时声音图标会变成禁止标志,音量也会变成 0。
静音设置示意
controls
是否显示音频控制器。如果没有设置此属性,则播放器界面不可见,适合播放背景音乐。
src
当前播放的音频地址。
prevsrc
上一个播放的音频地址。如果不设置,不显示上一个播放按钮,如果是空字符串或者是 ‘none’,上一个播放按钮则禁用。
nextvsrc
下一个播放的音频地址。如果不设置,不显示下一个播放按钮,如果是空字符串或者是 ‘none’,下一个播放按钮则禁用。
type
音频播放的类型。和原生的 type 属性不同,这里只需要后缀名称即可,无需指定mime type,一般用在 src 地址没有暴露音频文件后缀的场景下。
label
标签提示信息,一般用来显示当前播放音频的名称或者描述。如果不设置,则不显示对应元素。

label描述信息

mode
当前播放的音频模式,支持 ‘webapi’ 和 ‘html5’ 两种方式,默认是 webapi,如果希望使用 html5 播放,可以设置 mode=”html5″,一般音频文件比较大,或者希望音频边下载边播放的时候使用 html5 模式。html5 模式在移动端,尤其 Safari 下可能会有体验问题。

以上 HTML 属性均可以直接从 DOM 对象上设置或获取,例如:

const audio = document.querySelector('ui-audio');
// 获取
console.log(audio.loop);
// 设置
audio.muted = false;

JS 属性

而下面这些属性或方法只能通过 DOM 对象在 JavaScript 中进行设置。

属性
currentTime
当前播放的时间,单位是秒。
volume
当前的音量,范围0-1。
playbackRate
用来设置或获取当前媒体文件的播放速率,值为数值。
paused
当前音频是否暂停中。
方法
play()
音频播放。
pause()
音频暂停。
stop()
音频停止,播放点会回到 0。
prev()
播放前一个音频。
next()
播放下一个音频。
state()
返回当前音频的状态,值包括:’unready’, ‘unloaded’, ‘loading’, ‘loaded’
load()
触发音频文件的加载。
unload()
音频文件的卸载,会停止播放音频,并释放内存。
事件
play
播放时触发。
pause
暂停时触发。
load
加载完毕触发。
end
播放完毕触发,如果是单曲循环,则每次播放结束都会执行一次。
stop
播放停止触发。
seek
改变播放的位置时候触发,例如点击或拖动进度条。
playing
当前正在播放的时候触发,每秒执行约 60 次。
error
加载或播放错误的时候触发,错误类型通过 event.detail 获取。
audio.addEventListener('pause', function () {
    // 暂停啦...
});

三、实际使用说明

如果只是基础的单音频播放,只需要使用上面的属性或方法进行设置就能满足需求。

但是,如果是具有播放列表的复杂音频播放器,则需要自己根据业务代码和上面提供的API接口进行处理。

正如项目演示页面所示。

需要自己根据播放状态(随机播放,还是顺序播放)改变 prevsrcnextsrc 的属性值,此时播放器会根据在播放结束之后自定义播放 nextsrc 对应的音频地址。

控件的自定义

首先讲下基础控件的显隐自定义。

  1. 默认“倍速”按钮是显示的,如果希望隐藏,可以在 <ui-audio> 元素上设置 rate="none"

    rate=

  2. 如果希望显示循环播放设置按钮,设置 loop 属性值为 ‘0’, ‘1’, ‘2’ 中的一个即可,如果仅仅是 loop 属性,不会显示此按钮,仅仅表示单曲循环播放。
    <!-- 单曲循环,不显示设置循环模式按钮 -->
    <ui-audio loop>
  3. 如果没有设置 prevsrcnextsrc 属性,则前后播放按钮是不会显示的。
    前后播放按钮示意

    上图之所以“上一个”播放按钮禁用态,是因为设置了 prevsrc="none"

移动端下,音量控制,以及倍速和循环按钮会自动收起,以节约布局空间,此时可以通过点击对应的按钮呼起相应的控件。

移动端布局效果示意

样式的自定义

整个组件基于 CSS 变量构建,同时使用 part 暴露了 Shadow DOM 内部主要的元素。

因此,想要对组件进行样式自定义,既可以使用 CSS 变量,也可以使用 ::part 伪元素。

例如,本组件的基本单位是 1rem,如果希望进行设置,则可以这么处理:

ui-audio {
    --1rem: 16px;
}

希望重置使用的图标,可以使用下面这些 CSS 变量:

--ui-audio-icon-prev: url();
--ui-audio-icon-next: url();
--ui-audio-icon-play: url();
--ui-audio-icon-pause: url();
--ui-audio-icon-muted: url();
--ui-audio-icon-unmuted: url();
// 更多图标,移动端出现
--ui-audio-icon-more: url();
// 随机播放图标
--ui-audio-icon-shuffle: url();
// 顺序播放图标
--ui-audio-icon-swap: url();
// 单曲循环图标
--ui-audio-icon-repeat: url();

以及支持全局的背景色和颜色变量:

--ui-audio-background
--ui-audio-color

如果希望更进一步的样式设置,则 ::part 伪元素是非常好的选择,例如:

part 属性示意

因此,我们可以使用下面这些伪元素对整个组件进行精确的样式设置:

ui-audio::part(container) {}
ui-audio::part(label) {}
ui-audio::part(operate) {}

例如,希望播放器换个皮肤颜色,可以这样处理:

<ui-audio class="your-audio" src="your.mp3" controls></ui-audio>
.your-audio::part(container) {
    background: darkred;
    color: #fff;
}

效果如下图所示:

改变颜色后的效果

如果对 ::part 伪元素还不是很了解,可以参考这篇文章:“使用::part伪元素改变Shadow DOM的CSS样式

Slot占位

组件在下图所示的位置提供了一个占位符,可以使用下面的语法插入你希望插入的 DOM 元素结构。

占位符内容

语法是设置 slot="custom",例如:

<ui-audio controls>
    <span slot="custom">by zhangxinxu</span>
</ui-audio>

自定义元素示意

由于播放器采用的是 flex 布局,因此,大家可以使用 order 属性调整自定义占位元素的位置,不一定在最后显示。

如果对 <slot> 插槽元素不是很了解,可以参见这篇文章:“HTML slot 插槽元素深入

在 Web Components 组件开发中,<slot> 插槽元素是非常实用的,必学必会。

四、关于howler.js

本组件依赖 howler.js,这是我用过体验非常好(从未出bug)的音频播放控制组件,优先使用 Web Audio API 实现。

项目地址:https://github.com/goldfire/howler.js

目前 Star 数目快 2 万了。

Star 数目

其他不多说,使用还是比较简单的,方法和API也比较丰富。

五、好,就说这么多

业余时间整的个玩意,突然来了兴趣,就弄了下,以后需要用的时候直接拿过来用就好了,同时对一些新技术练练手。

不过自己未在实际项目中应用,不保证百分百无 bug,如果使用过程中有什么问题,欢迎反馈,或者 pull 下,共同建设。

好,以上就是本文的全部内容,感谢阅读。

希望里面的内容能够对你的工作与学习有所帮助。

对了,本文可以任意转载,无版权限制。

点亮灯泡

(本篇完)

分享到:


发表评论(目前17 条评论)

  1. 吹成狗说道:

    H5端你测试过吗,没有bug.。。。。。。。。。。。。。。。真能吹

  2. testuser说道:

    设置HTML5模式后,拖动音频进度条,播放有问题

  3. HHQ说道:

    大佬,下载代码后直接打开html显示跨越引用js了,ui-audio出不来,小白不懂,需要发布到IIS吗?

  4. 方正丶说道:

    demo上修改2.5倍速后,偶现的自动切换后语音播放变成1倍速

  5. 反馈BUG说道:

    点击播放音乐后,播放列表的暂停键无效。

  6. wujixian说道:

    仓库访问不了(‘为了营造绿色健康的开源社区,推动中国开源生态建设,当前仓库仅限成员访问。
    你可以联系仓库管理员提交公开仓库访问的申请。’)

  7. 仓库私有化说道:

    请问是闭源了嘛,仓库私有化了无法访问。

  8. audio无缝循环播放说道:

    现有需要短音频无缝循环播放,总是有明显的停顿,audio无缝循环播放,不知道大神有没有好的方法?

  9. 刘祺轩说道:

    web component yyds!

    目前除了没有实现双向绑定,各方面感受都优于react和vue。

  10. apache说道:

    大佬,20年前一个韩国人写过一个网页播放器,播放清单是可以分很多页的,默认随机播放,很方便做网络电台,但可惜只支持IE内核,不知道能不能做类似的。

  11. 张鑫旭说道:

    Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡Ͱ̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡

  12. 村望说道:

    用了一下最近也在写相关项目,感觉大佬写的很丝滑

  13. blue说道:

    edge手机浏览器,随机播放 循环播放没效果

  14. vevan说道:

    可howlerjs也不是墙内的啊