在路径表面外部绘制轮廓

Dom*_*ino 1 html javascript canvas

我有以下代码来绘制形状(主要用于矩形),但 HTML5 绘图函数似乎绘制边框,其厚度以指定的线条为中心。我想在形状表面之外有一个边框,但我不知所措。

Path.prototype.trace = function(elem, closePath) {
  sd.context.beginPath();
  sd.context.moveTo(this.getStretchedX(0, elem.width), this.getStretchedY(0, elem.height));
  sd.context.lineCap = "square";

  for(var i=1; i<this.points.length; ++i) {
    sd.context.lineTo(this.getStretchedX(i, elem.width), this.getStretchedY(i, elem.height));
  }

  if(closePath) {
    sd.context.lineTo(this.getStretchedX(0, elem.width), this.getStretchedY(0, elem.height));
  }
}
Run Code Online (Sandbox Code Playgroud)

一旦形状应用于设置的元素宽度、高度和偏移位置,getStretchedX 和 getStretchedY 返回第 n 个顶点的坐标。


感谢 Ken Fyrstenberg 的回答,我已经让它适用于矩形,但遗憾的是这个解决方案不适用于其他形状。

http://jsfiddle.net/0zq9mrch/

失败的三角形

在这里,我画了两个“宽”边框,一个在每个位置减去一半的线宽,另一个添加。它不起作用(如预期),因为它只会在一种情况下将粗线放在上方和左侧,在另一种情况下将粗线放在下方和右侧 - 而不是形状“外部”。您还可以看到斜坡周围有一个白色区域。


我尝试弄清楚如何让顶点手动绘制粗边框的路径(使用fill()而不是stroke())。

显示顶点的粗三角形

但事实证明我仍然遇到同样的问题:如何以编程方式确定边缘是内部还是外部。这需要一些三角学和繁重的算法。对于我现在的工作目的来说,这太麻烦了。我想用它来绘制建筑物的地图。房间墙壁需要绘制在给定尺寸之外,但我现在将坚持使用独立的倾斜墙壁。

小智 5

解决方案

您可以通过画两条线来解决这个问题:

  • 第一行的线条粗细符合预期
  • 第二条线收缩为外线宽度的 50%

要收缩,请将 x 和 y 添加 50%,从宽度和高度中减去线宽(或 2x 50%)。

例子

折断

var ctx = document.querySelector("canvas").getContext("2d");
var lineWidth = 20;
var lw50 = lineWidth * 0.5;

// outer line
ctx.lineWidth = lineWidth;         // intended line width
ctx.strokeStyle = "#975";          // color for main line
ctx.strokeRect(40, 40, 100, 100);  // full line

// inner line
ctx.lineWidth = 2;                 // inner line width
ctx.strokeStyle = "#000";          // color for inner line

ctx.strokeRect(40 + lw50, 40 + lw50, 100 - lineWidth, 100 - lineWidth);
Run Code Online (Sandbox Code Playgroud)
<canvas></canvas>
Run Code Online (Sandbox Code Playgroud)

复杂的形状

快照复合体

对于更复杂的形状,您将必须手动计算路径。这有点复杂,而且对于 SO 来说可能太宽泛了。您必须考虑诸如切线、弯曲角度、交叉点等因素。

“作弊”的一种方法是:

  • 在画布上绘制全厚度的主线
  • 然后使用重用路径作为剪贴蒙版
  • 将复合模式更改为destination-atop
  • 绘制在各个方向上偏移的形状
  • 恢复剪裁
  • 更改主线的颜色并再次重复使用路径。

下面的值offset将决定内线的粗细,而 则directions决定分辨率。

var ctx = document.querySelector("canvas").getContext("2d");
var lineWidth = 20;
var offset = 0.5;                                   // line "thickness"
var directions = 8;                                 // increase to increase details
var angleStep = 2 * Math.PI / 8;

// shape
ctx.lineWidth = lineWidth;                          // intended line width
ctx.strokeStyle = "#000";                           // color for inner line
ctx.moveTo(50, 100);                                // some random shape
ctx.lineTo(100, 20);
ctx.lineTo(200, 100);
ctx.lineTo(300, 100);
ctx.lineTo(200, 200);
ctx.lineTo(50, 100);
ctx.closePath();
ctx.stroke();

ctx.save()

ctx.clip();                                         // set as clipping mask
ctx.globalCompositeOperation = "destination-atop";  // draws "behind" existing drawings

for(var a = 0; a < Math.PI * 2; a += angleStep) {
  ctx.setTransform(1,0,0,1, offset * Math.cos(a), offset * Math.sin(a));
  ctx.drawImage(ctx.canvas, 0, 0);
}

ctx.restore();                              // removes clipping, comp. mode, transforms

// set new color and redraw same path as previous
ctx.strokeStyle = "#975";                           // color for inner line
ctx.stroke();
Run Code Online (Sandbox Code Playgroud)
<canvas height=250></canvas>
Run Code Online (Sandbox Code Playgroud)