Automattic / Automattic/node-canvas
Support Path2D API
- Dominant language
- JavaScript
- Stars
- 10.7k
- Forks
- 1.2k
- Avg merge
- 4d 8h
- Merged PRs (30d)
- 1
Description
https://developer.mozilla.org/en-US/docs/Web/API/Path2D
The Path2D interface of the Canvas 2D API is used to declare paths that are then later used on CanvasRenderingContext2D objects. The path methods of the CanvasRenderingContext2D interface are present on this interface as well and are allowing you to create paths that you can retain and replay as required on a canvas.
---
This is my polyfill:
```js
const {CanvasRenderingContext2D, createCanvas, Image} = require('canvas')
const {parseSVG, makeAbsolute} = require('svg-path-parser')
const MAX_SIZE = 2048
function createPathSVG(d, lineWidth, lineCap, lineJoin, strokeColor, fillColor) {
const tpl = `
`
const img = new Image()
img.src = Buffer.from(tpl, 'utf8')
return img
}
class Path2D {
constructor(d) {
this.footprint = []
this.commands = []
if(d instanceof Path2D) {
this.addPath(d)
} else if(typeof d === 'string') {
// svg path
const commands = makeAbsolute(parseSVG(d))
if(commands[0] && commands[0].code === 'M') {
this.footprint.push([commands[0].x, commands[0].y])
}
this.commands.push(['path', d])
}
}
addPath(path) {
this.footprint.push(...path.footprint)
this.commands.push(...path.commands)
}
closePath() {
const point = this.footprint[0]
if(point) {
this.moveTo(...point)
}
}
moveTo(x, y) {
this.footprint.push([x, y])
this.commands.push(['moveTo', x, y])
}
lineTo(x, y) {
this.footprint.push([x, y])
this.commands.push(['lineTo', x, y])
}
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
this.footprint.push([x, y])
this.commands.push(['bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y])
}
quadraticCurveTo(cpx, cpy, x, y) {
this.footprint.push([x, y])
this.commands.push('quadraticCurveTo', cpx, cpy, x, y)
}
arc(x, y, ...rest) {
this.footprint.push([x, y])
this.commands.push('arc', x, y, ...rest)
}
arcTo(x1, y1, x2, y2, radius) {
this.commands.push('artTo', x1, y1, x2, y2, radius)
}
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
this.footprint.push([x, y])
this.commands.push('ellipse', x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
}
rect(x, y, width, height) {
this.footprint.push([x, y])
this.commands.push('rect', x, y, width, height)
}
draw(context, type = 'stroke') {
const {lineWidth, lineCap, lineJoin, strokeStyle, fillStyle} = context
context.save()
context.beginPath()
this.commands.forEach((command) => {
const [cmd, ...args] = command
if(cmd === 'path') {
const svg = createPathSVG(...args, lineWidth, lineCap, lineJoin, strokeStyle, type === 'stroke' ? null : fillStyle)
context.drawImage(svg, 0, 0)
} else {
context[cmd](...args)
context[type]()
}
})
context.restore()
}
}
const _stroke = CanvasRenderingContext2D.prototype.stroke
Object.defineProperty(CanvasRenderingContext2D.prototype, 'stroke', {
value(p) {
if(p instanceof Path2D) {
return p.draw(this, 'stroke')
}
return _stroke.call(this)
},
})
const _fill = CanvasRenderingContext2D.prototype.fill
Object.defineProperty(CanvasRenderingContext2D.prototype, 'fill', {
value(p) {
if(p instanceof Path2D) {
return p.draw(this, 'fill')
}
return _fill.call(this)
},
})
const p = new Path2D('M10,80 q100,120 120,20 q140,-50 160,0')
const canvas = createCanvas(800, 600)
const context = canvas.getContext('2d')
context.strokeStyle = 'red'
context.stroke(p)
require('fs').writeFileSync('out.png', canvas.toBuffer())
```

Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.