im trying draw selected svg to canvas but it is very slow, inefficient, bad resolution and not all svg's are working
and i need them svgs on canvas as dynamic(changing color, rotation and scaling)
Is there any way to do it better? thank you
export const drawPatternsOnCanvas = (ctx, itemsOnTshirt, uvConditions) => {
itemsOnTshirt.forEach((item) => {
if (item.type !== "pattern" || !item.img) return;
const uv = uvConditions[item.part];
if (!uv) return;
const canvasSize = 100;
const img = new Image();
img.src = item.pattern; //item.pattern="/pattern/pattern01.svg"
img.crossOrigin = "anonymous";
img.onload = () => {
const minX = uv[0];
const maxX = uv[1];
const minY = uv[2];
const maxY = uv[3];
const regionWidth = maxX - minX;
const regionHeight = maxY - minY;
const coloredPattern = document.createElement("canvas");
coloredPattern.width = img.width;
coloredPattern.height = img.height;
const cpCtx = coloredPattern.getContext("2d");
cpCtx.drawImage(img, 0, 0);
cpCtx.globalCompositeOperation = "source-atop";
cpCtx.fillStyle = `rgb(${Math.floor(item.color[0] * 255)}, ${Math.floor(
item.color[1] * 255
)}, ${Math.floor(item.color[2] * 255)})`;
cpCtx.fillRect(0, 0, img.width, img.height);
cpCtx.globalCompositeOperation = "source-over";
const fillPattern = ctx.createPattern(coloredPattern, "repeat");
if (fillPattern.setTransform) {
const rotationDegrees = parseFloat(item.rotation) || 0;
const patternRatioX = img.width / canvasSize;
const patternRatioY = img.height / canvasSize;
const center = canvasSize * 0.5;
const matrix = new DOMMatrix()
.translateSelf(center, center)
.rotateSelf(-rotationDegrees)
.scaleSelf(item.scale / patternRatioX, -item.scale / patternRatioY)
.translateSelf(item.moveX * canvasSize, item.moveY * canvasSize)
.translateSelf(-center, -center);
fillPattern.setTransform(matrix);
}
ctx.save();
ctx.beginPath();
ctx.rect(minX, minY, regionWidth, regionHeight);
ctx.clip();
ctx.fillStyle = fillPattern;
ctx.fillRect(minX, minY, regionWidth, regionHeight);
ctx.restore();
};
});
};
this is the code which implies pattern svg into canvas
export const createUnifiedCanvasTexture = (
uvConditions,
colorsForparts,
itemsOnTshirt
) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const width = (canvas.width = 1000);
const height = (canvas.height = 1000)
//drawing colors
drawUVRegions(ctx, uvConditions, colorsForparts);
// Draw patterns
drawPatternsOnCanvas(ctx, itemsOnTshirt, uvConditions);
//inside color
ctx.fillStyle = "#ffffff";
ctx.fillRect(1, 180, 124, -179);
const texture = new THREE.CanvasTexture(canvas);
texture.flipY = false;
texture.needsUpdate = true;
return texture;
};
and this is my main canvas which is connected to shaders and model
I found the solution gusy and i wanted to share
so currently when the state updated with the new image i am loading all of them as async and using the image i need in temp canvas in that way there is no flickering and all svgs are working thx for the help
const loadImage = (src) =>
new Promise((res) => {
const img = new Image();
img.src = src;
img.onload = () => res(img);
});
const [deleteIcon, rotateIcon, duplicateIcon, resizeIcon, ...images] =
await Promise.all([
loadImage("/ActionButtonDelete.svg"),
loadImage("/ActionButtonRotate.svg"),
loadImage("/ActionButtonDuplicate.svg"),
loadImage("/ActionButtonScale.svg"),
...itemsOnTshirt.map((item) =>
item.type === "Sticker" || item.type === "Image" || item.texture
? loadImage(item.texture)
: Promise.resolve(null)
),
]);
itemsOnTshirt.forEach((item, i) => {
const key = item.type === "Text" ? item.textType : item.type;
const isClicked = item?.isClicked === true;
switch (key) {
case "Sticker":
drawSticker(
ctx,
item,
images[i],
width,
height,
isClicked,
deleteIcon,
rotateIcon,
duplicateIcon,
resizeIcon,
uvConditions
);
break;