在知识的海洋狗刨,使用canvas绘制标签

2024-03-17 15:30

本文主要是介绍在知识的海洋狗刨,使用canvas绘制标签,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

下面总结了一些canvas绘制标签的心得体会,希望能够对你有所帮助。

绘制圆弧

<canvas id="canvas" width="300" height="300" ref="canvas"></canvas>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(150,150,50,0,Math.PI*1/2);
ctx.stroke();

运行结果:

绘制直线

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(200,150);
ctx.stroke();

运行结果:

绘制圆角矩形

通过圆弧与直线相结合,绘制出矩形路径。

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');var width = 100;
var height = 50;
var radius = 5;ctx.translate(100, 100);
ctx.beginPath(0);
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
ctx.lineTo(radius, height);
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
ctx.lineTo(0, radius);
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
ctx.lineTo(width - radius, 0);
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
ctx.lineTo(width, height - radius);
ctx.closePath();
// 填充背景色
ctx.fillStyle = "#ff6a61";
ctx.fill();
ctx.restore();

运行结果:

填充文本

...
ctx.font = '16px PingFangSC-Regular';
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillStyle = '#fff';
ctx.fillText('快狗打车', 50, 25);

textBaseline 属性设置或返回在绘制文本时的当前文本基线。

运行结果:

宽度自适应

标签宽度设置定值的做法难免有些耍流氓,怎样才能实现标签宽度由文本内容撑起?canvas为我们提供了ctx.measureText(text).width接口获得文本内容宽度;标签宽度 = 文本内容宽度 + 左右内边距。

先把绘制圆角矩形背景的部分单独抽出来:

function drawRoundRect(ctx, x, y, width, height, radius, bgc) {ctx.save();ctx.translate(x, y);drawRoundRectPath(ctx, width, height, radius);ctx.fillStyle = bgc;ctx.fill();ctx.restore();
}
function drawRoundRectPath(ctx, width, height, radius) {ctx.beginPath(0);ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);ctx.lineTo(radius, height);ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);ctx.lineTo(0, radius);ctx.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2);ctx.lineTo(width - radius, 0);ctx.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2);ctx.lineTo(width, height - radius);ctx.closePath();
}
};
复制代码
接下来只需要设定参数调用即可  var canvas = document.getElementById("canvas");var ctx = canvas.getContext("2d");ctx.font = "16px PingFangSC-Regular";ctx.textAlign = "center";ctx.textBaseline = "middle";ctx.fillStyle = "#fff";var config = {paddingLeft: 20, // 文本左内边距paddingRight: 20, // 文本右内边距labelHeight: 50, // 标签高度labelRadius: 5, // 圆角labelBackgroundColor: "#ff6a61" // 标签背景色};var x = 100;var y = 100;var str = "快狗打车";var textWidth = ctx.measureText(str).width;drawRoundRect( ctx, x, y, textWidth + config.paddingLeft + config.paddingRight, config.labelHeight, config.labelRadius, config.labelBackgroundColor);ctx.fillText( str, x + config.paddingLeft + textWidth / 2, y + config.labelHeight / 2 );var x = 100;var y = 200;var str = "快狗打车-前端团队";var textWidth = ctx.measureText(str).width;drawRoundRect( ctx, x, y, textWidth + config.paddingLeft + config.paddingRight, config.labelHeight, config.labelRadius, config.labelBackgroundColor);ctx.fillText( str, x + config.paddingLeft + textWidth / 2, y + config.labelHeight / 2 );

运行结果:

measureText() 方法返回包含一个对象,该对象包含以像素计的指定字体宽度。


多标签自动换行

如何实现绘制多标签自动换行?

标签的实际占用空间宽度 = 文本宽度 + 左右内边距 + 左右外边距

遍历所有标签文本,根据限定空间宽度与标签的实际占用空间宽度计算出每一个标签的具体坐标值。

配置参数:

var labelList = [{ "id": 1, "name": "小型面包" },{ "id": 2, "name": "金杯" },{ "id": 3, "name": "依维柯" },{ "id": 4, "name": "商务车" },{ "id": 5, "name": "皮卡" },{ "id": 6, "name": "冷藏车" },{ "id": 7, "name": "平板货车" },{ "id": 8, "name": "高栏货车" },{ "id": 9, "name": "宽体尾板" },{ "id": 10, "name": "厢式货车" },{ "id": 11, "name": "其它" }
]
var config = {// 标签范围参数spaceX: 0, // x坐标spaceY: 0, // y坐标spaceWidth: 300, // 宽度spaceHeight: 300, // 高度// 标签参数paddingRight: 10, // 文本至左边框距离paddingLeft: 10, // 文本至右边框距离marginTop: 0, // 上外边界marginRight: 10, // 右外边界marginBottom: 10, // 下外边界marginLeft: 0, // 左外边界labelHeight: 30, // 高度labelRadius: 5, // 圆角labelBackgroundColor: '#ff6a61', // 背景色// 字体参数fontSize: 12, // 字体大小fontColor: '#fff', // 字体颜色fontFamily: 'PingFangSC-Regular', // 字体类型
}

遍历标签列表计算出每一个标签具体参数:

function formatLine(ctx, list, config) {let labelLine = [];let lineIndex = 0;let usedWidth = 0;list.forEach(item => {item.textWidth = ctx.measureText(item.name).width; // 文字占据空间let labelSpace = item.textWidth + config.paddingLeft + config.paddingRight + config.marginLeft + config.marginRight; // 标签实际占据宽度if(usedWidth + labelSpace > config.spaceWidth) {usedWidth = 0;lineIndex = lineIndex + 1;}item.x = config.spaceX + usedWidth + config.marginLeft;item.y = config.spaceY + lineIndex * (config.labelHeight + config.marginTop + config.marginBottom) + config.marginTop;labelLine.push(item);usedWidth = usedWidth + labelSpace;});return labelLine
}

接下来就是遍历标签进行绘制了:

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
// 填充背景色,以便于观察边界
ctx.fillStyle = "#ccc";
ctx.fillRect(config.spaceX, config.spaceY, config.spaceWidth, config.spaceHeight);let labelLine = formatLine(ctx, labelList, config);
drawLabel(ctx, labelLine, config);function drawLabel(ctx, labelLine, config) {ctx.font = `${config.fontSize}px ${config.fontFamily}`;ctx.textAlign = "center";ctx.textBaseline = 'middle';ctx.fillStyle = config.fontColor;labelLine.map((item)=>{drawRoundRect(ctx, item.x, item.y, item.textWidth + config.paddingLeft + config.paddingRight , config.labelHeight , config.labelRadius , config.labelBackgroundColor);ctx.fillText(item.name, item.x + config.paddingLeft + item.textWidth/2, item.y + config.labelHeight/2);})
}

运行结果:

延伸

那么到这里标签的绘制已经结束。鬼机灵的小伙伴可能会想到既然ctx.measureText(text).width可以获得文本实际占据宽度,那ctx.measureText(text).height肯定就是获取文本实际占据高度了呗(俺也一样),很遗憾并不是_(°:з」∠)_)

canvas并没有提供获取文本高度的接口,需要通过其他方式间接获取。所幸,我还是百度到了一种解决方案(百度打钱_(°:з」∠)_)——通过获取指定范围内的所有像素数据,计算非白色像素点之间的最大高度差值即为文本实际像素高度。说人话就是找出第一个与最后一个非白色像素点,两者所在像素行之间的差值即为文本实际高度。

核心代码如下:

function measureTextHeight(ctx, x, y, width, height) {// 从画布获取像素数据var data = ctx.getImageData(x, y, width, height).data,first = false,last = false,r = height,c = 0;// 找到最后一行非白色像素while (!last && r) {r--;for (c = 0; c < width; c++) {if (data[r * width * 4 + c * 4 + 3]) {last = r;break;}}}// 找到第一行非白色像素while (r) {r--;for (c = 0; c < width; c++) {if (data[r * width * 4 + c * 4 + 3]) {first = r;break;}}if (first != r) return last - first;}return 0;
}

getImageData()属性:复制画布上指定矩形的像素数据

这种方法简单粗暴,但是局限性也非常明显,先绘制后获取数据等于耍流氓。小伙伴们还有除此之外的方法不妨在评论区探讨一下。

推荐阅读

  • 前端获取微信头像 base64 数据的踩坑实践

  • 使用vue实现HTML页面生成图片

  • 如何实现微信小程序图像剪切?代码拿去用,不谢!

好文我在看????

这篇关于在知识的海洋狗刨,使用canvas绘制标签的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/819353

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他