canvas如何绘制标签,在知识的海洋狗刨(下)

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

接下来只需要设定参数调用即可


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()属性:复制画布上指定矩形的像素数据


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


目录
打赏
0
0
0
0
1
分享
相关文章
webpack 打包去掉控制台的console.log()
webpack 打包去掉控制台的console.log()
1010 0
webpack 打包去掉控制台的console.log()
vue3:vue3.2升级至vue3.3/vue升级版本
vue3:vue3.2升级至vue3.3/vue升级版本
989 0
Vue3气泡卡片(Popover)
这是一个基于Vue的气泡卡片组件(Popover)介绍,提供了在线预览链接及详细API参数说明,包括maxWidth、title、content等,并支持自定义样式。
469 0
Vue3气泡卡片(Popover)
一文浅谈CodeReview中的一些思考
CodeReview在日常的开发过程中越来越被重视,它在提高代码质量同时促进团队成员之间的知识共享和技能提升方面发挥了诸多作用,本文将主要围绕CodeReview展开,简单聊聊在CodeReview过程中的心得和思考。
【亲测有效】支持横竖屏 微信小程序video禁止进度条拖动,微信小程序遮罩进度条,
【亲测有效】支持横竖屏 微信小程序video禁止进度条拖动,微信小程序遮罩进度条,
307 1
【亲测有效】支持横竖屏 微信小程序video禁止进度条拖动,微信小程序遮罩进度条,
前端实战:基于Verdaccio搭建私有npm仓库,轻松上传与下载自定义npm插件包
前端实战:基于Verdaccio搭建私有npm仓库,轻松上传与下载自定义npm插件包
612 0
找不到模块“./App.vue”或其相应的类型声明。ts(2307)
这篇文章介绍了在Vue 3 + TypeScript + Vite开发环境中解决找不到`.vue`文件模块或其类型声明错误的两种方法:使用VSCode的TypeScript Vue Plugin (Volar)插件或手动在`env.d.ts`文件中声明`*.vue`模块类型。
1647 1
找不到模块“./App.vue”或其相应的类型声明。ts(2307)
在HTTPS安全页面中加载HTTP不安全的内容,如何绕过安全警告?
在HTTPS安全页面中加载HTTP不安全的内容,如何绕过安全警告?
634 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问