Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling Context.fill with a path does not work #20

Open
k1w1 opened this issue Jul 17, 2022 · 2 comments · May be fixed by #22
Open

Calling Context.fill with a path does not work #20

k1w1 opened this issue Jul 17, 2022 · 2 comments · May be fixed by #22

Comments

@k1w1
Copy link

k1w1 commented Jul 17, 2022

The Canvas API supports passing a Path2D object to fill and stroke. This is not handled by svgcanvas.

Unfortunately it doesn't look like it could be easily supported since there is no way to extract the path definition from the Path2D object.

One approach might be to mock the Path2D object too so that the calls that construct the path can be recorded.

@k1w1 k1w1 linked a pull request Aug 22, 2022 that will close this issue
@andywampir
Copy link

andywampir commented Dec 27, 2023

I was able to bypass this issue with help of Proxy, this way works well with canvas too.

Note: it's just POC, this solution need some refactoring.

Firstly i'd proxied three (that i used in my case) methods of Path2D's prototype:

Path2D.prototype.moveTo = new Proxy(Path2D.prototype.moveTo, {
	apply: function (target, thisArg: Path2D, args: [number, number]) {
		if (!thisArg.operations) {
			thisArg.operations = [];
		}

		thisArg.operations.push(['moveTo', args]);
		Reflect.apply(target, thisArg, args);
	},
});
Path2D.prototype.lineTo = new Proxy(Path2D.prototype.lineTo, {
	apply: function (target, thisArg: Path2D, args: [number, number]) {
		if (!thisArg.operations) {
			thisArg.operations = [];
		}

		thisArg.operations.push(['lineTo', args]);
		Reflect.apply(target, thisArg, args);
	},
});
Path2D.prototype.bezierCurveTo = new Proxy(Path2D.prototype.bezierCurveTo, {
	apply: function (
		target,
		thisArg: Path2D,
		args: [number, number, number, number, number, number],
	) {
		if (!thisArg.operations) {
			thisArg.operations = [];
		}

		thisArg.operations.push(['bezierCurveTo', args]);
		Reflect.apply(target, thisArg, args);
	},
});

Then i changed stroke and fill methods of Context:

ctx.stroke = function (path?: Path2D) {
	if (path?.operations) {
		ctx.beginPath();
		const operations = path.operations as (
			| [operation: 'lineTo' | 'moveTo', args: [number, number]]
			| [
					operation: 'bezierCurveTo',
					args: [number, number, number, number, number, number],
			  ]
		)[];

		for (const operation of operations) {
			switch (operation[0]) {
				case 'lineTo': {
					ctx.lineTo(...operation[1]);

					break;
				}

				case 'moveTo': {
					ctx.moveTo(...operation[1]);

					break;
				}

				case 'bezierCurveTo': {
					ctx.bezierCurveTo(...operation[1]);

					break;
				}
			}
		}
	}

	// Below is original code of `Context.stroke` method
	if (this.__currentElement.nodeName === 'path') {
		this.__currentElement.setAttribute('paint-order', 'fill stroke markers');
	}

	this.__applyCurrentDefaultPath();
	this.__applyStyleToCurrentElement('stroke');
};

ctx.fill = function (
	path?: Path2D | CanvasFillRule,
	_fillRule?: CanvasFillRule,
) {
	if (path?.operations) {
		ctx.beginPath();
		const operations = path.operations as (
			| [operation: 'lineTo' | 'moveTo', args: [number, number]]
			| [
					operation: 'bezierCurveTo',
					args: [number, number, number, number, number, number],
			  ]
		)[];

		for (const operation of operations) {
			switch (operation[0]) {
				case 'lineTo': {
					ctx.lineTo(...operation[1]);

					break;
				}

				case 'moveTo': {
					ctx.moveTo(...operation[1]);

					break;
				}

				case 'bezierCurveTo': {
					ctx.bezierCurveTo(...operation[1]);

					break;
				}
			}
		}
	}

	// Below is original code of `Context.fill` method
	if (this.__currentElement.nodeName === 'path') {
		this.__currentElement.setAttribute('paint-order', 'stroke fill markers');
	}

	this.__applyCurrentDefaultPath();
	this.__applyStyleToCurrentElement('fill');
};

@jiayihu
Copy link

jiayihu commented Feb 23, 2024

In the end the easiest fix it to draw using the Path commands (ctx.lineTo/moveTo etc), then call ctx.fill/stroke() without any arg. E.g.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants