canvaskonvajsreact-konvakonva

How to achieve depth effect (pseudo-3D) for text in KonvaJs


The expected 3D effect:

Full letter

Code:

<Group
                ref={mainRef}
                x={x}
                y={y}
                scaleX={scaleX}
                scaleY={scaleY}
                onTap={onTap}
                rotation={rotation}
                onClick={onTap}
                draggable
                onDragEnd={onDragEnd}
                onDragMove={onDragMove}
                onTransformEnd={onTransformEnd}

            >
                {[...Array(6)].map((_, i) => <Text
                    text={letter}
                    x={i}
                    y={i}
                    fontFamily="Bangers-Regular"
                    fontSize={fontSize}

                    stroke="red"
                    strokeWidth={3}
                    fill="red"
                    fillPriority="color"

                />)}
                < Text
                    ref={(ref) => onRender(ref)}
                    text={letter}
                    fontFamily="Bangers-Regular"
                    fontSize={fontSize}

                    stroke="blue"
                    strokeWidth={3}
                    fill="blue"
                    fillPriority="color"

                />
</Group>

Result from code above:

Result vs 3D text effect

As you can see in the comparison the quality of the reference 3D effect is much higher and there is no aliasing, in theory I could get a better effect by increasing the number of copies, but it still looks worse at zoom-in.

Reference from: https://www.graffiti-empire.com/graffiti-generator/


Solution

  • A possible solution is to increase number of steps when your scale is increased:

    import React from "react";
    import { createRoot } from "react-dom/client";
    import { Stage, Layer, Rect, Text, Circle, Line, Group } from "react-konva";
    
    const App = () => {
      const [scale, setScale] = React.useState(1);
      return (
        <>
          <input
            type="range"
            min="0.5"
            max="5"
            step="0.1"
            value={scale}
            onChange={() => {
              setScale(Number(event.target.value));
            }}
          />
          <Stage width={window.innerWidth} height={window.innerHeight}>
            <Layer>
              <Group draggable scaleX={scale} scaleY={scale}>
                {[...Array(Math.round(6 * scale))].map((_, i) => (
                  <Text
                    text={"Hello"}
                    x={i / scale}
                    y={i / scale}
                    stroke="red"
                    strokeWidth={3}
                    fill="red"
                    fillPriority="color"
                    fontSize={50}
                  />
                ))}
                <Text
                  text={"Hello"}
                  stroke="blue"
                  strokeWidth={3}
                  fill="blue"
                  fillPriority="color"
                  fontSize={50}
                />
              </Group>
            </Layer>
          </Stage>
        </>
      );
    };
    
    const container = document.getElementById("root");
    const root = createRoot(container);
    root.render(<App />);
    

    https://codesandbox.io/p/sandbox/react-konva-3d-text-fj5gg4

    Note: creating many layers may harm performance a lot. I suggest to cache the group if possible.