我的用例是模仿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;
您可能会注意到问题出在边缘。
我试图修改间隙和破折号尺寸,但似乎不可能获得与css示例相同的行为:边缘上的线条比侧面的线条大。作为一种解决方法,我可以想象用一条线绘制每一面,但我想用rect
方法一次绘制。
提前谢谢。
答案 0 :(得分:1)
CSS border-style: dashed
算法不受规范约束,因此无法在画布API中呈现完全相同的内容。
然后,您必须知道即使CSS也会逐行呈现:border
是所有border-top-XXX
,border-right-XXX
,border-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;
}