canvas在组件中循环画图时图片闪烁

news/2025/12/1 15:58:34/文章来源:https://www.cnblogs.com/sunshine233/p/19293095

起因 && bug 复现

有一个页面,要用 canvas 画出背景图片和文字,并且定时刷新页面。

这个图分三层, 第一层画红色和蓝色的矩形框,第二层画背景图,第三层写文字。

 image

原来的代码中我直接把canvasContext.fillRectcanvasContext.drawImagecacanvasContextnvas.fillText 放在一个函数中,循环多少次就画多少次。

这种写法直接放在页面中时,页面中的背景图片并不会闪烁。

但后来我优化了代码,把 canvas 画图部分挪到了组件中,然后在上级页面中调用 canvas 的组件页面。每秒更新数据,也就是每秒 canvas 重绘。

新版 canvas (type="2d") 获取 canvas 节点的方式变了: uniapp + 微信小程序:新版 canvas 常用 api 及注意事项

或许是子页面与主页面的通讯增加了性能开销,也可能是wx.createSelectorQuery()this.createSelectorQuery() 开销不同,总之这个页面开始闪烁,每次刷新都只能==随机显示一张背景图==。

 

分析

为了判断是哪一部分性能消耗大,我注释了 canvasContext.drawImage 部分,发现刷新时页面不闪烁了。由此判定是 ==背景图片== 的问题。

 

经过进一步排查,this.createSelectorQuery() 性能没有我想的大,问题还在别处。

this.createSelectorQuery()
.select(`#oil${id}`)
.node(({ node: canvas }) => {
   console.log("canvas: ", canvas);
})
.exec();

进一步排查,问题的确在图片上,但不是我以为的 canvasContext.drawImage 而在加载图片资源上。

因为在 canvas 中,不能直接使用 canvasContext.drawImage(imageUrl),而要先用 canvasContext.createImage(); 创建一个图片实例,再修改 bgImg.src ,然后才能使用 canvasContext.drawImage

const bgImg = canvas.createImage();
bgImg.src = "/static/hcbgimg.png";

const canvasContext = canvas.getContext("2d");
canvasContext.drawImage(bgImg, 0, 0, bgimgWidth, bgimgHeight);

在原先代码中,每次循环都重新创建图片实例,也就是每次都调用 canvas.createImage() ,所以页面闪烁。

解决办法

:::: steps

  1. 创建 canvas 实例和 canvasContext.draw分开,创建 canvas 实例只运行一次,每次更新数据后 canvas 实例不重新调用。

  2. 把创建图片实例放在创建第一个 canvas 实例之后,也只调用一次

  3. bgImg 缓存起来,以后每一次 canvasContext.drawImage(bgImg) 都调用这个缓存的对象

::::

组件代码(部分)

<template ><view><view v-for= "item in tankList" ><canvas  type= "2d" :id="'oil' + item.id"  class= "canvas" ></canvas ></view></view>
</template ><script >
import {  converIntToRgb,  getSysInfo }  from "/utils/utils.js"; 
​
export  default { props: { tankList: { type:  Array, default: [], },},data() { return {// 油罐图片的宽高bgimgWidth: 0,bgimgHeight: 0,
​context: [],canvasList: [],bgImg: null,};},mounted() {const screenWidth = getSysInfo();this.bgimgWidth = screenWidth * 0.9;this.bgimgHeight = screenWidth * 0.4;},watch: {tankList(newValue, oldValue) {// 第一次进入页面,等有数据了再画图if (oldValue.length == 0 && newValue.length != 0) {this.createContexts(this.bgimgWidth, this.bgimgHeight); //每次进来的时候先画一次
      }
​// if 防止没有context还要画图报错if (this.context.length != 0) {this.refreshCanvas(this.bgimgWidth, this.bgimgHeight);}},},methods: {// 这个页面只运行一次
    createContexts() {// ❗❗❗ 新版canvas需要消除锯齿const windowInfo = wx.getWindowInfo();const availableWidth = windowInfo.windowWidth;const dpr = windowInfo.pixelRatio; //设备像素比
// 1. 给 context[] 赋值(在这个页面只创建一次)this.tankList.forEach((tank, index) => {this.createSelectorQuery().select(`#oil${tank.id}`).node(({ node: canvas }) => {console.log("canvas: ", canvas);// 获取到第一个canvas节点时创建图片对象,以免后续加载图片太慢if (!this.bgImg) {this.bgImg = canvas.createImage();this.bgImg.src = "/static/bgimg.png";}
​this.context[index] = canvas.getContext("2d");
​canvas.width = availableWidth * dpr;canvas.height = availableWidth * 0.4 * dpr; //按照下面css的尺寸比例this.context[index].scale(dpr, dpr); //必须有
this.canvasList[index] = canvas; //存储全局变量,其她函数中也可以使用已存储的canvas实例if (index === this.tankList.length - 1) {//全部建立成功后再画图,调用一遍,防止等待时间过长setTimeout(() => {this.refreshCanvas(this.bgimgWidth, this.bgimgHeight);}, 100);}}).exec();});},refreshCanvas(bgimgWidth, bgimgHeight) {const context = this.context; // 为了下面画canvas时少写一个 this
// 2. 设置不同油罐油品的颜色let oilColors = []; //存储不同油罐油品的颜色this.tankList.forEach((tank) => {oilColors.push(converIntToRgb(tank.oilColor));});
​this.tankList.forEach((tank, index) => {// // ❗❗❗ 每次画新的之前先清空画布(包括图片)context[index].clearRect(0, 0, bgimgWidth, bgimgHeight);
​// // 3. 画油和水的矩形块,然后画背景图片const rectStartPointX = bgimgWidth * 0.15;const rectWidth = bgimgWidth * 0.1;let yOil = bgimgHeight * (1 - (0.9 * tank.oilRatio) / 100); // 一次性数据,只在传参时用一次,不用定义数组let yWater = bgimgHeight * (1 - (0.9 * tank.waterRatio) / 100); // 一次性数据,只在传参时用一次,不用定义数组// 3.1 画油位矩形context[index].fillStyle = oilColors[index];context[index].fillRect(rectStartPointX, yOil, rectWidth, bgimgHeight);// // 3.3 新版canvas画背景图片,将图片绘制到 canvas 上this.context[index].drawImage(this.bgImg,0,0,bgimgWidth,bgimgHeight);
​// 4. 写详细信息(放外面会被图片挡住,因为图片加载较慢)// 4.1 设置文字颜色:在线黑色,离线红色const textColor = tank.connect ? "black" : "red";context[index].fillStyle = textColor;// 4.2 写罐号 1context[index].font = "20px sans-serif";context[index].fillText(tank.id, bgimgWidth * 0.06, bgimgHeight * 0.6);});},},
};
</script><style scoped>
/* .canvas样式不需要需改!! */
.canvas {/* border: 1rpx solid black; */width: 750rpx;height: 300rpx;margin-top: 30rpx;margin-left: 37rpx;margin-right: 37rpx;
}
</style>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/983085.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

博士留学中介权威排名:面试辅导不过关的机构直接出局!

博士申请的 “生死线” 藏在面试环节 —— 即便背景与全奖适配,面试应答失误仍会导致功亏一篑。2025 年面试形式更趋复杂,学术追问深度、跨文化沟通要求同步升级,辅导体系薄弱的机构已难以应对。以下以星级评分(★…

[Vue]AntV1.7表格自带筛选确定和重置按钮位置交换

原来:修改后:代码如下: <style scope> /* 表格筛选器按钮样式 */ :deep(.ant-table-filter-dropdown-btns) {display: flex;flex-direction: row-reverse;justify-content: flex-start;padding: 8px;border-t…

2025年中国传统干锅鸭品牌推荐:好的干锅鸭大型品牌推荐有哪

本榜单依托全国餐饮消费调研数据、大众点评/抖音等平台真实口碑及品牌连锁规模,筛选出五家标杆干锅鸭品牌,为消费者选店、创业者加盟提供客观参考,助力匹配高性价比的餐饮品牌伙伴。 TOP1 推荐:商丘任广涛餐饮管…

PCIe-8052 双口万兆光纤图像采集卡:万兆传输赋能,解锁工业采集新速度

在工业自动化图像采集领域,随着高清化、高速化采集需求的不断升级,设备的传输速率、稳定性与兼容性成为制约产线效率的关键因素。PCIe-8052 双口万兆光纤图像采集卡凭借其万兆传输性能、高性能硬件配置及**的适配能力…

构建人机共生的价值基石:LLM与人类协同构建价值原语行为网络

构建人机共生的价值基石:LLM与人类协同构建价值原语行为网络 摘要 本文提出一种实现“AI元人文”构想的具体技术路径:构建一个人机协同、动态演化的价值原语行为网络。该路径的核心在于,将大语言模型视为强大的“价…

2025博士留学中介权力榜:前八谁的导师网络最硬核?

博士申请的 “隐形权力” 藏在导师网络里 —— 网络的广度决定选择边界,深度决定资源质量,活性决定对接实效。2025 年申博季,硬核的导师网络已成为机构核心竞争力的终极体现。以下以星级评分(★★★★★为满)解析…

123年时光淬炼:从瑞士乡村集体创业到GPHG入围的HEBE镂空制表传奇

HEBE Watch 于 1901 年在 Arc Jurassien 的瑞士小镇 Alle 成立,是村民集体创业的结果,随着时间的推移,他们在钟表的不同领域获得了各种技能。这些钟表专家统一植根于该地区的手工制表传统。在冬季,农作物无法种植,…

SpringCloud Feign首次调用慢解决方案

一、概述 Feign作为Spring Cloud中声明式HTTP客户端,首次调用慢是高频问题,核心成因包括懒加载初始化、连接池未预热、DNS解析耗时、Hystrix/Sentinel初始化等,先明确首次调用慢的核心环节,避免盲目优化:慢调用环…

2025年下半年内蒙古承载力检测服务商Top5推荐指南

摘要 2025年下半年,内蒙古地区承载力检测行业随着基础设施建设的加速发展,需求持续增长,专业检测服务商的重要性日益凸显。本文提供一份推荐榜单,涵盖五家优质公司,排名不分先后,旨在为用户提供参考。榜单基于行…

2025年特种工业泵供应商权威推荐榜单:工业泵/充填工业泵/耐磨泵源头厂家精选

特种工业泵作为现代工业流程中的核心动力与传输设备,其性能直接关系到生产系统的稳定性、能源消耗与运营安全。在化工、矿山、冶金、能源及污水处理等高要求领域,对泵类产品的耐腐蚀性、耐磨耗性、防爆安全性及智能控…

2025年下半年房屋检测公司推荐排行榜:内蒙古鑫质检测有限公司领跑行业

摘要 2025年下半年房屋检测行业迎来新一轮技术升级与服务优化,随着内蒙古地区城镇化进程加快和老旧小区改造需求增长,专业房屋检测服务成为保障建筑安全的重要环节。本文基于市场调研数据、用户口碑评价和技术实力评…

2025年高口碑灌浆防水涂料公司推荐,解决您的防水烦恼!

在寻求修补防水涂料厂家的过程中,了解各公司的产品特性和市场口碑显得尤为重要。尤其是在2025年,信息的透明性和有效的对比能够帮助消费者做出明智的选择。我们推荐的几家公司,如重庆市高新技术产业开发区高和建筑材…

现今靠谱的短视频运营团队排行榜单

摘要 随着短视频平台的快速发展,2025年短视频运营行业已成为企业营销的核心渠道,市场规模预计突破千亿元。企业通过专业团队提升品牌曝光和获客效率至关重要。本文基于行业数据和用户口碑,整理出前十名靠谱的短视频…

想找闸机租赁源头厂家,这几点一定要知道!

在当今各类商业活动、公共场合中,闸机的使用越来越广泛。对于一些临时性活动或者预算有限的场所来说,闸机租赁成为了一个不错的选择。然而,要找到靠谱的闸机租赁源头厂家,有几点是一定要了解清楚的。 1、成本优势考…

国内优秀的下载 maven jar 加速镜像站点

1、华为云:<mirror><id>huaweicloud</id><mirrorOf>central</mirrorOf><name>华为云公共仓库</name><url>https://repo.huaweicloud.com/repository/maven</url&…

如何用 vxe-table 实现粘贴数据自动进入新增行与新增列,数据无限扩充

如何用 vxe-table 实现粘贴数据自动进入新增行与新增列,数据无限扩充,对于大部分业务操作场景,有时需要从 excel 复制数据并粘贴到表格中,由于粘贴的数据会列多于表格定义的行与列,多出的数据需要能支持自动新增与…

2025成都全包装修权威推荐,资质服务双优,装修/整装/家装/房屋装修品牌选择指南

引言:透明整装时代,如何甄选值得托付的成都装修公司? 随着成都家装消费持续升级,业主对全包装修、整装交付、环保工艺与设计落地一致性的要求日益提高。然而,市场仍存在施工队伍不稳、材料以次充好、增项频发、售…

回头看基础,TemplatePrompt和MessagePrompt

最近想实现一个简单的功能:用户输入一个餐厅名关键词,agent调用高德地图api,拿到餐厅的一些关键信息,如准确店名、地址(文字地址+经纬度)、评分、人均、营业时间等,接下来agent将接口返回的数据整理,形成更精简…

2025年碳纤木门制造商权威推荐榜单:套装门/实木门/无漆木门源头厂家精选

碳纤木门,作为现代门业中融合了前沿材料科技与家居美学的高端产品,凭借其卓越的强度重量比、优异的耐用性、出众的环保性能以及时尚的外观设计,正迅速成为住宅、商业及工程项目中的热门选择。选择一家技术扎实、品控…

2025年氯化镁阻化剂定制厂家权威推荐榜单:LH—Z01阻化剂‌/煤矿专用阻化剂‌/MSF—II阻化剂‌源头厂家精选

在煤矿安全生产标准不断提升与环保政策日益严格的背景下,氯化镁阻化剂作为抑制煤炭自燃的关键材料,其市场需求正朝着高效、环保、定制化的方向深度发展。据行业调研,应用优质阻化剂后,煤炭的自燃发火期能够延长约2…