leeoniya / leeoniya/uPlot

don't use center-aligned strokes for bars

Open
#628 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
JavaScript
Stars
10.5k
Forks
463
PR merge metrics
No merged PRs in 30d

Description

not quite sure if this is a net positive or not, but drawing crisp bars with canvas-native strokes is a real pain in the ass with a lot of accidental edge cases. for a long time i've been trying to figure out how to do performant masking in canvas (the opposite of clipping) for uPlot's purposes. today i finally figured it out.

  1. the first one is just what canvas does natively.-
  2. the second if we add some math, but it still bleeds stroke and can end up accidentally anti-aliased (non crisp). this is what uPlot currently does.
  3. the last is the holy grail, where the stroke is drawn using an additional fill with a mask. the math is simple and always exact, and never bleeds.

https://jsfiddle.net/eaLfo8zn/

image

<canvas id="can2" width="300" height="150"></canvas>
<canvas id="can3" width="300" height="150"></canvas>
<canvas id="can1" width="300" height="150"></canvas>
canvas {
  image-rendering: pixelated;
}
let strokeWidth = 9;

var c = document.getElementById("can1");
var ctx = c.getContext("2d");

let s = new Path2D();
let f = new Path2D();

s.rect(0, 0, 150, 100);
f.rect(0 + strokeWidth, 0 + strokeWidth, 150 - strokeWidth * 2, 100 - strokeWidth * 2);

ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.moveTo(75 + 0.5, 0);
ctx.lineTo(75 + 0.5, 100);
ctx.stroke();

ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.fill(f);

ctx.fillStyle = "rgba(0,0,255,0.5)";
s.addPath(f);
ctx.fill(s, 'evenodd');


var c2 = document.getElementById("can2");
var ctx2 = c2.getContext("2d");

ctx2.strokeStyle = 'black';
ctx2.lineWidth = 1;
ctx2.moveTo(75 + 0.5, 0);
ctx2.lineTo(75 + 0.5, 100);
ctx2.stroke();

let s2 = new Path2D();

s2.rect(0, 0, 150, 100);

ctx2.lineWidth = strokeWidth;
ctx2.fillStyle = "rgba(255,0,0,0.5)";
ctx2.fill(s2);

ctx2.strokeStyle = "rgba(0,0,255,0.5)";
ctx2.stroke(s2);


var c3 = document.getElementById("can3");
var ctx3 = c3.getContext("2d");

ctx3.strokeStyle = 'black';
ctx3.lineWidth = 1;
ctx3.moveTo(75 + 0.5, 0);
ctx3.lineTo(75 + 0.5, 100);
ctx3.stroke();

let s3 = new Path2D();

s3.rect(0 + strokeWidth / 2, 0 + strokeWidth / 2, 150 - strokeWidth, 100 - strokeWidth);

ctx3.lineWidth = strokeWidth;
ctx3.fillStyle = "rgba(255,0,0,0.5)";
ctx3.fill(s3);

ctx3.strokeStyle = "rgba(0,0,255,0.5)";
ctx3.stroke(s3);

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the three canvas stroke examples in the linked JSFiddle and compare them with the current bar rendering. Read the issue's canvas masking example first; done means bars no longer rely on center-aligned strokes and render crisp edges without bleeding or accidental anti-aliasing.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
data-visualization
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.