Ändern Sie die Deckkraft außerhalb eines beschnittenen Rechtecks ​​in Konva

Nov 21 2020

Ich versuche, mit React und konva ein Tool zum Zuschneiden von Bildern zu erstellen. Ich möchte die Deckkraft außerhalb des beschnittenen Rechtecks ​​ändern, um den Rest des Bildes zu verwischen.

Ich habe bisher versucht, unterschiedliche Trübungen für das Rechteck und das Bild festzulegen, bin jedoch gescheitert. Ich habe nachgeschlagen und es gibt keinen direkten Weg, dies zu tun

Hier ist die Zuschneidefunktion, die ich angepasst habe, um mithilfe dieser Antwort zu reagieren

import React, { useState, useEffect, useRef } from "react";
import { render } from "react-dom";
import { Stage, Layer, Rect, Image } from "react-konva";
import Konva from "konva";

const App = () => {
  // Stage dims
  let sW = 720,
    sH = 720,
    sX = 0,
    sY = 0;

   let src = "https://dummyimage.com/720x720/e85de8/fff&text=SO Rocks!";
  let img = document.createElement("img");

  useEffect(() => {
    img.src = src;
    function loadStatus() {
      setloading(false);
    }
    img.addEventListener("load", loadStatus);
    return () => {
      img.removeEventListener("load", loadStatus);
    };
  }, [img, src]);

  let scale = 1;
  const [loading, setloading] = useState(true);
  const [posStart, setposStart] = useState({});
  const [posNow, setposNow] = useState({});
  const [mode, setmode] = useState("");

  /**
   * Sets the state of posStart and posNow for tracking the coordinates of the cropping rectangle
   * @param {Object} posIn - Coordinates of the pointer when MouseDown is fired
   */
  function startDrag(posIn) {
    setposStart({ x: posIn.x, y: posIn.y });
    setposNow({ x: posIn.x, y: posIn.y });
  }

  /**
   * Updates the state accordingly when the MouseMove event is fired
   * @param {Object} posIn - Coordiantes of the current position of the pointer
   */
  function updateDrag(posIn) {
    setposNow({ x: posIn.x, y: posIn.y });
    let posRect = reverse(posStart, posNow);
    r2.current.x(posRect.x1);
    r2.current.y(posRect.y1);
    r2.current.width(posRect.x2 - posRect.x1);
    r2.current.height(posRect.y2 - posRect.y1);
    r2.current.visible(true);
  }

  /**
   * Reverse coordinates if dragged left or up
   * @param {Object} r1 - Coordinates of the starting position of cropping rectangle
   * @param {Object} r2 - Coordinates of the current position of cropping rectangle
   */
  function reverse(r1, r2) {
    let r1x = r1.x,
      r1y = r1.y,
      r2x = r2.x,
      r2y = r2.y,
      d;
    if (r1x > r2x) {
      d = Math.abs(r1x - r2x);
      r1x = r2x;
      r2x = r1x + d;
    }
    if (r1y > r2y) {
      d = Math.abs(r1y - r2y);
      r1y = r2y;
      r2y = r1y + d;
    }
    return { x1: r1x, y1: r1y, x2: r2x, y2: r2y }; // return the corrected rect.
  }

  /**
   * Crops the image and saves it in jpeg format
   * @param {Konva.Rect} r - Ref of the cropping rectangle
   */
  function setCrop(r) {
    let jpeg = new Konva.Image({
      image: img,
      x: sX,
      y: sY
    });
    jpeg.cropX(r.x());
    jpeg.cropY(r.y());
    jpeg.cropWidth(r.width() * scale);
    jpeg.cropHeight(r.height() * scale);
    jpeg.width(r.width());
    jpeg.height(r.height());
    const url = jpeg.toDataURL({ mimeType: "image/jpeg", quality: "1.0" });
    const a = document.createElement("a");
    a.href = url;
    a.download = "cropped.jpg";
    a.click();
  }

  // Foreground rect to capture events
  const r1 = useRef();

  // Cropping rect
  const r2 = useRef();

  const image = useRef();

  return (
    <div className="container">
      <Stage width={sW} height={sH}>
        <Layer>
          {!loading && (
            <Image
              ref={image}
              {...{
                image: img,
                x: sX,
                y: sY
              }}
            />
          )}
          <Rect
            ref={r1}
            {...{
              x: 0,
              y: 0,
              width: sW,
              height: sH,
              fill: "white",
              opacity: 0
            }}
            onMouseDown={function (e) {
              setmode("drawing");
              startDrag({ x: e.evt.layerX, y: e.evt.layerY });
            }}
            onMouseMove={function (e) {
              if (mode === "drawing") {
                updateDrag({ x: e.evt.layerX, y: e.evt.layerY });
                image.current.opacity(0.5);
                r2.current.opacity(1);
              }
            }}
            onMouseUp={function (e) {
              setmode("");
              r2.current.visible(false);
              setCrop(r2.current);
              image.current.opacity(1);
            }}
          />
          <Rect
            ref={r2}
            listening={false}
            {...{
              x: 0,
              y: 0,
              width: 0,
              height: 0,
              stroke: "white",
              dash: [5, 5]
            }}
          />
        </Layer>
      </Stage>
    </div>
  );
};

render(<App />, document.getElementById("root"));

Demo für den obigen Code

Antworten

2 mplusr Nov 21 2020 at 14:04

Dies kann mit Konva.Group und seiner Clip-Eigenschaft erfolgen. Fügen Sie der Gruppe ein neues Konva.Image hinzu und stellen Sie die Beschneidungspositionen und -größe so ein, dass sie mit dem Zuschneiderechteck übereinstimmen. Vergessen Sie nicht, die Hörstütze der Gruppe auf false zu setzen, da dies sonst die Sache kompliziert. Hier ist das Endergebnis

import { render } from "react-dom";
import React, { useLayoutEffect, useRef, useState } from "react";
import { Stage, Layer, Image, Rect, Group } from "react-konva";

/**
 * Crops a portion of image in Konva stage and saves it in jpeg format
 * @param {*} props - Takes no props
 */
function Cropper(props) {
  // Stage dims
  let sW = 720,
    sH = 720,
    sX = 0,
    sY = 0;

  let src = "https://dummyimage.com/720x720/e85de8/fff&text=SO Rocks!";
  let img = new window.Image();
  useLayoutEffect(() => {
    img.src = src;
    function loadStatus() {
      // img.crossOrigin = "Anonymous";
      setloading(false);
    }
    img.addEventListener("load", loadStatus);
    return () => {
      img.removeEventListener("load", loadStatus);
    };
  }, [img, src]);
  let i = new Konva.Image({
    x: 0,
    y: 0,
    width: 0,
    height: 0
  });
  let scale = 1;
  const [loading, setloading] = useState(true);
  const [posStart, setposStart] = useState({});
  const [posNow, setposNow] = useState({});
  const [mode, setmode] = useState("");

  /**
   * Sets the state of posStart and posNow for tracking the coordinates of the cropping rectangle
   * @param {Object} posIn - Coordinates of the pointer when MouseDown is fired
   */
  function startDrag(posIn) {
    setposStart({ x: posIn.x, y: posIn.y });
    setposNow({ x: posIn.x, y: posIn.y });
  }

  /**
   * Updates the state accordingly when the MouseMove event is fired
   * @param {Object} posIn - Coordiantes of the current position of the pointer
   */
  function updateDrag(posIn) {
    setposNow({ x: posIn.x, y: posIn.y });
    let posRect = reverse(posStart, posNow);
    r2.current.x(posRect.x1);
    r2.current.y(posRect.y1);
    r2.current.width(posRect.x2 - posRect.x1);
    r2.current.height(posRect.y2 - posRect.y1);
    r2.current.visible(true);
    grp.current.clip({
      x: posRect.x1,
      y: posRect.y1,
      width: posRect.x2 - posRect.x1,
      height: posRect.y2 - posRect.y1
    });
    grp.current.add(i);
    i.image(img);
    i.width(img.width);
    i.height(img.height);
    i.opacity(1);
  }

  /**
   * Reverse coordinates if dragged left or up
   * @param {Object} r1 - Coordinates of the starting position of cropping rectangle
   * @param {Object} r2 - Coordinates of the current position of cropping rectangle
   */
  function reverse(r1, r2) {
    let r1x = r1.x,
      r1y = r1.y,
      r2x = r2.x,
      r2y = r2.y,
      d;
    if (r1x > r2x) {
      d = Math.abs(r1x - r2x);
      r1x = r2x;
      r2x = r1x + d;
    }
    if (r1y > r2y) {
      d = Math.abs(r1y - r2y);
      r1y = r2y;
      r2y = r1y + d;
    }
    return { x1: r1x, y1: r1y, x2: r2x, y2: r2y }; // return the corrected rect.
  }

  /**
   * Crops the image and saves it in jpeg format
   * @param {Konva.Rect} r - Ref of the cropping rectangle
   */
  function setCrop(r) {
    let jpeg = new Konva.Image({
      image: img,
      x: sX,
      y: sY
    });
    jpeg.cropX(r.x());
    jpeg.cropY(r.y());
    jpeg.cropWidth(r.width() * scale);
    jpeg.cropHeight(r.height() * scale);
    jpeg.width(r.width());
    jpeg.height(r.height());
    const url = jpeg.toDataURL({ mimeType: "image/jpeg", quality: "1.0" });
    const a = document.createElement("a");
    a.href = url;
    a.download = "cropped.jpg";
    a.click();
  }

  // Foreground rect to capture events
  const r1 = useRef();

  // Cropping rect
  const r2 = useRef();

  const image = useRef();
  const grp = useRef();

  return (
    <div className="container">
      <Stage width={sW} height={sH}>
        <Layer>
          {!loading && (
            <Image
              ref={image}
              listening={false}
              {...{
                image: img,
                x: sX,
                y: sY
              }}
            />
          )}

          <Rect
            ref={r1}
            {...{
              x: 0,
              y: 0,
              width: sW,
              height: sH,
              fill: "white",
              opacity: 0
            }}
            onMouseDown={function (e) {
              setmode("drawing");
              startDrag({ x: e.evt.layerX, y: e.evt.layerY });
              image.current.opacity(0.2);
            }}
            onMouseMove={function (e) {
              if (mode === "drawing") {
                updateDrag({ x: e.evt.layerX, y: e.evt.layerY });
              }
            }}
            onMouseUp={function (e) {
              setmode("");
              r2.current.visible(false);
              setCrop(r2.current);
              image.current.opacity(1);
              grp.current.removeChildren(i);
            }}
          />
          <Group listening={false} ref={grp}></Group>

          <Rect
            ref={r2}
            listening={false}
            {...{
              x: 0,
              y: 0,
              width: 0,
              height: 0,
              stroke: "white",
              dash: [5, 10]
            }}
          />
        </Layer>
      </Stage>
    </div>
  );
}

render(<Cropper />, document.getElementById("root"));

Vielen Dank an @Vanquished Wombat für all die wertvollen Beiträge . Dies ist eine Anpassung seiner Antwort hier

Demo des obigen Codes