在知识的海洋狗刨,使用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

相关文章

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

MySQL 强制使用特定索引的操作

《MySQL强制使用特定索引的操作》MySQL可通过FORCEINDEX、USEINDEX等语法强制查询使用特定索引,但优化器可能不采纳,需结合EXPLAIN分析执行计划,避免性能下降,注意版本差异... 目录1. 使用FORCE INDEX语法2. 使用USE INDEX语法3. 使用IGNORE IND

C# $字符串插值的使用

《C#$字符串插值的使用》本文介绍了C#中的字符串插值功能,详细介绍了使用$符号的实现方式,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录$ 字符使用方式创建内插字符串包含不同的数据类型控制内插表达式的格式控制内插表达式的对齐方式内插表达式中使用转义序列内插表达式中使用

flask库中sessions.py的使用小结

《flask库中sessions.py的使用小结》在Flask中Session是一种用于在不同请求之间存储用户数据的机制,Session默认是基于客户端Cookie的,但数据会经过加密签名,防止篡改,... 目录1. Flask Session 的基本使用(1) 启用 Session(2) 存储和读取 Se