使用 D3 线在画布上绘制孤立点

问题描述 投票:0回答:1

我正在使用画布使用 D3 线生成器,我注意到,如果

lineCap
设置为
round
square
,Chrome 在显示独立数据点时的行为与其他浏览器不同。

在下面的屏幕截图中,我绘制了以下数据集的三倍。

const datapoints = [0, 1, null, 3, null, 5, 6, 7, 8];

数据点

3
是孤立的,因为它的前后是
null
和线生成器组

line().defined(d => d !== null)

因此点

1-3
3-5
之间会有间隙。

所有三条线都使用相同的线发生器。三者之间的区别在于,红线有

lineCap = 'butt'
,蓝线有
lineCap = 'round'
,绿线有
lineCap = butt
,但它在数据点不是
null
的地方添加了圆圈。

从屏幕截图中可以看到,在 Chrome 中,蓝线呈现孤立的数据点,而在 Firefox(和 Safari)上则不然。

我承认 Chrome 的行为很方便,因为我可以在屏幕上获得孤立的数据点,而无需手动放置它们。你知道是否有办法在 Firefox(和 Safari)上达到相同的结果?

您可以在此处找到代码。

截图

代码

这是处理绘图的代码中有意义的部分,但是您可以在此沙箱中找到代码。

const brush = line()
  .defined((d) => d !== null)
  .x((d) => scale(d))
  .curve(curveLinear)
  .context(ctx);

function drawLine(
  data,
  y,
  { color = "black", lineCap = "butt", points = false }
) {
  ctx.save();
  ctx.strokeStyle = color;
  ctx.lineWidth = 2;
  ctx.lineCap = lineCap;
  ctx.beginPath();
  brush.y(y)(data);

  if (points) {
    data.forEach((point) => {
      ctx.moveTo(scale(point), y);
      ctx.arc(scale(point), y, 1, 0, Math.PI * 2);
    });
  }

  ctx.stroke();
  ctx.restore();
}

const datapoints = [0, 1, null, 3, null, 5, 6, 7, 8];

drawLine(datapoints, 120, { color: "darkred" });
drawLine(datapoints, 140, { color: "darkblue", lineCap: "round" });
drawLine(datapoints, 160, { color: "darkgreen", points: true });

进一步调查

通过跟踪D3代码,我发现Chrome在时绘制了一个由线帽

组成的点
  1. 移动到某个坐标
  2. 关闭当前子路径
  3. 又搬家了

通过删除第二个

moveTo
调用,不会显示任何内容。

我不确定这种行为是否应该被视为错误,因为在高级别上,我们可以看到上面描述的序列,即画笔移动到某个位置,然后再次移动,从不接触画布。

ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(160, 20);
ctx.closePath();
ctx.moveTo(161, 20); // < this
ctx.stroke();
javascript d3.js firefox html5-canvas visualization
1个回答
0
投票

嗯,抚摸零长度线而不关闭路径,适用于 Firefox/Win 中的

'square'
'round'
线帽。 (
'butt'
盖子不会被渲染,只是因为它们不会向最终形状添加任何区域,因此空的部分仍然是空的。)

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(0 0 0 / 10%)';
ctx.fillRect(0, 0, canvas.width, canvas.height);

function strokeZeroLine (x, y, lineCap) {
  ctx.lineCap = lineCap;
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x, y);
  ctx.stroke();
}

ctx.lineWidth = 10;
strokeZeroLine(40, 20, 'butt');
strokeZeroLine(40, 40, 'square');
strokeZeroLine(40, 60, 'round');
<canvas id="canvas" width="80" height="80"></canvas>

© www.soinside.com 2019 - 2024. All rights reserved.