在画布上以与css边框相同的方式绘制虚线和虚线矩形:绘制4条相同的边

时间:2017-11-14 23:15:05

标签: javascript canvas html5-canvas

我的用例是模仿css边框渲染。是否可以使用CanvasRenderingContext2D::rect方法与CanvasRenderingContext2D::setLineDash模拟与css渲染器相同的边框绘制,例如border: 5px dashed red。考虑这个例子:



let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
ctx.lineWidth = 5
ctx.strokeStyle = 'red'
ctx.lineCap = 'square'
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(2.5,2.5);
ctx.rect(2.5, 2.5, 195, 65);
ctx.stroke();

div {
  border: 5px dashed red;
  width: 200px;
  height: 70px;
  box-sizing: border-box;
  margin-bottom: 5px;
}

canvas {
  display: block;
  width: 200px;
  height: 70px;
}

<div></div>
<canvas width=200 height=70></canvas>
&#13;
&#13;
&#13;

您可能会注意到问题出在边缘。

Not pretty line joins on edges

我试图修改间隙和破折号尺寸,但似乎不可能获得与css示例相同的行为:边缘上的线条比侧面的线条大。作为一种解决方法,我可以想象用一条线绘制每一面,但我想用rect方法一次绘制。

提前谢谢。

1 个答案:

答案 0 :(得分:1)

CSS border-style: dashed算法不受规范约束,因此无法在画布API中呈现完全相同的内容。

然后,您必须知道即使CSS也会逐行呈现:border是所有border-top-XXXborder-right-XXXborder-bottom-XXX,{的简写{1}}。
这就是为什么它表现得如此:每个边界都有独立于其他边界的线条划线。

无论如何,如果你想用canvas API来做,最简单的解决方案是使用四行,并分别设置它们的行 - 。

以下是对短划线进行标准化的粗略尝试,以使它们始终在边缘处开始和结束:

border-left-XXX
var ctx = c.getContext('2d');
ctx.lineCap = 'square';

// returns a normalized dashArray per segment
// This in no way does the same as any browser's implementation,
// this is just a lazy way to always get dashes start and end at edges
function getLineDash(x1, y1, x2, y2) {
  var length = Math.hypot((x2 - x1), (y2 - y1));
  var dash_length = length / 8;
  var nb_of_dashes = length / dash_length;
  var dash_gap = (dash_length * 0.66);
  dash_length -= dash_gap * 0.33;
  return [dash_length, dash_gap];
}

function draw() {
  ctx.lineWidth = lineWidth_.value;
  ctx.clearRect(0, 0, c.width, c.height);

  var points = [
    [x1_.value, y1_.value],
    [x2_.value, y2_.value],
    [x3_.value, y3_.value],
    [x4_.value, y4_.value]
  ];

  points.forEach(function(pt, i) {
    var next = points[(i + 1) % points.length];
    ctx.beginPath();
    ctx.moveTo(pt[0], pt[1]);
    ctx.lineTo(next[0], next[1]);
    ctx.setLineDash(getLineDash(pt[0], pt[1], next[0], next[1]));
    ctx.stroke();
  });

}

draw();
document.oninput = function(e) {
  if (e.target.parentNode.parentNode === inputs_) {
    draw();
  }
}
label {
  display: inline-block;
}

input {
  max-width: 50px;
}

现在,如果您只想使用<div id="inputs_"> <label>x1<input type="number" id="x1_" value="10"></label> <label>y1<input type="number" id="y1_" value="25"></label> <label>x2<input type="number" id="x2_" value="350"></label> <label>y2<input type="number" id="y2_" value="25"></label> <label>x3<input type="number" id="x3_" value="350"></label> <label>y3<input type="number" id="y3_" value="225"></label> <label>x4<input type="number" id="x4_" value="10"></label> <label>y4<input type="number" id="y4_" value="225"></label> <label>lineWidth<input type="number" id="lineWidth_" value="3"></label> </div> <canvas id="c" width="400" height="400"></canvas>,您还可以创建一个包含所有破折号的巨大破折号数组......

XXXRect
var ctx = c.getContext('2d');
ctx.lineCap = 'square';

function getRectDashes(width, height) {
  var w_array = getLineDashes(width, 0, 0, 0);
  var h_array = getLineDashes(0, height, 0, 0);
  dashArray = [].concat.apply([], [w_array, 0, h_array, 0, w_array, 0, h_array]);
  return dashArray;
}
// same as previous snippet except that it does return all the segment's dashes
function getLineDashes(x1, y1, x2, y2) {
  var length = Math.hypot((x2 - x1), (y2 - y1));
  var dash_length = length / 8;
  var nb_of_dashes = length / dash_length;

  var dash_gap = dash_length * 0.66666;
  dash_length -= dash_gap * 0.3333;

  var total_length = 0;
  var dasharray = [];
  var next;
  while (total_length < length) {
    next = dasharray.length % 2 ? dash_gap : dash_length;
    total_length += next;
    dasharray.push(next);
  }
  return dasharray;
}

function draw() {
  ctx.clearRect(0, 0, c.width, c.height);
  ctx.lineWidth = lineWidth_.value;
  var w = width_.value,
    h = height_.value;
  ctx.setLineDash(getRectDashes(w, h));
  ctx.strokeRect(20, 20, w, h);
}
draw();
document.oninput = function(e) {
  if (e.target.parentNode.parentNode === inputs_)
    draw();
};
label {
  display: inline-block;
}

input {
  max-width: 50px;
}