5

I have multiple Path2D in a single canvas and I want to be able to scale and position each of them independently rather than adjusting the context.scale() and context.translate(). I am constructing each path2D object with SVG path data because I want to be able to modify stroke-dash and stroke-length.

It seems like I might not be able to achieve this using Path2D, what's the best way to approach solving this?

I'm considering a few potential options:

  1. use the drawImage method with svg source
  2. convert svg path data to canvas path arcs (possibly using a library)
  3. adjust the actual svg path data and reconstruct Path2D objects for each paint

Edit:

I built this code pen where I am trying to move p1 toward p2 without changing p2's position. When I translate the context, both objects move. What's the best way to adjust the position of p1 only?

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var p1x = 0
var p1 = new Path2D("M1 0 h 100 v 50 h -100 Z");
var p2 = new Path2D("M200 0 h 50 v 25 h -50 Z");

setInterval(() => {
  p1x = p1x + 1
  ctx.translate(0, 0)
  ctx.clearRect(0, 0, 300, 300)
  ctx.translate(p1x, 0)
  ctx.fill(p1)
  ctx.fill(p2)    
}, 1000)

http://codepen.io/jasonpearson/pen/reXyVG

Jason Pearson
  • 105
  • 2
  • 5
  • I don't get what you are doing right now, nor what you want. There is a [`setLineDash`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) method, and I don't see what the problem is in calling `scale` and `translate` for every path (you can reset the transformation matrix using `ctx.setTransform(1,0,0,1,0,0)`). Could you clarify a little bit ? Maybe a code sample could help. – Kaiido May 20 '16 at 10:10
  • @Kaiido just added a copepen, hope that clarifies – Jason Pearson May 23 '16 at 03:04

3 Answers3

4

One could use the addPath method in conjunction with the second transform-parameter to scale the path...

const m = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix()
const p = new Path2D()
const t = m.scale(0.5)
p.addPath(p1, t)

Example implementation:

var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
var p1x = 0
var p1 = new Path2D("M1 0 h 100 v 50 h -100 Z")
var p2 = new Path2D("M200 0 h 50 v 25 h -50 Z")
var m = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix()

setInterval(() => {
  p1x = p1x + 1

  const p = new Path2D()
  const t = m.translate(p1x, 0)
  p.addPath(p1, t)

  ctx.clearRect(0, 0, 300, 300)
  ctx.fill(p)
  ctx.fill(p2)
}, 1000)
canvas {
  border: 1px solid blue;
  height: 300px;
  width: 300px;
}
<canvas id="canvas"></canvas>

Better implementation:

var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
var p1 = new Path2D("M1 0 h 100 v 50 h -100 Z")
var p2 = new Path2D("M200 0 h 50 v 25 h -50 Z")
var m1 = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix()

setInterval(() => {
  let p = new Path2D()
  m1 = m1.translate(1, 0)
  p.addPath(p1, m1)

  ctx.clearRect(0, 0, 300, 300)
  ctx.fill(p)
  ctx.fill(p2)
}, 1000)
canvas {
  border: 1px solid blue;
  height: 300px;
  width: 300px;
}
<canvas id="canvas"></canvas>

Just a little helper function for future uses:

function transformPath2D(path, matrix) {
  const p = new Path2D()
  p.addPath(path, matrix)
  return p
}
yckart
  • 32,460
  • 9
  • 122
  • 129
  • addPath is not a function in Chrome – mofojed Jul 16 '18 at 20:07
  • @mofojed Oh, I didn't know... MDN's "Compatibility Table" said *Chrome Desktop* is supported. But it's gone, for now: https://stackoverflow.com/questions/48833460/path2d-addpath-got-removed However, it will come back, some day: https://bugs.chromium.org/p/chromium/issues/detail?id=651798&q=canvas%20path2D&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified For now, you can enable the `ExperimentalCanvasFeatures`-flag... – yckart Jul 17 '18 at 09:17
2

You can apply the translation to just the first element:

setInterval(() => {
    p1x = p1x + 1;
    ctx.clearRect(0, 0, 300, 300);
    ctx.save();
    ctx.translate(p1x, 0);
    ctx.fill(p1);
    ctx.restore();
    ctx.fill(p2);
}, 1000);
Joaquin Cuenca Abela
  • 2,535
  • 22
  • 21
1

Since Chrome no longer as Path2D.addPath(), if you want Chrome compatibility without requiring experimental flags, you'll need to set the transform on the context, I don't think there is another way.

That being said, you can just save() and restore() the context before/after you make your adjustments. Updating your example from above:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var p1x = 0
var p1 = new Path2D("M1 0 h 100 v 50 h -100 Z");
var p2 = new Path2D("M200 0 h 50 v 25 h -50 Z");

setInterval(() => {
  p1x = p1x + 1
  ctx.clearRect(0, 0, 300, 300);
  ctx.save();            // Save the previous translation context
  ctx.translate(p1x, 0); // Adjust context to where you want to draw p1
  ctx.fill(p1);
  ctx.restore();         // Restore the context before drawing p2
  ctx.fill(p2)    
}, 1000)

https://codepen.io/anon/pen/oMLNEz

mofojed
  • 1,342
  • 9
  • 11