2026-07-23 14:37:42
直播网站源码中Canvas实现圆形时间倒计时进度条的完整方案
基于提供的HTML模板和JS方法框架,以下是完整的实现代码及关键点解析:
一、核心实现代码(index.js)function CircleClock(canvasId) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); this.width = this.canvas.width; this.height = this.canvas.height; this.centerX = this.width / 2; this.centerY = this.height / 2; this.radius = Math.min(this.width, this.height) * 0.3; this.lineWidth = 15; this.totalSeconds = 0; this.currentSeconds = 0; this.animationId = null;}CircleClock.prototype = { // 设置进度(0-1) setProgress: function(progress) { this.currentSeconds = this.totalSeconds * (1 - progress); this.update(); }, // 获取当前进度 getProgress: function() { return 1 - (this.currentSeconds / this.totalSeconds); }, // 绘制背景圆环 drawBackgroundCircle: function() { this.ctx.save(); this.ctx.beginPath(); this.ctx.arc( this.centerX, this.centerY, this.radius, -Math.PI / 2, 2 * Math.PI - Math.PI / 2 ); this.ctx.lineWidth = this.lineWidth; this.ctx.strokeStyle = '#eee'; this.ctx.stroke(); this.ctx.restore(); }, // 绘制进度圆环 drawCurrentProgressCircle: function() { const progress = this.getProgress(); this.ctx.save(); this.ctx.beginPath(); this.ctx.arc( this.centerX, this.centerY, this.radius, -Math.PI / 2, 2 * Math.PI * progress - Math.PI / 2 ); this.ctx.lineWidth = this.lineWidth; this.ctx.strokeStyle = this.createLinearGradientByTime(progress); this.ctx.lineCap = 'round'; this.ctx.stroke(); this.ctx.restore(); }, // 创建渐变色(根据进度变化) createLinearGradientByTime: function(progress) { const gradient = this.ctx.createLinearGradient( this.centerX - this.radius, this.centerY, this.centerX + this.radius, this.centerY ); // 进度越少颜色越红 const r = Math.floor(255 * (1 - progress)); const g = Math.floor(200 * progress); gradient.addColorStop(0, `rgb(${r}, ${g}, 50)`); gradient.addColorStop(1, `rgb(${r}, ${g}, 100)`); return gradient; }, // 绘制倒计时文字 drawTimeText: function() { const secondsLeft = Math.ceil(this.currentSeconds); this.ctx.save(); this.ctx.font = `bold ${this.radius * 0.6}px Arial`; this.ctx.textAlign = 'center'; this.ctx.textBaseline = 'middle'; this.ctx.fillStyle = '#333'; this.ctx.fillText(secondsLeft.toString(), this.centerX, this.centerY); // 添加"秒"文字 this.ctx.font = `${this.radius * 0.2}px Arial`; this.ctx.fillText('秒', this.centerX, this.centerY + this.radius * 0.3); this.ctx.restore(); }, // 清空画布 clear: function() { this.ctx.clearRect(0, 0, this.width, this.height); }, // 设置总时间 setTime: function(seconds) { this.totalSeconds = seconds; this.currentSeconds = seconds; }, // 设置当前剩余时间 setCurrentTime: function(seconds) { this.currentSeconds = Math.max(0, seconds); }, // 启动倒计时 run: function(seconds, endCallback) { this.setTime(seconds); const startTime = Date.now(); const duration = seconds * 1000; const animate = () => { const elapsed = Date.now() - startTime; const remaining = Math.max(0, duration - elapsed); this.setCurrentTime(remaining / 1000); this.update(); if (remaining > 0) { this.animationId = requestAnimationFrame(animate); } else { this.animationId = null; if (typeof endCallback === 'function') { endCallback(); } } }; animate(); }, // 更新绘制 update: function() { this.clear(); this.drawBackgroundCircle(); this.drawCurrentProgressCircle(); this.drawTimeText(); }, // 停止倒计时 stop: function() { if (this.animationId) { cancelAnimationFrame(this.animationId); this.animationId = null; } }};// 使用示例const circleClock = new CircleClock("canvas");circleClock.run(30, () => { console.log("倒计时执行完毕");});二、关键实现解析Canvas初始化
通过构造函数获取Canvas元素和2D上下文
计算画布中心点和半径(取宽高最小值的30%)
设置圆环线宽为15像素
环形进度条绘制
背景圆环:灰色静态圆环作为底衬
进度圆环:
使用arc()方法绘制圆弧
起点设为-90°(12点钟方向)
根据当前进度计算结束角度
添加圆角端点(lineCap = 'round')
动态渐变效果
创建水平线性渐变
根据进度值动态计算RGB颜色:
进度越少红色成分越高(255 * (1 - progress))
进度越多绿色成分越高(200 * progress)
倒计时文字
中央显示剩余秒数(取整)
下方显示"秒"字作为单位
文字大小基于半径动态计算
动画控制
使用requestAnimationFrame实现平滑动画
通过时间戳计算精确的剩余时间
提供回调函数处理倒计时结束事件
性能优化
每次更新前清空画布
使用save()/restore()管理绘图状态
避免不必要的对象创建
自定义样式
// 可添加配置参数function CircleClock(canvasId, options = {}) { this.lineWidth = options.lineWidth || 15; this.bgColor = options.bgColor || '#eee'; // ...其他配置}暂停/继续功能
CircleClock.prototype.pause = function() { if (this.animationId) { cancelAnimationFrame(this.animationId); this.pausedAt = Date.now(); }};CircleClock.prototype.resume = function() { if (this.pausedAt) { const pausedDuration = Date.now() - this.pausedAt; this.startTime += pausedDuration; this.run(); // 需要调整实现方式 }};响应式适配
// 在构造函数中添加window.addEventListener('resize', () => { this.width = this.canvas.clientWidth; this.height = this.canvas.clientHeight; this.canvas.width = this.width; this.canvas.height = this.height; this.update();});这个实现方案提供了完整的圆形倒计时功能,包含动态颜色变化、平滑动画和清晰的文字显示,可直接集成到直播网站的礼物发送、连麦倒计时等场景中。