what i want to make is kind of image markup tool. so user can upload image with various sizes(for example, 512 * 512, 512 * 768). And also user could use brush tool on the image. and this function is made with konva js but i have a problem when user works with 512 * 512 image and did brush job on it, and then user changed image size like 512 * 768. Because image size is changed, brush layer must change so the brush jobs are fitted with new image size. but I don't have any idea of how to make this function ... so please help me with it..
** and image changed with object-fit:cover rule and then changed again with object-fit: contain rule
my code is
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
#layer {
display: flex;
width: 100%;
height: 700px;
background-color: #000;
}
#aside {
width: 200px;
background-color: #111;
}
#main {
overflow: auto;
flex: 1;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
#container {
overflow: hidden;
position: relative;
margin: auto;
width: 600px;
height: 400px;
background-color: #222;
}
#canvas {
margin: auto;
}
</style>
</head>
<body>
<div id="layer">
<div id="aside"></div>
<div id="main">
<div id="container">
<div id="canvas"></div>
</div>
</div>
</div>
<div>
<select id="select-output-size">
<option>512 x 512</option>
<option>512 x 768</option>
<option>768 x 512</option>
<option>1024 x 1024</option>
</select>
<button id="button-export-image">Export</button>
</div>
<div>
<img id="result" src="" />
</div>
<script src="https://unpkg.com/[email protected]/konva.min.js"></script>
<script>
const $container = document.querySelector("#container");
const $canvas = document.querySelector("#canvas");
const $selectOutputSize = document.querySelector("#select-output-size");
const $buttonExportImage = document.querySelector("#button-export-image");
const $result = document.querySelector("#result");
let scale = 1;
const output = {
width: 0,
height: 0,
image: null,
};
const canvasStage = new Konva.Stage({
container: "canvas",
width: 0,
height: 0,
});
const canvasDrawLayer = new Konva.Layer();
canvasStage.add(canvasDrawLayer);
const getContainSize = (
containerWidth,
containerHeight,
outputWidth,
outputHeight
) => {
const containerRatio = containerWidth / containerHeight;
const outputRatio = outputWidth / outputHeight;
return containerRatio < outputRatio
? {
width: containerWidth,
height: containerWidth / outputRatio,
}
: {
width: containerHeight * outputRatio,
height: containerHeight,
};
};
const getResizedImage = (imageSrc, containerWidth, containerHeight) => {
return new Promise((resolve) => {
const canvas = document.createElement("canvas");
const image = new Image();
canvas.width = containerWidth;
canvas.height = containerHeight;
image.addEventListener("load", () => {
const containerRatio = containerWidth / containerHeight;
const imageRatio = image.width / image.height;
let width = containerWidth,
height = containerHeight,
x = 0,
y = 0;
if (containerRatio < imageRatio) {
width = containerHeight * imageRatio;
x = (containerWidth - width) / 2;
} else if (containerRatio > imageRatio) {
height = containerWidth / imageRatio;
y = (containerHeight - height) / 2;
}
scale =
containerRatio < imageRatio
? height / image.height
: width / image.width;
canvas.getContext("2d").drawImage(image, x, y, width, height);
canvasStage.position({ x, y });
canvasStage.scale({
x: scale,
y: scale,
});
resolve(canvas.toDataURL("image/png"));
});
image.src = imageSrc;
});
};
const changeOutputSize = () => {
const { width: containerWidth, height: containerHeight } =
$container.getBoundingClientRect();
$selectOutputSize.addEventListener(
"change",
async ({ target: { value } }) => {
const [outputWidth, outputHeight] = value.split(" x ").map(Number);
const { width, height } = getContainSize(
containerWidth,
containerHeight,
outputWidth,
outputHeight
);
const base64 = await getResizedImage("./ss.jpg", width, height);
canvasStage.size({ width, height });
$canvas.style.cssText = `
width: ${width}px;
height: ${height}px;
background: url('${base64}') center/cover;
`;
Object.assign(output, {
width: outputWidth,
height: outputHeight,
image: base64,
});
}
);
$selectOutputSize.dispatchEvent(new Event("change"));
};
const setCanvasDrawLayer = () => {
let lastLine,
isDraw = false;
canvasStage.on("mousedown", () => {
isDraw = true;
const x =
(canvasStage.getPointerPosition().x - canvasDrawLayer.x()) / scale;
const y =
(canvasStage.getPointerPosition().y - canvasDrawLayer.y()) / scale;
lastLine = new Konva.Line({
stroke: "blue",
strokeWidth: 5,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
points: [x, y, x, y],
});
canvasDrawLayer.add(lastLine);
});
canvasStage.on("mousemove", ({ evt }) => {
if (!isDraw) return;
evt.preventDefault();
const x =
(canvasStage.getPointerPosition().x - canvasDrawLayer.x()) / scale;
const y =
(canvasStage.getPointerPosition().y - canvasDrawLayer.y()) / scale;
lastLine.points(lastLine.points().concat([x, y]));
});
canvasStage.on("mouseup", () => {
isDraw = false;
});
canvasStage.add(canvasDrawLayer);
};
changeOutputSize();
setCanvasDrawLayer();
$buttonExportImage.addEventListener("click", async () => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const foreground = new Image();
const background = new Image();
canvas.width = output.width;
canvas.height = output.height;
const loadForeground = new Promise((resolve) => {
foreground.onload = resolve;
foreground.src = canvasStage.toDataURL({ pixelRatio: 2 });
});
const loadBackground = new Promise((resolve) => {
background.onload = resolve;
background.src = output.image;
});
Promise.all([loadForeground, loadBackground]).then(() => {
context.drawImage(background, 0, 0, output.width, output.height);
context.drawImage(foreground, 0, 0, output.width, output.height);
$result.src = canvas.toDataURL("image/png");
});
});
</script>
</body>
</html>
i tried to set scale of konva stage(drawing layer) and position so drawing layer is fitted to image(when image size changed). but it's not working..