《起源》使用的播放器插件开发指引
《起源》一帖,播放器控制器使用纯画布制作。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
可以做不同样式的,,,效果更好看期待,
可以的。但这个是很基础,合适初级JS水平的爱好者学习 马黑黑 发表于 2022-12-19 22:53
可以的。但这个是很基础,合适初级JS水平的爱好者学习
谢谢黑黑分享,黑神太厉害了,一个接一个的{:4_177:} 小辣椒 发表于 2022-12-19 23:04
谢谢黑黑分享,黑神太厉害了,一个接一个的
{:4_190:} 这个解说非常清晰,真的很适合我们这样没什么基础的,黑黑辛苦了{:4_199:} 一步步看下来,竟然看懂了,心里非常欢喜。谢谢{:4_187:} 红影 发表于 2022-12-20 02:17
一步步看下来,竟然看懂了,心里非常欢喜。谢谢
用心就可以 马黑黑 发表于 2022-12-20 10:07
用心就可以
这个简单又好看,真不错{:4_187:} 红影 发表于 2022-12-20 12:51
这个简单又好看,真不错
虽简单,也需要一些知识积累做基础 马黑黑 发表于 2022-12-20 12:52
虽简单,也需要一些知识积累做基础
是的,而且这个还可以有多重画法的可能,真棒。 红影 发表于 2022-12-20 16:46
是的,而且这个还可以有多重画法的可能,真棒。
还行
页:
[1]