热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

实现一个HTML5音乐播放器的实例-

技术点:ES6+Webpack+HTML5Audio+Sass这里,我们将一步步的学到如何从零去实现一个H5音乐播放器。首先来看一下最终的实现效果:Demo链接 接下来就步入正题:要做一个音乐播放器就要非常了解在Web中音频播放的方式,通常都采用HTML5的audio标签关于audio标签,它有大量的属性、方法和事件,在这里我就做一个大致的介绍。属性:src:必需,音频来源
技术点:ES6+Webpack+HTML5 Audio+Sass

这里,我们将一步步的学到如何从零去实现一个H5音乐播放器。

首先来看一下最终的实现效果:Demo链接

接下来就步入正题:

  1. 要做一个音乐播放器就要非常了解在Web中音频播放的方式,通常都采用HTML5的audio标签
    关于audio标签,它有大量的属性、方法和事件,在这里我就做一个大致的介绍。

    属性:
    src:必需,音频来源;
    controls:常见,设置后显示浏览器默认的audio控制面板,不设置默认隐藏audio标签;
    autoplay:常见,设置后自动播放音频(移动端不支持);
    loop:常见,设置后音频将循环播放;
    preload:常见,设置音频预加载(移动端不支持);
    volume:少见,设置或返回音频大小,值为0-1之间的一个浮点数(移动端不支持);
    muted:少见,设置或返回静音状态;
    duration:少见,返回音频时长;
    currentTime:少见,设置或返回当前播放时间;
    paused:少见,返回当前播放状态,是否暂停;
    buffered:少见,一个TimeRanges对象,包含已缓冲的时间段信息,即加载进度。该对象包含一个属性length,返回一个从0开始的数表示当前缓冲了多少段音频;还包含两个方法,start、end,分别需要传入一个参数,即传入音频已加载的第几段,从0开始。start返回该段的起始时间,end返回该段的终点时间。举例:即传入0,第一段的起始是0,终止时间是17,单位秒;
    属性就介绍到这里,可能还有一些比较少用的属性如:playbackRate等,在视频播放中可能会用到,我就暂不讲解。

    方法:
    play():开始播放音频;
    pause():暂停播放音频;

    事件:
    canplay:当前音频可以开始播放(只加载了部分buffered,并未全部加载完成);
    canplaythrough:可以无停顿播放(即音频全部加载完成);
    durationchange:音频时长发生变化;
    ended:播放结束;
    error:发生错误;
    pause:播放暂停;
    play:播放开始;
    progress:音频下载过程中触发,事件触发过程中可以通过访问audio的buffered属性获取加载进度;
    seeking:音频跳跃中触发,即为修改currentTime时;
    seeked:音频跳跃完成时触发,即为修改完成currentTime时;
    timeupdate:音频播放过程中触发,同时currentTime属性在同步更新;
    事件就介绍到这里,可能还有一些不常用的事件暂不讲解。

    最后再讲解一下一个音频从开始加载到播放结束过程中,所触发的事件流以及我们在不同时间段可以操作的属性:
    loadstart:开始加载;
    durationchange:获取到音频时长(此时可以获取duration属性);
    progress:音频下载中(将伴随下载过程一直触发,此时可以获取buffered属性);
    canplay:所加载的音频足够开始播放(每次暂停后开始播放也会触发);
    canplaythrough:音频全部加载完成;
    timeupdate:播放过程中(currentTime属性伴随着同步更新);
    seeking:修改当前播放进度中(即为修改currentTime属性);
    seeked:修改当前播放进度完成;
    ended:播放完成;
    这就是整个音频的大致事件流,可能有一些少用的事件没有列举出。
    在事件触发过程中,有一些属性在音频还没有开始加载的时候就可以设置,如:controls、loop、volume等等;

  2. 确定整体结构:
    因为自己是做成插件的方式发布在npm上供他人使用的,所以我们就采用面向对象的方式进行代码编写,又因为用户的需求不一,所以在设计之初就暴露出大量的API和配置项以满足大部分用户的需求。
    这里因为自己更习惯es6的语法,就全程以es6为基础进行开发,同时为了开发效率,又使用了sass进行css的编写,最后还使用了webpack和webpack-dev-server用以编译es6和sass,项目打包,构建本地服务器。


  3. 确定播放器UI和交互:
    可能关于界面每个人有自己的想法,这里就不过多赘述了,以我做好的播放器UI为例进行分解

    const Util = {
        leftDistance: (el) => {
            let left = el.offsetLeft;
            let scrollLeft;while (el.offsetParent) {
                el = el.offsetParent;
                left += el.offsetLeft;
            }
            scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;return left - scrollLeft;
        },
        timeFormat: (time) => {
            let tempMin = parseInt(time / 60);
            let tempSec = parseInt(time % 60);
            let curMin = tempMin <10 ? (&#39;0&#39; + tempMin) : tempMin;
            let curSec = tempSec <10 ? (&#39;0&#39; + tempSec) : tempSec;return curMin + &#39;:&#39; + curSec;
        },
        percentFormat: (percent) => {return (percent * 100).toFixed(2) + &#39;%&#39;;
        },
        ajax: (option) => {
            option.beforeSend && option.beforeSend();
            let xhr = new XMLHttpRequest();
            xhr.Onreadystatechange= () => {if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status <300){
                        option.success && option.success(xhr.responseText);
                    }else{
                        option.fail && option.fail(xhr.status);
                    }
                }
            };
            xhr.open(&#39;GET&#39;,option.url);
            xhr.send(null);
        }
    };
    View Code

由于设计之初,考虑到播放器的独特性,设计为只能存在一个实例,设置了一个全局变量以判断当前是否存在实例:

let instance = false;

在使用ES6的情况下,我们将主逻辑放在构造函数内部,将通用性强和API放在公共函数内部:

class skPlayer {
    constructor(option){
    }

    template(){
    }

    init(){
    }

    bind(){
    }

    prev(){
    }

    next(){
    }

    switchMusic(index){
    }

    play(){
    }

    pause(){
    }

    toggle(){
    }

    toggleList(){
    }

    toggleMute(){
    }

    switchMode(){
    }

    destroy(){
    }
}
View Code

实例判断,如果存在返回无原型的空对象,因为ES6构造函数内默认返回带原型的实例:

        if(instance){
            console.error(&#39;SKPlayer只能存在一个实例!&#39;);return Object.create(null);
        }else{
            instance = true;
        }

初始化配置项,默认配置与用户配置合并:

        const defaultOption = {
            ...
        };this.option = Object.assign({},defaultOption,option);

将常用属性绑定在实例上:

        this.root = this.option.element;this.type = this.option.music.type;this.music = this.option.music.source;this.isMobile = /mobile/i.test(window.navigator.userAgent);

一些公共的API内部this指向在默认情况下指向实例,但是为了减少代码量,将操作界面上的功能与API调用一套代码,在绑定事件的时候this指向会改变,所以通过bind的方式绑定this,当然也可以在绑定事件的时候使用箭头函数:

        this.toggle = this.toggle.bind(this);this.toggleList = this.toggleList.bind(this);this.toggleMute = this.toggleMute.bind(this);this.switchMode = this.switchMode.bind(this);

接下来,我们就使用ES6字符串模板开始生成HTML,插入到页面中:

            this.root.innerHTML = this.template();

接下来初始化,初始化过程中将常用DOM节点绑定,初始化配置项,初始化操作界面:

            this.init();
    init(){this.dom = {
            cover: this.root.querySelector(&#39;.skPlayer-cover&#39;),
            playbutton: this.root.querySelector(&#39;.skPlayer-play-btn&#39;),
            name: this.root.querySelector(&#39;.skPlayer-name&#39;),
            author: this.root.querySelector(&#39;.skPlayer-author&#39;),
            timeline_total: this.root.querySelector(&#39;.skPlayer-percent&#39;),
            timeline_loaded: this.root.querySelector(&#39;.skPlayer-line-loading&#39;),
            timeline_played: this.root.querySelector(&#39;.skPlayer-percent .skPlayer-line&#39;),
            timetext_total: this.root.querySelector(&#39;.skPlayer-total&#39;),
            timetext_played: this.root.querySelector(&#39;.skPlayer-cur&#39;),
            volumebutton: this.root.querySelector(&#39;.skPlayer-icon&#39;),
            volumeline_total: this.root.querySelector(&#39;.skPlayer-volume .skPlayer-percent&#39;),
            volumeline_value: this.root.querySelector(&#39;.skPlayer-volume .skPlayer-line&#39;),
            switchbutton: this.root.querySelector(&#39;.skPlayer-list-switch&#39;),
            modebutton: this.root.querySelector(&#39;.skPlayer-mode&#39;),
            musiclist: this.root.querySelector(&#39;.skPlayer-list&#39;),
            musicitem: this.root.querySelectorAll(&#39;.skPlayer-list li&#39;)
        };this.audio = this.root.querySelector(&#39;.skPlayer-source&#39;);if(this.option.listshow){this.root.className = &#39;skPlayer-list-on&#39;;
        }if(this.option.mode === &#39;singleloop&#39;){this.audio.loop = true;
        }this.dom.musicitem[0].className = &#39;skPlayer-curMusic&#39;;
    }
View Code

事件绑定,主要绑定audio的事件以及操作面板的事件:

            this.bind();
    bind(){this.updateLine = () => {
            let percent = this.audio.buffered.length ? (this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration) : 0;this.dom.timeline_loaded.style.width = Util.percentFormat(percent);
        };// this.audio.addEventListener(&#39;load&#39;, (e) => {//     if(this.option.autoplay && this.isMobile){//         this.play();//     }// });this.audio.addEventListener(&#39;durationchange&#39;, (e) => {this.dom.timetext_total.innerHTML = Util.timeFormat(this.audio.duration);this.updateLine();
        });this.audio.addEventListener(&#39;progress&#39;, (e) => {this.updateLine();
        });this.audio.addEventListener(&#39;canplay&#39;, (e) => {if(this.option.autoplay && !this.isMobile){this.play();
            }
        });this.audio.addEventListener(&#39;timeupdate&#39;, (e) => {
            let percent = this.audio.currentTime / this.audio.duration;this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(this.audio.currentTime);
        });//this.audio.addEventListener(&#39;seeked&#39;, (e) => {//    this.play();//});this.audio.addEventListener(&#39;ended&#39;, (e) => {this.next();
        });this.dom.playbutton.addEventListener(&#39;click&#39;, this.toggle);this.dom.switchbutton.addEventListener(&#39;click&#39;, this.toggleList);if(!this.isMobile){this.dom.volumebutton.addEventListener(&#39;click&#39;, this.toggleMute);
        }this.dom.modebutton.addEventListener(&#39;click&#39;, this.switchMode);this.dom.musiclist.addEventListener(&#39;click&#39;, (e) => {
            let target,index,curIndex;if(e.target.tagName.toUpperCase() === &#39;LI&#39;){
                target = e.target;
            }else{
                target = e.target.parentElement;
            }
            index = parseInt(target.getAttribute(&#39;data-index&#39;));
            curIndex = parseInt(this.dom.musiclist.querySelector(&#39;.skPlayer-curMusic&#39;).getAttribute(&#39;data-index&#39;));if(index === curIndex){this.play();
            }else{this.switchMusic(index + 1);
            }
        });this.dom.timeline_total.addEventListener(&#39;click&#39;, (event) => {
            let e = event || window.event;
            let percent = (e.clientX - Util.leftDistance(this.dom.timeline_total)) / this.dom.timeline_total.clientWidth;if(!isNaN(this.audio.duration)){this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(percent * this.audio.duration);this.audio.currentTime = percent * this.audio.duration;
            }
        });if(!this.isMobile){this.dom.volumeline_total.addEventListener(&#39;click&#39;, (event) => {
                let e = event || window.event;
                let percent = (e.clientX - Util.leftDistance(this.dom.volumeline_total)) / this.dom.volumeline_total.clientWidth;this.dom.volumeline_value.style.width = Util.percentFormat(percent);this.audio.volume = percent;if(this.audio.muted){this.toggleMute();
                }
            });
        }
    }
View Code

至此,核心代码基本完成,接下来就是自己根据需要完成API部分。
最后我们暴露模块:

module.exports = skPlayer;

一个HTML5音乐播放器就大功告成了 ~ !

以上就是实现一个HTML5音乐播放器的实例的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • loader资源模块加载器webpack资源模块加载webpack内部(内部loader)默认只会处理javascript文件,也就是说它会把打包过程中所有遇到的 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
author-avatar
手机用户2602901563
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有