马黑黑 发表于 2024-5-27 12:06

canvas画布圆形进度条播放器半拉子项目预览

<canvas id="audCanv" width="400" height="240" style="margin: 20px auto; display: block; background: #333;"></canvas>
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=29999537" autoplay loop></audio>

<script>
const ww = audCanv.width, hh = audCanv.height;
const ctx = audCanv.getContext('2d');
var _play = 0, _change = 0;

class Audio {
        constructor(ctx,config) {
                this.ctx = ctx;
                this.cf = config;
                this.cx = config.w / 2;
                this.cy = config.h - config.size / 2 - 20;
                this.r = config.size / 2;
        };
        drawUi(text,value,flag) {
                this.ctx.clearRect(0, 0, this.cf.w, this.cf.h);
                this.ctx.lineCap = 'round';
                this.ctx.lineWidth = 10;
                this.ctx.strokeStyle = 'rgba(192,192,192,.95)';
                ctx.beginPath();
                this.ctx.arc(this.cx, this.cy, this.r, 0, Math.PI * 2);
                this.ctx.stroke();
                this.ctx.strokeStyle = 'rgba(255,250,250,.85)';
                ctx.beginPath();
                this.ctx.arc(this.cx, this.cy, this.r, 1.5 * Math.PI, Math.PI * (2 * value + 1.5));
                this.ctx.stroke();
                this.ctx.fillStyle = 'rgba(255,250,250,.85)';
                if(flag) {
                        this.ctx.beginPath();
                        this.ctx.moveTo(this.cx - 10, this.cy - 10);
                        this.ctx.lineTo(this.cx - 10, this.cy + 10);
                        this.ctx.lineTo(this.cx + 10, this.cy);
                        this.ctx.closePath();
                        this.ctx.fill();
                }else{
                        this.ctx.fillStyle = 'white';
                        this.ctx.beginPath();
                        this.ctx.fillRect(this.cx - 10, this.cy - 10, 4, 20);
                        this.ctx.fillRect(this.cx + 4, this.cy - 10, 4, 20);
                }
                this.ctx.font = 'normal 13px sans-serif';
                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'middle';
                this.ctx.beginPath();
                this.ctx.fillText(text, this.cx, this.cy + 35);
                this.ctx.fill();
        };
};

var option = {w: ww, h: hh, size: 140};
var _aud = new Audio(ctx,option);
_aud.drawUi('00:00/00:00',0,0);

aud.ontimeupdate = () => {
        var val = aud.currentTime / aud.duration;
        var txt = s2m(aud.currentTime) + '/' + s2m(aud.duration);
        _aud.drawUi(txt, val, aud.paused);
};

audCanv.onclick = (e) => {
        if(_play) aud.paused ? aud.play() : aud.pause();
        if(_change) {
                var deg = Math.atan2(e.offsetY - _aud.cy, e.offsetX - _aud.cx) * 180 / Math.PI;
                deg += (e.offsetX < _aud.cx && e.offsetY < _aud.cy) ? 450 : 90;
                aud.currentTime = aud.duration * deg / 360;
        };
};

audCanv.onmousemove = (e) => {
        var _ex = e.offsetX,
                _ey = e.offsetY,
                _cx = _aud.cx,
                _cy = _aud.cy,
                r1 = 10,
                r2 = _aud.r + 10; //+10: lineWidth
        if(innerCircle(_ex, _ey, _cx, _cy, r2) && innerCircle(_ex, _ey, _cx, _cy, r2 - 20) === false) {
                _change = 1;
                _play = 0;
        } else {
                _change = 0;
                _play = 0;
        }
        if(innerCircle(_ex, _ey, _cx, _cy, r1)) {
                _change = 0;
                _play = 1;
        }
        audCanv.style.cursor = ['default', 'pointer'];
        audCanv.title = !_change && !_play ? '' : _change ? '调节进度' : aud.paused ? '播放' : '暂停';
};

var innerCircle = (ex,ey,cx,cy,r) => Math.sqrt((ex - cx) ** 2 + (ey - cy) ** 2) <= r;
var s2m = (s) => (Math.floor(s / 60)).toString().padStart(2, '0') + ':' + (Math.floor(s % 60)).toString().padStart(2, '0');

</script>

马黑黑 发表于 2024-5-27 12:06

代码:
<canvas id="audCanv" width="400" height="240" style="margin: 20px auto; display: block; background: #333;"></canvas>
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=29999537" autoplay loop></audio>

<script>
const ww = audCanv.width, hh = audCanv.height;
const ctx = audCanv.getContext('2d');
var _play = 0, _change = 0;

class Audio {
        constructor(ctx,config) {
                this.ctx = ctx;
                this.cf = config;
                this.cx = config.w / 2;
                this.cy = config.h - config.size / 2 - 20;
                this.r = config.size / 2;
        };
        drawUi(text,value,flag) {
                this.ctx.clearRect(0, 0, this.cf.w, this.cf.h);
                this.ctx.lineCap = 'round';
                this.ctx.lineWidth = 10;
                this.ctx.strokeStyle = 'rgba(192,192,192,.95)';
                ctx.beginPath();
                this.ctx.arc(this.cx, this.cy, this.r, 0, Math.PI * 2);
                this.ctx.stroke();
                this.ctx.strokeStyle = 'rgba(255,250,250,.85)';
                ctx.beginPath();
                this.ctx.arc(this.cx, this.cy, this.r, 1.5 * Math.PI, Math.PI * (2 * value + 1.5));
                this.ctx.stroke();
                this.ctx.fillStyle = 'rgba(255,250,250,.85)';
                if(flag) {
                        this.ctx.beginPath();
                        this.ctx.moveTo(this.cx - 10, this.cy - 10);
                        this.ctx.lineTo(this.cx - 10, this.cy + 10);
                        this.ctx.lineTo(this.cx + 10, this.cy);
                        this.ctx.closePath();
                        this.ctx.fill();
                }else{
                        this.ctx.fillStyle = 'white';
                        this.ctx.beginPath();
                        this.ctx.fillRect(this.cx - 10, this.cy - 10, 4, 20);
                        this.ctx.fillRect(this.cx + 4, this.cy - 10, 4, 20);
                }
                this.ctx.font = 'normal 13px sans-serif';
                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'middle';
                this.ctx.beginPath();
                this.ctx.fillText(text, this.cx, this.cy + 35);
                this.ctx.fill();
        };
};

var option = {w: ww, h: hh, size: 140};
var _aud = new Audio(ctx,option);
_aud.drawUi('00:00/00:00',0,0);

aud.ontimeupdate = () => {
        var val = aud.currentTime / aud.duration;
        var txt = s2m(aud.currentTime) + '/' + s2m(aud.duration);
        _aud.drawUi(txt, val, aud.paused);
};

audCanv.onclick = (e) => {
        if(_play) aud.paused ? aud.play() : aud.pause();
        if(_change) {
                var deg = Math.atan2(e.offsetY - _aud.cy, e.offsetX - _aud.cx) * 180 / Math.PI;
                deg += (e.offsetX < _aud.cx && e.offsetY < _aud.cy) ? 450 : 90;
                aud.currentTime = aud.duration * deg / 360;
        };
};

audCanv.onmousemove = (e) => {
        var _ex = e.offsetX,
                _ey = e.offsetY,
                _cx = _aud.cx,
                _cy = _aud.cy,
                r1 = 10,
                r2 = _aud.r + 10; //+10: lineWidth
        if(innerCircle(_ex, _ey, _cx, _cy, r2) && innerCircle(_ex, _ey, _cx, _cy, r2 - 20) === false) {
                _change = 1;
                _play = 0;
        } else {
                _change = 0;
                _play = 0;
        }
        if(innerCircle(_ex, _ey, _cx, _cy, r1)) {
                _change = 0;
                _play = 1;
        }
        audCanv.style.cursor = ['default', 'pointer'];
        audCanv.title = !_change && !_play ? '' : _change ? '调节进度' : aud.paused ? '播放' : '暂停';
};

var innerCircle = (ex,ey,cx,cy,r) => Math.sqrt((ex - cx) ** 2 + (ey - cy) ** 2) <= r;
var s2m = (s) => (Math.floor(s / 60)).toString().padStart(2, '0') + ':' + (Math.floor(s % 60)).toString().padStart(2, '0');

</script>

马黑黑 发表于 2024-5-27 12:20

Math.atan2(),返回从原点 (0,0) 到 (x,y) 点的线段与 x 轴正方向之间的平面角度 (弧度值),也就是 Math.atan2(y,x);换言之, math.atan2(y,x) 返回给定的 y 及 x 坐标值的反正切值 atan(y / x),以弧度为单位,结果是在 -pi 和 pi 之间。我们这里使用它来处理进度条被点击时点击点与进度的关系,通过角度来实现。

在svg相类似的播放器中,我们也用到了这个 Math.atan2()

小辣椒 发表于 2024-5-27 12:30

这个播放器好像以前有这样的样式的,我收藏在最喜欢的播放器之一中{:4_173:}

马黑黑 发表于 2024-5-27 12:40

小辣椒 发表于 2024-5-27 12:30
这个播放器好像以前有这样的样式的,我收藏在最喜欢的播放器之一中

容易做的

小辣椒 发表于 2024-5-27 12:43

马黑黑 发表于 2024-5-27 12:40
容易做的

这个播放器做双语歌词用了几次

马黑黑 发表于 2024-5-27 13:13

小辣椒 发表于 2024-5-27 12:43
这个播放器做双语歌词用了几次

挺好用的吧

梦江南 发表于 2024-5-27 14:12

问老师,如果音乐只有0.4左右这个播放器要否调整?

南无月 发表于 2024-5-27 18:01

进来看到一个漂亮的小播。。。我点个赞。。

南无月 发表于 2024-5-27 18:02

马黑黑 发表于 2024-5-27 12:20
Math.atan2(),返回从原点 (0,0) 到 (x,y) 点的线段与 x 轴正方向之间的平面角度 (弧度值),也就是 Math.at ...

看了一眼烧脑,溜了溜了。。

南无月 发表于 2024-5-27 18:02

半拉子工程,这个名字起得好。。
看上去毛坯可以正常使用,缺点软装{:4_170:}

马黑黑 发表于 2024-5-27 18:05

南无月 发表于 2024-5-27 18:02
半拉子工程,这个名字起得好。。
看上去毛坯可以正常使用,缺点软装

{:4_172:}

马黑黑 发表于 2024-5-27 18:06

南无月 发表于 2024-5-27 18:02
看了一眼烧脑,溜了溜了。。

比较高端的数学知识点

马黑黑 发表于 2024-5-27 18:06

南无月 发表于 2024-5-27 18:01
进来看到一个漂亮的小播。。。我点个赞。。

感谢点赞

马黑黑 发表于 2024-5-27 18:07

梦江南 发表于 2024-5-27 14:12
问老师,如果音乐只有0.4左右这个播放器要否调整?

这个现在还不太合适投入使用环节

南无月 发表于 2024-5-27 20:08

马黑黑 发表于 2024-5-27 18:06
感谢点赞

天经地义的事儿不用费劲儿谢{:4_170:}

南无月 发表于 2024-5-27 20:08

马黑黑 发表于 2024-5-27 18:06
比较高端的数学知识点

那我就放心的忽略了,反正看不懂{:4_170:}

南无月 发表于 2024-5-27 20:08

马黑黑 发表于 2024-5-27 18:05


{:4_173:}等精装

马黑黑 发表于 2024-5-27 20:08

南无月 发表于 2024-5-27 20:08
那我就放心的忽略了,反正看不懂

需要至少大学本科五年的学历

马黑黑 发表于 2024-5-27 20:09

南无月 发表于 2024-5-27 20:08
等精装

出来了,回家看看去
页: [1] 2 3 4 5 6
查看完整版本: canvas画布圆形进度条播放器半拉子项目预览