当前位置: 首页 > news >正文

音视频开发远端未发布视频占位图2

音视频开发时候,如果对方未发布视频,或者后面才发布视频,或停止发送流时,占位图的优势就有了;
考虑到大部分音视频SDK都会有play方法,但不一定有成员状态,如果我们自己创建canvas和视频有概率会有布局问题;最好还是给他弄成背景图片放后面,不管是对方流是空的(网络不好无法传流,或者有个别设备摄像头坏了无法编解码,或者本身就没摄像头给的空轨道);这个时候有个保底占位图就很舒服了;
总结一句话就是:比黑屏好;

效果图

请添加图片描述

调用API
/* 先new一个@params: curDrawDiv: HTMLElement; 说明:需要绘制的dev;@params: useName: String;   说明:用户姓名;
*/
const avatarSpace = new AvatarCanvasSpaceBj(curDrawDiv, useName);
// 开始绘制
avatarSpace.createCanvasBackground();// 单独更新名字_并绘制
avatarSpace.updateUserName(useName);// 销毁
avatarSpace.destroy();
核心方法
// 头像占位
class AvatarCameraSpace {myCanvas: HTMLCanvasElement | null;ctx: CanvasRenderingContext2D | null;curDrawDiv: HTMLElement;useName: string;constructor(curDrawDiv: HTMLElement, useName: string) {this.curDrawDiv = curDrawDiv;this.useName = useName;this.myCanvas = document.createElement('canvas');this.ctx = this.myCanvas.getContext('2d');}// 更新用户名(相同则不处理)updateUserName(useName: string) {// 检查用户名是否相同,相同则不处理if (useName === this.useName) return;this.useName = useName;this.drawCanvas();}// 创建canvas并设置为parentDiv的背景createCanvasBackground() {this.drawCanvas();}// 绘制用户头像占位并设置为背景drawCanvas() {// 先把原背景图给他置空掉this.clearResources();try {if (!this.myCanvas || !this.ctx || !this.parentDivIsExists()) return;const canvas = this.myCanvas;const ctx = this.ctx;const useName = this.useName;const canvasWidth = 1280;const canvasHeight =  720;canvas.width = 1280;canvas.height = 720;// 保存上下文初始状态(重要:确保每次绘制都是干净的状态)ctx.save();// 重置变换矩阵ctx.setTransform(1, 0, 0, 1, 0, 0);// 清空画布ctx.clearRect(0, 0, canvasWidth, canvasHeight);// 绘制黑色背景ctx.fillStyle = '#000000';ctx.fillRect(0, 0, canvasWidth, canvasHeight);// 计算蓝色方框的位置(居中)const boxSize = Math.min(120, canvasWidth * 0.2, canvasHeight * 0.2); // 自适应大小const boxX = (canvasWidth - boxSize) / 2;const boxY = (canvasHeight - boxSize) / 2 - 50; // 稍微向上移动一点,给下方名字留出空间// 圆角半径const borderRadius = 15;// 绘制蓝色方框ctx.fillStyle = '#1689F4';ctx.beginPath();// 左上角圆角ctx.moveTo(boxX + borderRadius, boxY);// 上边缘ctx.lineTo(boxX + boxSize - borderRadius, boxY);// 右上角圆角ctx.arcTo(boxX + boxSize, boxY, boxX + boxSize, boxY + borderRadius, borderRadius);// 右边缘ctx.lineTo(boxX + boxSize, boxY + boxSize - borderRadius);// 右下角圆角ctx.arcTo(boxX + boxSize, boxY + boxSize, boxX + boxSize - borderRadius, boxY + boxSize, borderRadius);// 下边缘ctx.lineTo(boxX + borderRadius, boxY + boxSize);// 左下角圆角ctx.arcTo(boxX, boxY + boxSize, boxX, boxY + boxSize - borderRadius, borderRadius);// 左边缘ctx.lineTo(boxX, boxY + borderRadius);// 左上角收尾圆角ctx.arcTo(boxX, boxY, boxX + borderRadius, boxY, borderRadius);// 闭合路径并填充ctx.closePath();ctx.fill();// 获取名字的第一个字const firstChar = useName.charAt(0);// 在蓝色方框中绘制第一个字(居中)ctx.fillStyle = '#FFFFFF'; // 白色文字ctx.font = `${Math.min(80, boxSize * 0.6)}px Arial`; // 自适应字体大小ctx.textAlign = 'center';ctx.textBaseline = 'middle';ctx.fillText(firstChar, boxX + boxSize / 2, boxY + boxSize / 2);// 在蓝色方框下方绘制全名const nameFontSize = Math.min(40, boxSize * 0.3);ctx.font = `${nameFontSize}px Arial`; // 自适应字体大小const displayName = useName.length > 12 ? `${useName.substring(0, 12)}...` : useName;ctx.fillText(displayName, canvasWidth / 2, boxY + boxSize + 60);// 恢复上下文初始状态(确保后续绘制不受当前状态影响)ctx.restore();// 绘制完成后设置为背景this.setCanvasAsBackground();} catch(error) {console.warn('绘制失败了', error);}}// 将Canvas内容设置为parentDiv的背景图片setCanvasAsBackground() {try {if (!this.myCanvas || !this.parentDivIsExists()) return;// 将Canvas转换为图片URLconst dataUrl = this.myCanvas.toDataURL('image/png');// 设置为parentDiv的背景图片this.curDrawDiv.style.backgroundImage = `url(${dataUrl})`;this.curDrawDiv.style.backgroundSize = 'cover';this.curDrawDiv.style.backgroundPosition = 'center';this.curDrawDiv.style.backgroundRepeat = 'no-repeat';} catch(error) {console.warn('设置背景失败了', error);}}// 父元素是否还存在于DOM中parentDivIsExists() {return document.body.contains(this.curDrawDiv);}// 清除背景和资源clearResources() {try {// 清除背景图this.curDrawDiv.style.backgroundImage = 'none';} catch(error) {}}// 销毁实例destroy() {this.clearResources();// 释放引用this.myCanvas = null;this.ctx = null;}
}export default AvatarCameraSpace;
http://www.dtcms.com/a/537038.html

相关文章:

  • 【普中STM32F1xx开发攻略--标准库版】-- 第 9 章 STM32 固件库介绍
  • OpenCV 视频处理
  • 有些网站做不了seo物业公司会计好做吗
  • 实惠的制作网站网站首页的功能需求分析
  • ROS2 轨迹规划核心点
  • [GESP202506 五级] 奖品兑换
  • Strassen矩阵乘法算法
  • 网站开发新闻怎么写相应式手机网站建设
  • [C++][windows]C++类成员函数默认参数和成员变量初始化问题
  • Vue 动态路由复制标签页失效?彻底解决新标签页路由空白问题
  • 扁平化网站特效张家港网站建设培训班
  • 【GaussDB】深入剖析Insert Select慢的定位全过程
  • 面向智能体与大语言模型的 AI 基础设施:选项、工具与优化
  • 招商网站建设服务商湖南专业网站建设服务
  • 从0到1:易趋驱动产品研发项目全流程管理效能跃升
  • 巴彦淖尔市百家姓网站建设文昌市规划建设管理局网站
  • JAX 高性能机器学习的新选择 - 从NumPy到变换编译
  • 能盈利的网站网站首页description标签
  • Geoserver修行记-安装CSS插件避坑
  • O(1) 时间获取最小值的巧妙设计——力扣155.最小栈
  • 韩国网站建设wordpress安装博客步骤
  • dbpystream webapi: 一次clickhouse数据从系统盘迁至数据盘的尝试
  • 大数据-136 - ClickHouse 集群 表引擎详解 选型实战:TinyLog/Log/StripeLog/Memory/Merge
  • 高效的项目构建和优化之前端构建工具
  • 网站建设公司宣传文案如何通过cpa网站做推广
  • windows环境,设置git 默认提交信息
  • 电商平台网站建设合同宁波seo优化报价多少
  • 哪里找人做网站系统设计
  • 做一个网站需要多少钱大概费用商贸有限公司注销流程
  • OpenVLA-OFT+ 在真实世界 ALOHA 机器人任务中的应用