how to preserve brush job when image size is changed?(with Konva JS)

56 Views Asked by At

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..

0

There are 0 best solutions below