|
|
请马上登录,朋友们都在花潮里等着你哦:)
您需要 登录 才可以下载或查看,没有账号?立即注册
x
带进度条+歌词同步的canvas画布音频播放器 | 马黑黑
做童安格的《耶利亚女郎》帖子时,一时兴起用canvas画布做了一个进度条加歌词同步的音频播放器,感觉基本功能、外观都还可以,就是可定制性不够强。想想还是认为将播放器作为一个实例对象加以操作,使用时方能拥有更方便的自定义设定。
实例化对象适合做音频播放器,属性、方法可以无序化存在,需要什么不需要什么可以随意增删,无需因为一个属性或方法的变化四处去找寻和修改关联的代码语句,基于对象的变动所涉及修改范围基本就在对象代码块之内。
所以,建立了一个 mplayer 对象,它是实例化的,因为无需克隆:
let mplayer = {
属性1: 值,
属性2: 值,
属性N: 值,
方法1: function() { ... },
方法2: function() { ... },
方法M: function() { ... },
}
每一个属性和方法都是以键值对的形式存在,每一个属性和方法之间用逗号隔开。分行不是必须的,分行只是为了方便组织和阅读代码。
我们的 mplayer 对象的最终任务是描述播放器界面及其行为。实现手段是,对象给出各种组成播放器所需的“零件”即属性并提供这些零件的组装方法。比方说,播放器的按钮是个圆形的小东东,它上面还有一个表示播放的三角形图案或是表示暂停的两道杠,圆形、三角形、两道杠这些部件,通过对象的方法实现,而圆形等的尺寸、位置以及颜色啥的,是对象提供的“零件”即属性。具体说,圆形的按钮,圆心xy坐标及其半径,还有它要上什么颜色,都是 mplayer 对象以键值对的形式一一提供的属性,而圆形按钮画在哪、怎么画,则是通过 mplayer 对象提供的方法去完成。mplayer 提供的所有属性与方法,都是基于一个播放器的基本样式和功能,换言之,我们把实现一个播放器所需的“零件”和“组装”方法全部封装在了一个叫做 mplayer 的对象里。
对象的方法其实就是函数,函数是可以接收“第三方”提供的参数的。例如,mplayer 要绘制出歌词,它并不知道歌词是什么,这时,就由 audio 控件的播放进度机制来处理歌词,并一句一句地传递给 mplayer 对象,mplayer 则将歌词给绘制出来。就是说,针对lrc同步,mplayer 只管处理一个歌词变量 text,text 的内容传递过来了它就绘制这些文本。
具体实现过程请在播放器源码中体会:
- <canvas id="player" style="position: absolute; left: 10px; top: 10px; border: 1px solid;"></canvas>
- <script>
- let ctx = player.getContext('2d'),
- w = player.width = 350,
- h = player.height = 100,
- flag, //鼠标滑过按钮标识
- aud = new Audio();
- let mplayer = {
- startX: 30, //圆形按钮圆心x
- startY: 70, //圆形按钮圆心y
- radius: 16, //圆形按钮半径
- c_lrc: 'teal', //lrc歌词颜色
- c_time: 'teal', //时间信息颜色
- c_track: '#eee', //进度轨线颜色
- c_prg: 'red', //进度条颜色
- c_circle: 'lightblue', //圆圈颜色
- c_btn: 'snow', //按钮标识颜色
- c_btnhover: 'pink', //按钮标识鼠标滑过颜色
- lineLen: 200, //进度条长度
- drawLrc: function(text) { //lrc同步
- ctx.clearRect(0, 0, w, 50);
- ctx.fillStyle = this.c_lrc;
- ctx.textAlign = 'center';
- ctx.beginPath();
- ctx.font = '1.2em sans-serif';
- ctx.fillText(text, w/2, 30);
- ctx.fill();
- },
- drawProgress: function(prog, text) { //进度
- ctx.clearRect(this.startX + this.radius, this.startY - 10, w - this.startX + this.radius, 40);
- ctx.beginPath();
- ctx.font = '14px sans-serif';
- ctx.textBaseline = 'middle';
- ctx.strokeStyle = this.c_track;
- ctx.moveTo(this.startX + this.radius + 4, this.startY);
- ctx.lineTo(this.startX + this.radius + 4 + this.lineLen, this.startY); //底线
- ctx.stroke();
- ctx.beginPath();
- ctx.strokeStyle = this.c_prg;
- ctx.moveTo(this.startX + this.radius + 4, this.startY);
- ctx.lineTo(this.startX + this.radius + 4 + prog, this.startY); //进度线
- ctx.stroke();
- ctx.fillStyle = this.c_time;
- ctx.textAlign = 'left';
- ctx.fillText(text, this.startX + this.radius + 8 + this.lineLen, this.startY); //数字进度
- ctx.fill();
- },
- drawBtn: function(id) { //绘制播放+暂停按钮
- ctx.clearRect(this.startX - this.radius, this.startY - this.radius, this.radius *2, this.radius*2);
- ctx.fillStyle = this.c_circle;
- ctx.beginPath();
- ctx.arc(this.startX, this.startY, this.radius, 0, 2*Math.PI);
- ctx.fill();
- ctx.fillStyle = flag ? this.c_btnhover : this.c_btn;
- ctx.beginPath();
- if (id) { //播放图标
- ctx.moveTo(this.startX - this.radius / 2 + 1, this.startY - this.radius / 2);
- ctx.lineTo(this.startX - this.radius / 2 + 1, this.startY + this.radius / 2);
- ctx.lineTo(this.startX + this.radius / 2 + 1, this.startY);
- ctx.fill();
- } else { //暂停图标
- ctx.fillRect(this.startX - this.radius / 2 + 5, this.startY - this.radius / 2, 2, this.radius);
- ctx.fillRect(this.startX - this.radius / 2 + 10, this.startY - this.radius / 2, 2, this.radius);
- }
- },
- isHover: function (x, y) {
- return Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2) <= Math.pow(this.radius, 2);
- },
- }
- let lrcAr = [
- ['0.01','童安格 耶利亚女郎'],
- ['34.10','很远的地方有个女郎 名字叫做耶利亚'],
- ['42.33','有人在传说她的眼睛 看了使人更年轻'],
- ['50.62','如果你得到她的拥抱 你就永远不会老'],
- ['59.15','为了这个神奇的传说 我要努力去寻找'],
- ['66.42','耶利亚神秘耶利亚 耶利耶利亚'],
- ['74.77','耶利亚神秘耶利亚 我一定要找到她'],
- ['101.12','很远的地方有个女郎 名字叫做耶利亚'],
- ['109.32','有人在传说她的眼睛 看了使人更年轻'],
- ['117.90','如果你得到她的拥抱 你就永远不会老'],
- ['126.22','为了这个神奇的传说 我要努力去寻找'],
- ['133.57','耶利亚神秘耶利亚 耶利耶利亚'],
- ['141.90','耶利亚神秘耶利亚 我一定要找到她'],
- ['150.29','耶利亚神秘耶利亚 耶利耶利亚'],
- ['158.76','耶利亚神秘耶利亚 我一定要找到她'],
- ['198.70','耶利亚神秘耶利亚 耶利耶利亚'],
- ['207.11','耶利亚神秘耶利亚 我一定要找到她'],
- ['215.28','耶利亚神秘耶利亚 耶利耶利亚'],
- ['223.72','耶利亚神秘耶利亚 我一定要找到她']
- ];
- //audio控件设置
- aud.src = '耶利亚女郎.mp3';
- aud.autoplay = true;
- aud.loop = true;
- //初始化按钮
- mplayer.drawBtn(aud.paused);
- mplayer.drawProgress(1, '00:00 | 00:00');
- mplayer.drawLrc('等待播放 ...');
- //监听鼠标经过
- player.addEventListener('mousemove', (e) => {
- flag = mplayer.isHover(e.offsetX, e.offsetY);
- if (flag) {
- player.style.cursor = 'pointer';
- mplayer.drawBtn(aud.paused);
- } else {
- player.style.cursor = 'default';
- mplayer.drawBtn(aud.paused);
- }
- });
- player.addEventListener('click', (e) => { if(flag) aud.paused ? aud.play() : aud.pause(); }); //画布点击事件
- aud.addEventListener('playing', () => mplayer.drawBtn(false)); //监听播放状态
- aud.addEventListener('pause', () => mplayer.drawBtn(true)); //监听暂停状态
- //进度监听
- aud.addEventListener('timeupdate', () => {
- let prg = mplayer.lineLen * aud.currentTime / aud.duration, tMsg = toMin(aud.duration) + ' | ' + toMin(aud.currentTime);
- mplayer.drawProgress(prg, tMsg);
- for(j = 0; j < lrcAr.length; j ++) {
- if(aud.currentTime >= lrcAr[j][0]) mplayer.drawLrc(lrcAr[j][1]);
- }
- });
- //格式化时间信息 → 00:00
- function toMin(val) {
- if (!val) return '00:00';
- val = Math.floor(val);
- let min = parseInt(val / 60);
- let sec = parseFloat(val % 60);
- if(min < 10) min = '0' + min;
- if(sec < 10) sec = '0' + sec;
- return min + ':' + sec;
- }
- </script>
复制代码 播放器演示在二楼。
|
评分
-
| 参与人数 3 | 威望 +130 |
金钱 +260 |
经验 +130 |
收起
理由
|
加林森
| + 30 |
+ 60 |
+ 30 |
很给力! |
红影
| + 50 |
+ 100 |
+ 50 |
赞一个! |
小辣椒
| + 50 |
+ 100 |
+ 50 |
赞一个! |
查看全部评分
|