uniapp + 微信小程序:新版canvas常用api及注意事项

news/2025/2/24 16:54:46/文章来源:https://www.cnblogs.com/sunshine233/p/18733637

关于新旧canvas的比较我以前写过一篇博客 :https://www.cnblogs.com/sunshine233/p/17014701.html ,这里就不重复了。

但在正文开始之前,我不得不再说一遍微信的文档写的真垃圾。很多问题的答案都是在微信开发者社区里找到的。

 一、新版canvas 基础用法:

<template><view><view class="divider">新版canvas 👇</view><!-- 1. canvas-id 换成了 id --><!-- 2. 增加了 type="2d" 表示新版canvas --><canvas id="myCanvas" type="2d" ></canvas></view>
</template><script>export default {data() {return {}},mounted() {this.newCanvas();},methods: {newCanvas() {// 3. 获取canvas节点的方式变了,必须按照这个格式写// 3.1 如果不是直接页面而是子页面,把 wx 改成 this,否则获取不到 node节点// 也就是 wx.createSelectorQuery()  其她部分一样
                wx.createSelectorQuery().select('#myCanvas').node(({node: canvas}) => {//4. 获取正确实例const ctx = canvas.getContext('2d');ctx.fillStyle = "green";ctx.fillRect(0, 0, 50, 50);// 4.1 设置字体大小(两个参数必须都写)ctx.font = "20px sans-serif";// 4.2 写文字ctx.fillText("我是新版canvas", 50, 30);// 4.3 新版 canvas 不需要调用 draw()// ctx.draw(); 
                    }).exec();},}}
</script><style scoped>.divider {margin: 10px 0;}canvas {background-color: antiquewhite;}
</style>

代码中获取实例的写法和官方实例效果一样

 运行结果:

二、新版canvas画图模糊失真

但canvas实际上有两种设置尺寸的方式:

  • 一种是默认尺寸300*150(文档中写可以直接用  <canvas id="myCanvas" type="2d" width="300" height="300"></canvas>  修改,但我2025/2/24尝试好像不起效了);
  • 一种是在css中设置  canvas { width: 300px; height: 300px; }  

这两个尺寸被成为渲染宽高和逻辑宽高。但我觉得微信文档写的似乎有点问题且模糊不清。

查了别的文档 + 实践,我发现如果在css中手动设置了canvas的宽高,画图就会出现模糊(因为canvaWidth和styleWidth不一致)。

和上一段代码差异只增加了css

<template><view><view class="divider">新版canvas 👇</view><!-- 1. canvas-id 换成了 id --><!-- 2. 增加了 type="2d" 表示新版canvas --><canvas id="myCanvas" type="2d" ></canvas></view>
</template><script>export default {data() {return {}},mounted() {this.newCanvas();},methods: {newCanvas() {// 3. 获取canvas节点的方式变了,必须按照这个格式写// 3.1 如果不是直接页面而是子页面,把 wx 改成 this,否则获取不到 node节点// 也就是 wx.createSelectorQuery()  其她部分一样
                wx.createSelectorQuery().select('#myCanvas').node(({node: canvas}) => {//4. 获取正确实例const ctx = canvas.getContext('2d');ctx.fillStyle = "green";ctx.fillRect(0, 0, 50, 50);// 4.1 设置字体大小(两个参数必须都写)ctx.font = "20px sans-serif";// 4.2 写文字ctx.fillText("我是新版canvas", 50, 30);// 4.3 新版 canvas 不需要调用 draw()// ctx.draw(); 
                    }).exec();},}}
</script><style scoped>.divider {margin: 10px 0;}canvas {background-color: antiquewhite;  width: 300px;height: 300px;}
</style>

 想解决模糊失真的问题就要使用  canvas.width = styleWidth*dpr; canvas.height = styleHeight*dpr;  也就是重新设置canvas的尺寸等于css中的尺寸*像素比

获取像素比微信已经给出了api : const dpr = wx.getWindowInfo().pixelRatio; 

现在css中宽高设置的都是300, 所以要设置  canvas.width = 300* dpr; canvas.height = 300 * dpr;  最后缩放  ctx.scale(dpr, dpr); 

<template><view><view class="divider">新版canvas 👇</view><!-- 1. canvas-id 换成了 id --><!-- 2. 增加了 type="2d" 表示新版canvas --><canvas id="myCanvas" type="2d" ></canvas></view>
</template><script>export default {data() {return {}},mounted() {this.newCanvas();},methods: {newCanvas() {// 3. 获取canvas节点的方式变了,必须按照这个格式写// 3.1 如果不是直接页面而是子页面,把 wx 改成 this,否则获取不到 node节点// 也就是 wx.createSelectorQuery()  其她部分一样
                wx.createSelectorQuery().select('#myCanvas').node(({node: canvas}) => {//4. 获取正确实例const ctx = canvas.getContext('2d');  const dpr = wx.getWindowInfo().pixelRatio;console.log("default canvas.width:", canvas.width," default canvas.height:", canvas.height);console.log("dpr:", dpr);canvas.width = 300* dpr;canvas.height = 300 * dpr;ctx.scale(dpr, dpr);ctx.fillStyle = "green";ctx.fillRect(0, 0, 50, 50);// 4.1 设置字体大小(两个参数必须都写)ctx.font = "20px sans-serif";// 4.2 写文字ctx.fillText("我是新版canvas", 50, 30);// 4.3 新版 canvas 不需要调用 draw()// ctx.draw(); 
                    }).exec();},}}
</script><style scoped>.divider {margin: 10px 0;}canvas {background-color: antiquewhite; width: 300px;height: 300px;}
</style>

 如果想用动态数据可以用 

<template><view><view class="divider">新版canvas 👇</view><!-- 1. canvas-id 换成了 id --><!-- 2. 增加了 type="2d" 表示新版canvas --><canvas id="myCanvas" type="2d" :style="{width:styleWidth+'px',height:styleHeight+'px'}" ></canvas></view>
</template><script>export default {data() {return {  styleWidth:200,styleHeight:100}},mounted() {this.newCanvas();},methods: {newCanvas() {// 3. 获取canvas节点的方式变了,必须按照这个格式写// 3.1 如果不是直接页面而是子页面,把 wx 改成 this,否则获取不到 node节点// 也就是 wx.createSelectorQuery()  其她部分一样
                wx.createSelectorQuery().select('#myCanvas').node(({node: canvas}) => {//4. 获取正确实例const ctx = canvas.getContext('2d');const dpr = wx.getWindowInfo().pixelRatio;console.log("default canvas.width:", canvas.width," default canvas.height:", canvas.height);console.log("dpr:", dpr); canvas.width = this.styleWidth* dpr;canvas.height = this.styleHeight * dpr;ctx.scale(dpr, dpr);ctx.fillStyle = "green";ctx.fillRect(0, 0, 50, 50);// 4.1 设置字体大小(两个参数必须都写)ctx.font = "20px sans-serif";// 4.2 写文字ctx.fillText("我是新版canvas", 50, 30);// 4.3 新版 canvas 不需要调用 draw()// ctx.draw(); 
                    }).exec();},}}
</script><style scoped>.divider {margin: 10px 0;}canvas {background-color: antiquewhite;}
</style>            

 三、新版canvas易错api

id不能用数字开头,字符串的数字也不行

括号赋值变成了等号赋值

旧版:  ctx.setFillStyle("red"); ctx.fillRect(50, 50, 75, 75); 

新版:  // 设置的方式从 ctx.setFillStyle("red") 改成了 ctx.fillStyle = "red"; ctx.fillStyle = "red"; ctx.fillRect(50, 50, 75, 75); 

画图片

旧版:  ctx.drawImage("/static/cherry.png, 0,0,100,100"); 

新版: 

const image = canvas.createImage();
image.src = "/static/cherry.png";
image.onload = () => {//等待图片资源加载完成才可以画ctx.drawImage(image, 0, 0,100,100);
}

  

设置文字大小

旧版: ctx.setFontSize(20); ctx.fillText('20', 20, 20); 

新版:  ctx.font = "20px sans-serif"; ctx.fillText("我是新版canvas", 50, 30); 

实时刷新抖动白屏

还有一点要注意,如果需要实时更新数据,就需要canvas不同绘画,但不要多次运行  const ctx = canvas.getContext('2d');  否则页面刷新时会有白屏抖动。

正确的做法是分两个函数,一个初始化context,一个只专心画内容。

下面是我的一部分代码(不能直接运行,删去了部分敏感信息)

<template><view><view v-for="item in tankList"><canvas type="2d" :id="item.id" class="canvas" ></canvas></view></view>
</template><script>export default {props: {tankList: {type: Array,default: []}},data() {return {// 油罐图片的宽高bgimgWidth: 200,bgimgHeight: 150,context: [],bgImg: null}},mounted() {this.createContexts(this.bgimgWidth, this.bgimgHeight);//每次进来的时候先画一次
    },watch: {tankList(newValue, oldValue) {// 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(`#${tank.id}`).node(({ node: canvas }) => {this.context[index] = canvas.getContext('2d');console.log('this.context[] 建立成功');canvas.width = availableWidth * dpr;canvas.height = availableWidth * 0.4 * dpr; //按照下面css的尺寸比例this.context[index].scale(dpr, dpr); //必须有//这里调用一遍,防止等待时间过长this.refreshCanvas(this.bgimgWidth, this.bgimgHeight);}).exec();})},refreshCanvas(bgimgWidth, bgimgHeight) {const context = this.context;// 2. 设置不同油罐油品的颜色let oilColors = [];//存储不同油罐油品的颜色this.tankList.forEach(tank => {oilColors.push(converIntToRgb(tank.oilColor));});this.tankList.forEach((tank, index) => {this.createSelectorQuery().select(`#hc${tank.id}`).node(({ node: canvas }) => {this.bgImg = canvas.createImage();this.bgImg.src = "/static/hcbgimg.png";this.bgImg.onload = () => {// ❗❗❗ 每次画新的之前先清空画布(包括图片)this.context[index].clearRect(0, 0, bgimgWidth, bgimgHeight);// 新版canvas画背景图片,将图片绘制到 canvas 上this.context[index].drawImage(this.bgImg, 0, 0, bgimgWidth, bgimgHeight);// 写详细信息(放外面会被图片挡住,因为图片加载较慢)const textColor = tank.connect ? "black" : "red";context[index].fillStyle = textColor;context[index].font = "20px sans-serif";context[index].fillText(tank.id, bgimgWidth * 0.06, bgimgHeight * 0.6);}}).exec();});},}
}
</script><style scoped>
.canvas {width: 750rpx;height: 300rpx;margin-top: 30rpx;margin-left: 37rpx;margin-right: 37rpx;
}
</style>

 

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

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

相关文章

Java 实现 Excel(XLS/ XLSX)和 HTML 格式之间的转换

Excel 是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,以便更好地利用和展示数据。本文将介绍如何通过 Java 实现 Excel 与 HTML 格式之间的相互转换。将Exce…

Python异步编程终极指南:用协程与事件循环重构你的高并发系统

title: Python异步编程终极指南:用协程与事件循环重构你的高并发系统 date: 2025/2/24 updated: 2025/2/24 author: cmdragon excerpt: 🚀 深入剖析Python异步编程的核心机制。你将掌握:\n 事件循环的底层实现原理与调度算法\n async/await协程的6种高级用法模式\n 异步H…

Uniapp开发安卓app之使用360加固加壳处理

Uniapp开发安卓app之使用360加固加壳处理 注:360加固只针对Android App免费。360加固助手官方下载 1.流程 安卓加固 》 签名APK 2.前提 先使用Hbuilder完成app打包,得到apk文件。 3.安卓加固1)安装、登录360加固软件并打开 2)在安卓加固》APK加固》添加任务,选择hbi…

用python画五角星

import turtle turtle.setup(500,500) turtle.bgcolor("white") turtle.pensize(3) turtle.color("red") turtle.begin_fill() for _ in range(5): turtle.forward(200) turtle.right(144) turtle.end_fill() turtle.hideturtle() turtle.done()

win10忘记开机密码怎么办?

下面我们来看看win10笔记本电脑开机密码忘记了怎么办_ 方法一、重置电脑,但是数据可能会丢失 1、开机,在看到Windows10Logo,下面有个圆圈在转的时候,数5秒。然后直接按下电源键关机。2.桌面,如果您的台式机有重新启动键,也可以直接按重新启动键。这样重复2~3次。 三、鼠标…

mysql知识面试day1

1.介绍mysql MYSQL是一个关系型数据库 2.事务的四大特性 acidA原子性表示要么全部成功要么全部失败 C一致性事务执行前和执行后需要保持一致的状态 I隔离性一个事务只能读到已提交的更改 D持久性事务一旦被提交更改就是永久性的3.数据库的三大范式 第一范式inf:确保字段的原子性…

图像采集卡功能与应用详解

图像采集卡(Image Capture Card),也称为视频采集卡或图像采集设备,是一种用于将摄像头、相机、显微镜、传感器等设备输出的图像或视频信号转换为计算机可处理的数字信号的硬件设备。它在工业检测、医疗影像、机器视觉、安防监控、科学研究等领域有广泛应用。 一、核心功能 …

项目管理工具-禅道(2)

一、bug的等级 (1)1级bug (致命bug) (2)2级bug(严重bug) (3)3级bug(一般bug) (4)4级bug(简易性bug) 划分: 1级bug (致命bug) 必须优先修改,在测试中较少出现,一旦出现应立即中止当前版本测试; 致命bug: (1)常规操作引起的崩溃,死机,死循环,内存泄露…

npccctf第一周wp

1、全网呼叫密码人 题目 from Crypto.Cipher import AES from Crypto.Util.number import * from Crypto.Util.Padding import pad """ Dinzheng先生准备去买一根RuiKeV电子烟,在付款的时候忘记了它的支付密码。 还好他的朋友早有准备,留下了若干个密保问题,…