2
votes

My project uses React Konva (https://github.com/konvajs/react-konva)

I am trying to draw multiple shapes into a Group and use this to mask the image "below".

When my component draws a single shape with globalCompositeOperation applied, it produces the expected result. Here's the code:

render() {
        return (

            <Group >
                <Group>
                    <Image 
                        image={this.state.image} 
                        ref={node => { this.image = node; }} 
                    />
                    <Group>
                        <Rect fill={"#555555"} 
                            width={200} height={200} 
                            x={100} y={100} 
                            globalCompositeOperation={"destination-in"}
                        />
                    </Group>
                </Group>
            </Group>
        )

    }

And the result: enter image description here

Notice how the image is cropped to the rectangle, revealing the text layer below.

However, as soon as the shape moves inside a Group, and I apply the globalCompositeOperation there, no masking happens. The relevant part of the code:

<Group>
            <Image 
                image={this.state.image} 
                ref={node => { this.image = node; }} 
            />
            <Group globalCompositeOperation={"destination-in"}>
                <Rect fill={"#555555"} 
                    width={200} height={200} 
                    x={100} y={100} 
                />
            </Group>
        </Group>

And the result:

enter image description here

This is odd because the Konva documentation indicates that Group does in fact have a property globalCompositeOperation (see https://konvajs.github.io/api/Konva.Group.html#globalCompositeOperation__anchor).

Any idea how to get (React) Konva to apply the globalCompositeOperation at the Group level rather than just at the Shape level?

1

1 Answers

2
votes

Ah, just found the solution.

It seems that the entire Group needs to be cached before the globalCompositeOperation is applied. I am assuming this means that the Group gets flattened/rasterised first.

In my React component, I achieved the solution as follows:

 import React from 'react';
import { Image, Group, Rect } from 'react-konva';

class CutoutImage extends React.Component {
    state = {
        image: null,
        mask: null
    }

    componentDidMount() {
        const image = new window.Image();
        image.src = "/images/frisbee.jpg";
        image.onload = () => {
            this.setState({ image });
        }
    }

    componentDidUpdate() {
        if (this.image) {
            this.image.cache();
        }
        if (this.mask) {
            this.mask.cache();
        }
    }

    render() {
        return (

            <Group>
                <Image 
                    image={this.state.image} 
                    ref={node => { this.image = node; }} 
                />
                <Group 
                    globalCompositeOperation={"destination-in"} 
                    ref={node => {this.mask = node; }}
                    >
                    <Rect fill={"#555555"} 
                        width={200} height={200} 
                        x={100} y={100} 
                    />
                    <Rect fill={"#dddddd"} 
                        width={200} height={200} 
                        x={300} y={300} 
                    />
                    <Rect fill={"#dddddd"} 
                        width={200} height={100} 
                        x={150} y={350} 
                    />
                </Group>
            </Group>
        )

    }

}
export default CutoutImage;

And the result:

enter image description here