【问题标题】:Draw line with different widths on left or right side of line vector in HTML canvas在 HTML 画布中在线矢量的左侧或右侧绘制不同宽度的线条
【发布时间】:2021-11-23 20:20:50
【问题描述】:

我正在处理html canvas,我正在画布上绘制一些线条。这个过程很简单。但现在我想在同一坐标处绘制两条宽度不同的线,但一条在实际线坐标的右侧,一条在左侧。为了更清楚,让我们看一下这个例子 https://jsfiddle.net/am2222/2oqhLfd9/

如您所见,当我们画一条宽度为x 的线时,它将围绕该线延伸x/2 像素。我想得到这样的东西:

如您所见,第二个示例线宽仅在一侧延伸。我知道我可以通过在一侧偏移x/2 的线来得到这个,但我不确定计算将如何在画布上工作。 附言: 我想在最后得到这样的东西。两条具有相同坐标的不同样式的线

【问题讨论】:

  • 线条是动态的,比如改变长度、颜色、位置、宽度等,还是只绘制一次?
  • 它们是动态的,它们的长度和颜色会发生变化,但它们的宽度将相同。它们也可以在任何方向,并且在每个画布中可以有不止一条线。但是一旦它们被绘制出来,我就会缓存它们。每个画布是一个地图图块。

标签: javascript html canvas


【解决方案1】:

在这个例子中,我采用了另一种方法。我创建了一个 SVG 图像,将其加载到图像对象中,然后将其绘制在画布上。 SVG 当然可以在 JS 中隐藏/仅是一个字符串。

我不知道它的性能,所以它可能不适用于您的上下文 - 我只是觉得它是解决问题的一种有趣方式。

var svg = document.querySelector('svg');
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

var img = new Image(300, 30);

img.addEventListener('load', e => {
  ctx.drawImage(e.target, 0, 0);
});

var imagesrc = btoa(svg.outerHTML);

img.src = `data:image/svg+xml;base64,${imagesrc}`;
<svg viewBox="0 0 200 20" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="lg1" gradientUnits="userSpaceOnUse">
      <stop offset="0"  stop-color="orange" />
      <stop offset="1" stop-color="red" />
    </linearGradient>
    <linearGradient id="lg2" gradientUnits="userSpaceOnUse">
      <stop offset="0"  stop-color="navy" />
      <stop offset="1" stop-color="darkgreen" />
    </linearGradient>
  </defs>
  <line x1="0" y1="5" x2="200" y2="5" stroke-width="10" stroke="url(#lg1)" />
  <line x1="0" y1="15" x2="200" y2="15" stroke-width="10" stroke="url(#lg2)" />
  <line x1="0" y1="10" x2="200" y2="10" stroke-width="2" stroke="black" />
</svg>

<canvas width="300" height="30"></canvas>

【讨论】:

  • 不幸的是,我需要从坐标中绘制这些线,而不能使用 svg 来绘制。除非 svg 中有类似的功能。还有哪一个做得更好? svg 还是画布?
【解决方案2】:

这可能会有所帮助。我使用标志功能化了渐变线的绘制以绘制黑色下划线。

该函数获取坐标、线宽和渐变颜色。

这可能是您探索的起点。可能有一些我错过了这个函数会崩溃的边缘情况。

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

function distance( a, b ) {
  return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );
}

function drawGradientLine( line_width, grad_1, grad_2, xy1, xy2, underline ) {
  var grad = ctx.createLinearGradient(xy1.x, xy1.y, xy2.x, xy2.y);
  grad.addColorStop(0, grad_1);
  grad.addColorStop(1, grad_2);
  
    ctx.save();  
  ctx.lineWidth = line_width;
  ctx.strokeStyle = grad;
  ctx.beginPath();
    ctx.moveTo(xy1.x, xy1.y);
    ctx.lineTo(xy2.x, xy2.y);
    ctx.stroke();
  ctx.restore();
  
  if ( underline ) {
    const linelen = distance( xy1, xy2);
    const hyp1 = line_width / 2;
    const angle = Math.asin( (xy2.y - xy1.y) / (linelen) );
    const dy = (angle < 0) ? -1 * hyp1 * Math.cos( angle ) : hyp1 * Math.cos( angle );
    const dx = (angle < 0) ? -1 * hyp1 * Math.sin( angle ) : hyp1 * Math.sin( angle );
 
    const c1 = {
        x: xy1.x - dx, 
      y: xy1.y + dy
    };
    const c2 = {
        x:xy2.x - dx, 
      y: xy2.y + dy
    };
    
    ctx.save();  
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'black';
    ctx.beginPath();
    ctx.moveTo(c1.x, c1.y);
    ctx.lineTo(c2.x, c2.y);
    ctx.stroke();
    ctx.restore();
  }
}



drawGradientLine( 20, 'red', 'green', {x:50, y: 150}, {x:150, y:150}, true);


drawGradientLine( 20, 'pink', 'orange', {x:10, y: 10}, {x:50, y:50}, true);

drawGradientLine( 20, 'pink', 'orange', {x:200, y: 100}, {x:250, y:20}, true);


drawGradientLine( 10, 'blue', 'green', {x: 50, y: 350}, {x:150, y:450}, false);


drawGradientLine( 40, 'pink', 'aquamarine', {x: 100, y: 200}, {x: 150, y:350}, false);
&lt;canvas id=canvas1 width=300 height=600&gt;&lt;/canvas&gt;

【讨论】:

  • 谢谢,太好了,我实际上可以将它扩展成两行。一个在顶部,一个在底部,有没有办法对fillRec 做同样的事情?喜欢拥有xy1, xy2 并绘制一个固定宽度的矩形?
【解决方案3】:

这只是在画布上移动并绘制线条的问题。这里我画了三条线,两条是渐变的,一条是黑色的。

线的替代方案可以是CanvasRenderingContext2D.fillRect(),您可以在其中定义 x、y、宽度和高度。然后更容易计算线条的位置。

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');


let gradient1 = ctx.createLinearGradient(0, 0, 280, 0);
gradient1.addColorStop(0, 'green');
gradient1.addColorStop(1, 'blue');
ctx.strokeStyle = gradient1;
ctx.lineWidth = 10;

ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(290, 10);
ctx.stroke();

let gradient2 = ctx.createLinearGradient(0, 0, 280, 0);
gradient2.addColorStop(0, 'red');
gradient2.addColorStop(1, 'green');
ctx.strokeStyle = gradient2;
ctx.lineWidth = 10;

ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(290, 20);
ctx.stroke();

ctx.strokeStyle = 'black';
ctx.lineWidth = 1;

ctx.beginPath();
ctx.moveTo(10, 15);
ctx.lineTo(290, 15);
ctx.stroke();
&lt;canvas width="300" height="200"&gt;&lt;/canvas&gt;

【讨论】:

  • 感谢您的示例。我已经知道这种方法。但我需要坐标计算。就像我有一条线的线坐标 (x1,y1) 和 (x2,y2) 并想在这条线周围绘制这两条单线。我也喜欢你的fillRec 方法。你能给我一些想法如何使用它来获得 AB 坐标吗?考虑到线也可以在任何方向上的事实
【解决方案4】:

这也是我自己的方法



function distance( a, b ) {
  return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );
}

function drawRec(width, grad_1, grad_2, a, b, direction=1,underline  ){

var l = distance(a,b);

var ao = Math.pow(l,2)+Math.pow(width,2);
var v={x:b.x - a.x,y:b.y - a.y};
var normalized = {
x: (b.x - a.x )/l,
y: (b.y- a.y )/l
}

var uv = {
x: - normalized.y,
y: normalized.x
}


var c = {
x: b.x + (direction)* (width*uv.x),
y: b.y + (direction)* (width*uv.y),
}

var d = {
x: a.x + (direction)* (width*uv.x),
y: a.y + (direction)* (width*uv.y),
}
  var grad = ctx.createLinearGradient(a.x, a.y, b.x, b.y);
  grad.addColorStop(0, grad_1);
  grad.addColorStop(1, grad_2);


// First path
let region = new Path2D();
region.moveTo(a.x, a.y);
region.lineTo(b.x, b.y);
region.lineTo(c.x, c.y);
region.lineTo(d.x, d.y);
region.closePath();

ctx.fillStyle = grad;
ctx.fill(region, 'evenodd');
ctx.lineWidth = 2;
ctx.strokeStyle = 'orange';
ctx.stroke(region);

if(underline){

ctx.lineWidth = 3;

// linear gradient from start to end of line

ctx.strokeStyle = 'white';

ctx.beginPath();
ctx.moveTo(a.x,a.y);
ctx.lineTo(b.x,b.y);


ctx.stroke();
}
}



var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

drawRec(30,'green','blue',{x:50, y: 150},{x:150, y:20},1,true);
drawRec(30,'green','red',{x:50, y: 150},{x:150, y:20},-1);

查看实际情况 https://jsfiddle.net/am2222/6zgh2ub0/41/

【讨论】:

    猜你喜欢
    • 2019-08-14
    • 2017-12-28
    • 1970-01-01
    • 2021-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多