canvas如何繪製標籤,在知識的海洋狗刨

下面總結了一些canvas繪製標籤的心得體會,但願可以對你有所幫助。javascript

繪製圓弧


<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();
複製代碼

運行結果: php

繪製直線


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

運行結果: html

繪製圓角矩形


經過圓弧與直線相結合,繪製出矩形路徑。前端

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();
複製代碼

運行結果: java

填充文本c++

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

textBaseline 屬性設置或返回在繪製文本時的當前文本基線。canvas

運行結果: 字體

寬度自適應


標籤寬度設置定值的作法不免有些耍流氓,怎樣才能實現標籤寬度由文本內容撐起?canvas爲咱們提供了ctx.measureText(text).width接口得到文本內容寬度;標籤寬度 = 文本內容寬度 + 左右內邊距。ui

先把繪製圓角矩形背景的部分單獨抽出來:spa

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()屬性:複製畫布上指定矩形的像素數據

這種方法簡單粗暴,可是侷限性也很是明顯,先繪製後獲取數據等於耍流氓。小夥伴們還有除此以外的方法不妨在評論區探討一下。

關注咱們

公衆號@前端論道
相關文章
相關標籤/搜索