Untitled

 avatar
unknown
typescript
a year ago
5.3 kB
4
Indexable
import { Stage, Sprite } from "@pixi/react";
import * as PIXI from "pixi.js";
import { useState, useEffect, useRef } from "react";
import BrushGenerator from "../../components/painters/brushGenerator.ts";
import BrushPaint from "../../components/painters/brushPaint.ts";
import { PainterConfig } from "../../components/painters/ipainter.ts";
import { PainterType } from "../../components/painters/painterType.ts";
import { Constant } from "../../constant.ts";

interface History {
  layerPointer: number;
  layers: PIXI.RenderTexture[];
  currentLayer: PIXI.RenderTexture;
}

export default function DrawingComponent() {

  const spriteRef = useRef<PIXI.Sprite>(null);
  const [app, setApp] = useState<PIXI.Application>(null);
  const [isDrawing, setIsDrawing] = useState(false);
  const [brush, setBrush] = useState<BrushPaint>(null);
  const [drawingBuffer] = useState(new PIXI.Container());
  const [painterConfig, setPainterConfig] = useState<PainterConfig>({
    type      : PainterType.Brush,
    size      : 10,
    opacity   : 1,
    smoothing : 0.5,
    color     : "#55ec50",
  });
  const [history, setHistory] = useState<History>({
    layerPointer : 0,
    layers       : [],
    currentLayer : null,
  });

  const render = () => {
    if (drawingBuffer.children.length > 0 && history.currentLayer) {
      app.renderer.render(drawingBuffer, { renderTexture: history.currentLayer, clear: false });
    }
  };

  const init = () => {
    if (!app || !app.stage) {
      return;
    }
    app.ticker.add(render);
    BrushGenerator.init(app.renderer as PIXI.Renderer);
    setPainterConfig({ ...painterConfig });
    return () => {
      app.ticker.remove(render);
    };
  };

  const onPainterConfigChange = () => {
    if (!app) {
      return;
    }
    setBrush(new BrushPaint(painterConfig));
  };

  const onHistoryChange = () => {
    if (!app || !spriteRef.current) {
      return;
    }
    console.log("history changed", history);
    if (history.currentLayer) {
      spriteRef.current.texture = history.currentLayer;
    }

    app.ticker.remove(render);
    app.ticker.add(render);
    window.addEventListener("keydown", onKeydown);
    return () => {
      app.ticker.remove(render);
      window.removeEventListener("keydown", onKeydown);
    };
  };

  const onDown = (e: PIXI.FederatedPointerEvent) => {
    if (!brush) {
      return;
    }
    setIsDrawing(true);
    createLayer();
    brush.startDraw(drawingBuffer, e.global);
  };
  const onMove = (e: PIXI.FederatedPointerEvent) => {

    if (!isDrawing || !brush) {
      return;
    }
    brush.draw(e.global);
  };

  const onUp = () => {
    if (!isDrawing || !brush) {
      return;
    }
    setIsDrawing(false);
    brush.endDraw();
  };

  const undo = () => {
    if (history.layerPointer > 0) {
      let newHistory = { ...history };
      newHistory.layerPointer--;
      newHistory.currentLayer = newHistory.layers[history.layerPointer];
      setHistory(newHistory);
    }
  };
  const redo = () => {
    if (history.layerPointer < history.layers.length - 1) {
      let newHistory = { ...history };
      newHistory.layerPointer++;
      newHistory.currentLayer = history.layers[history.layerPointer];
      setHistory(newHistory);
    }
  };

  const onKeydown = (e: KeyboardEvent) => {
    if (e.key === "z" && e.ctrlKey) {
      if (e.shiftKey) {
        redo();
      }
      else {
        undo();
      }

    }
    else if (e.key === "y" && e.ctrlKey) {
      redo();
    }
  };

  const createLayer = (): PIXI.RenderTexture | null => {
    if (!app || !spriteRef.current) {
      return null;
    }
    let layer = PIXI.RenderTexture.create({
      width  : Constant.DRAW_BOARD_WIDTH,
      height : Constant.DRAW_BOARD_HEIGHT,
    });
    let newHistory = { ...history };
    app.renderer.render(spriteRef.current, { renderTexture: layer, clear: false });
    if (history.layerPointer !== history.layers.length - 1) {
      let deletedLayers = history.layers.splice(history.layerPointer + 1);
      deletedLayers.forEach((l) => l.destroy());
    }
    newHistory.layers = [...history.layers, layer];
    newHistory.layerPointer = newHistory.layers.length - 1;
    newHistory.currentLayer = layer;
    setHistory(newHistory);
    return layer;
  };

  useEffect(init, [app]);
  useEffect(onPainterConfigChange, [painterConfig]);
  useEffect(onHistoryChange, [history]);

  return (
    <div className="w-full h-full flex justify-center items-center" >
      <Stage width={Constant.DRAW_BOARD_WIDTH} height={Constant.DRAW_BOARD_HEIGHT} options={{
        backgroundColor : 0xffffff,
        backgroundAlpha : 1,
        antialias       : true,
      }}
      onMount={setApp}>
        <Sprite ref={spriteRef}
          eventMode={"static"}
          width={Constant.DRAW_BOARD_WIDTH}
          height={Constant.DRAW_BOARD_HEIGHT}
          texture={PIXI.Texture.EMPTY}
          onmousedown={onDown}
          onmousemove={onMove}
          onmouseup={onUp}
          ontouchstart={onDown}
          ontouchmove={onMove}
          ontouchend={onUp}
        />
      </Stage>
    </div>
  );
}
Editor is loading...
Leave a Comment