马黑黑 发表于 2022-12-19 21:03

《起源》使用的播放器插件开发指引

《起源》一帖,播放器控制器使用纯画布制作。canvas是操作十分繁琐的HTML元素,它是个空板,需要JS(或其他语言)在上面一一绘制图形,还必须不停地擦除、重绘。

帖子的播放器效果,只不过如下图,看上去简简单单:



却:

(一)进度条需要画两个长条矩形(也可以画线条);
(二)画两处文本;
(三)画一个圆;
(四)画播放/暂停按钮需要绘制一个三角形、两个小矩形。

这四样,如果单独画每一样,那都不是问题(ctx 是画笔):

——画矩形:ctx.fillRect(x,y,w,h); 其中,xy为矩形左上角坐标,w是矩形长度,h是矩形高度;
——画文本:ctx.fillText(text,x,y); 其中,text 是文本,xy是文本绘制的起点坐标;
——画圆:ctx.arc(x,y,r,0,2*Math.PI); 其中,xy是圆心坐标,r是圆半径,0是起始弧度,2*Math.PI 是结束弧度;
——画三角形:用画线方法 lineTo() 画三条前后衔接的封闭线即可,lineTo(x1,y1) 要先 moveTo(x,y)。

可以将各种图形的画法封装成函数:

//画矩形
let drawRect = (x,y,ww,hh,color) => {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.fillRect(x,y,ww,hh);
};


//画文本
let drawTxt = (text,x,y,align,color) => {
        ctx.beginPath();
        ctx.font = '16px sans-serif';
        ctx.textAlign = align;
        ctx.textBaseline = 'middle';
        ctx.fillStyle = color;
        ctx.fillText(text,x,y);
};


//画圆
let drawCircle = (x,y,r,color) => {
        ctx.beginPath();
        ctx.strokeStyle = color;
        ctx.lineWidth = 2;
        ctx.arc(x,y,r,0,2*Math.PI);
        ctx.stroke();
};


//画三角形
let drawTriangle = (x,y,len,color) => {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.moveTo(x,y);
        ctx.lineTo(x, y+len);
        ctx.lineTo(x+len, y + len/2);
        ctx.lineTo(x,y);
        ctx.fill();
};


然后将这些函数再封装成一个总封装。封装前为了方便设置一些交互所有变量,先声明一个对象用以存储用得上的变量:

//创建 player 实体对象
let player = {
        prog: 0,
        track: w,
        color: ['#ff0000','#a9a9a9'],
        cur: '00:00',
        dur: '00:00',
};


接下来才封装各种画法:

//整体封装
let drawAll = () => {
        ctx.clearRect(0,0,w,h);
        let btnColor = btnFlag ? player.color : player.color; //按钮颜色
        drawRect(0,10,w, 4, player.color); // 画线 :track
        drawRect(0,10,player.prog, 4, player.color); //画线 : prog
        drawTxt(player.cur,w/2-22,h-20,'right',player.color); //文本 : 当前播放到
        drawCircle(w/2,h-20,16,btnColor); //圆环
        drawTxt(player.dur,w/2 + 22,h-20,'left',player.color); //文本 : 总时长
        //暂停|播放按钮
        aud.paused ? drawTriangle(w/2-6,h-28,16,btnColor) : (drawRect(w/2-6, h-28,4,16,btnColor), drawRect(w/2+2, h-28,4,16,btnColor));
};


至此,运行函数 drawAll() 便能得出大概的播放控制器样式,前提是,我们需要补上一些声明和HTML相关标签(比如audio等)。

以上代码只是揭示《起源》所用到的插件的核心代码制作。播放控制器的完整代码如下:

<style>
#papa { margin: auto; padding: 12px; width: 600px; height: 400px; color: red; border: 1px solid olive; position: relative; }
#canv { position: absolute; left: calc(50% - 150px); top: calc(50% - 50px); }
</style>

<div id="papa">
        <canvas id="canv" width="300" height="60"></canvas>
        <audio id="aud" src="https://music.163.com/song/media/outer/url?id=2005217155.mp3" autoplay></audio>
</div>

<script>
let ctx = canv.getContext('2d');
let w = canv.width, h = canv.height;
let btnFlag = false;

let player = {
        prog: 0,
        track: w,
        color: ['#ff0000','#a9a9a9'],
        cur: '00:00',
        dur: '00:00',
};

//画圆
let drawCircle = (x,y,r,color) => {
        ctx.beginPath();
        ctx.strokeStyle = color;
        ctx.lineWidth = 2;
        ctx.arc(x,y,r,0,2*Math.PI);
        ctx.stroke();
};
//画三角形
let drawTriangle = (x,y,len,color) => {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.moveTo(x,y);
        ctx.lineTo(x, y+len);
        ctx.lineTo(x+len, y + len/2);
        ctx.lineTo(x,y);
        ctx.fill();
};

//画矩形
let drawRect = (x,y,ww,hh,color) => {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.fillRect(x,y,ww,hh);
};
//画文本
let drawTxt = (text,x,y,align,color) => {
        ctx.beginPath();
        ctx.font = '16px sans-serif';
        ctx.textAlign = align;
        ctx.textBaseline = 'middle';
        ctx.fillStyle = color;
        ctx.fillText(text,x,y);
};

//整体封装
let drawAll = () => {
        ctx.clearRect(0,0,w,h);
        let btnColor = btnFlag ? player.color : player.color; //按钮颜色
        drawRect(0,10,w, 4, player.color); // track
        drawRect(0,10,player.prog, 4, player.color); //画线 : prog
        drawTxt(player.cur,w/2-22,h-20,'right',player.color); //文本 : 当前播放到
        drawCircle(w/2,h-20,16,btnColor); //圆环
        drawTxt(player.dur,w/2 + 22,h-20,'left',player.color); //文本 : 总时长
        //暂停|播放按钮
        aud.paused ? drawTriangle(w/2-6,h-28,16,btnColor) : (drawRect(w/2-6, h-28,4,16,btnColor), drawRect(w/2+2, h-28,4,16,btnColor));
};

//时间信息格式化
let toMin = (val) => {if (!val) return '00:00';val = Math.floor(val);let min = parseInt(val / 60), sec = parseFloat(val % 60);if (min < 10) min = '0' + min;if (sec < 10) sec = '0' + sec;return min + ':' + sec;};
//鼠标指针滑过按钮判断依据
let overBtn = (e) => Math.sqrt((e.offsetX-w/2) ** 2 + (e.offsetY-(h-20)) ** 2) < 16;
//鼠标指针滑过进度条判断依据
let overProg = (e) => e.offsetY > 0 && e.offsetY < 15;
//鼠标指针滑过画布监视
canv.onmousemove = (e) => {
        canv.style.cursor = overBtn(e) || overProg(e) ? 'pointer' : 'default';
        canv.title = overProg(e) ? toMin(e.offsetX*aud.duration/w) : '';
        overBtn(e) ? (btnFlag = true,drawAll()) : (btnFlag = false,drawAll());
}
//按钮和进度条单击
canv.onclick = (e) => {
        if(overBtn(e)) {
                aud.paused ? aud.play() : aud.pause();
                drawAll();
        }
        if(overProg(e)) {
                aud.currentTime = aud.duration * e.offsetX / w;
        }
}
//audio进度监视
aud.addEventListener('timeupdate', () => {
        player.prog = aud.currentTime * w /aud.duration;
        player.cur = toMin(aud.currentTime);
        player.dur = toMin(aud.duration);
        drawAll();
});

drawAll();

</script>


风中飞尘 发表于 2022-12-19 21:39

可以做不同样式的,,,效果更好看期待,

马黑黑 发表于 2022-12-19 22:53

风中飞尘 发表于 2022-12-19 21:39
可以做不同样式的,,,效果更好看期待,

可以的。但这个是很基础,合适初级JS水平的爱好者学习

小辣椒 发表于 2022-12-19 23:04

马黑黑 发表于 2022-12-19 22:53
可以的。但这个是很基础,合适初级JS水平的爱好者学习

谢谢黑黑分享,黑神太厉害了,一个接一个的{:4_177:}

马黑黑 发表于 2022-12-19 23:20

小辣椒 发表于 2022-12-19 23:04
谢谢黑黑分享,黑神太厉害了,一个接一个的

{:4_190:}

红影 发表于 2022-12-20 02:16

这个解说非常清晰,真的很适合我们这样没什么基础的,黑黑辛苦了{:4_199:}

红影 发表于 2022-12-20 02:17

一步步看下来,竟然看懂了,心里非常欢喜。谢谢{:4_187:}

马黑黑 发表于 2022-12-20 10:07

红影 发表于 2022-12-20 02:17
一步步看下来,竟然看懂了,心里非常欢喜。谢谢

用心就可以

红影 发表于 2022-12-20 12:51

马黑黑 发表于 2022-12-20 10:07
用心就可以

这个简单又好看,真不错{:4_187:}

马黑黑 发表于 2022-12-20 12:52

红影 发表于 2022-12-20 12:51
这个简单又好看,真不错

虽简单,也需要一些知识积累做基础

红影 发表于 2022-12-20 16:46

马黑黑 发表于 2022-12-20 12:52
虽简单,也需要一些知识积累做基础

是的,而且这个还可以有多重画法的可能,真棒。

马黑黑 发表于 2022-12-20 17:25

红影 发表于 2022-12-20 16:46
是的,而且这个还可以有多重画法的可能,真棒。

还行
页: [1]
查看完整版本: 《起源》使用的播放器插件开发指引