在 HTML canvas 线向量的左侧或右侧绘制不同宽度的线
Draw line with different widths on left or right side of line vector in HTML canvas
我正在 html canvas
上工作,我正在 canvas 上画一些线。这个过程很简单。但现在我想在相同坐标处绘制两条线,但宽度不同,但一条在实际线坐标的右侧,另一条在左侧。为了更清楚让我们看看这个例子
https://jsfiddle.net/am2222/2oqhLfd9/
如您所见,当我们绘制一条宽度为 x
的线时,它将围绕线延伸 x/2
像素。我想得到这样的东西:
如您所见,第二个示例线宽仅在一侧延伸。我知道我可以通过在一侧偏移 x/2
的线来得到这个,但我不确定计算如何在 canvas 上进行。
Ps:
我想在具有相同坐标的不同样式的 end.Two 行中得到类似的东西
在这个例子中,我采用了另一种方法。我创建了一个 SVG 图像,将其加载到一个图像对象中,然后将其绘制在 canvas 上。 SVG 当然可以是 hidden/only 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>
这可能会有所帮助。我用一个标志功能化了渐变线的绘制以绘制黑色下划线。
该函数采用坐标、线宽和渐变颜色。
这可能是您探索的起点。可能有一些边缘情况我错过了这个功能会崩溃的地方。
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);
<canvas id=canvas1 width=300 height=600></canvas>
只需在 canvas 周围移动并画线即可。我在这里画了三条线,其中两条是渐变的,一条是黑色的。
线条的替代方法可以是 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();
<canvas width="300" height="200"></canvas>
这也是我自己的方法
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);
我正在 html canvas
上工作,我正在 canvas 上画一些线。这个过程很简单。但现在我想在相同坐标处绘制两条线,但宽度不同,但一条在实际线坐标的右侧,另一条在左侧。为了更清楚让我们看看这个例子
https://jsfiddle.net/am2222/2oqhLfd9/
如您所见,当我们绘制一条宽度为 x
的线时,它将围绕线延伸 x/2
像素。我想得到这样的东西:
如您所见,第二个示例线宽仅在一侧延伸。我知道我可以通过在一侧偏移 x/2
的线来得到这个,但我不确定计算如何在 canvas 上进行。
Ps:
我想在具有相同坐标的不同样式的 end.Two 行中得到类似的东西
在这个例子中,我采用了另一种方法。我创建了一个 SVG 图像,将其加载到一个图像对象中,然后将其绘制在 canvas 上。 SVG 当然可以是 hidden/only 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>
这可能会有所帮助。我用一个标志功能化了渐变线的绘制以绘制黑色下划线。
该函数采用坐标、线宽和渐变颜色。
这可能是您探索的起点。可能有一些边缘情况我错过了这个功能会崩溃的地方。
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);
<canvas id=canvas1 width=300 height=600></canvas>
只需在 canvas 周围移动并画线即可。我在这里画了三条线,其中两条是渐变的,一条是黑色的。
线条的替代方法可以是 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();
<canvas width="300" height="200"></canvas>
这也是我自己的方法
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);