Search code examples
reactjstypescriptcanvaskonvajskonvajs-reactjs

KonvaJS, Possible to have a draggable group of images?


I'm using react-konva and I need to be able to drag multiple images at once (not using mutliselect). It appears that Konva handles this by using Groups or Containers.

I looked at the TypeScript files and:

export declare class Group extends Container<Group | Shape> {
    _validateAdd(child: Node): void;
}
export interface ContainerConfig extends NodeConfig {
    clearBeforeDraw?: boolean;
    clipFunc?: (ctx: CanvasRenderingContext2D) => void;
    clipX?: number;
    clipY?: number;
    clipWidth?: number;
    clipHeight?: number;
}
export declare abstract class Container<ChildType extends Node> extends Node<ContainerConfig> {
export declare class Image extends Shape<ImageConfig> {

It would seem the Image is an instance of Shape and should therefore be placeable in a Group, but I just can't get it to work.

I tried doing:

<Group height={<SOME NUMBER>} width={<SOME NUMBER>} draggable>
   {obj.map((obj2: any, idx: number) => {
      return (
        <Image
          X={<SOME NUMBER>}
          Y={<SOME NUMBER>}
          key={idx}
          scale={<SOME NUMBER>}
          image=<SOME IMAGE CONFIG>}
          draggable
        />
      )
   })}
</Group>

I tried looking for example by the vanilla API, but couldn't find any. It shows that Group should be able to contain Shape api

Group constructor. Groups are used to contain shapes or other groups.

I was wondering if anyone has any suggestions on leveraging provided Konva code to accomplish creating a draggable group of images or perhaps an alternative approach I'm not considering.


Solution

  • Your attempt looks fine to me. In Konva a layer may have groups and shapes inside. Any group may have other groups or shapes inside. The image is just a shape.

    There is a working demo for you:

    const URLImage = ({ url, ...rest }) => {
      const [image] = useImage(url);
      return <Image image={image} {...rest} />;
    };
    
    class App extends Component {
      render() {
        const images = [
          "https://konvajs.org/assets/lion.png",
          "https://konvajs.org/assets/yoda.jpg"
        ];
        return (
          <Stage width={window.innerWidth} height={window.innerHeight}>
            <Layer>
              <Group draggable>
                {images.map((url, i) => (
                  <URLImage url={url} x={i * 120} key={i} />
                ))}
              </Group>
            </Layer>
          </Stage>
        );
      }
    }
    

    DEMO: https://codesandbox.io/s/recursing-bohr-vlcsw?file=/src/index.js