Image manipulation in React with Konva.js

What is Konva?

 

Konva an HTML5 Canvas JavaScript framework that extends the 2d context
by enabling canvas interactivity for desktop and mobile applications, enables high performance animations, transitions, node nesting, layering, filtering,
caching, event handling for desktop and mobile applications, and much more.

Setting the Konva Stage

Here we set the Stage which is our Canvas, and here we can set it’s dimensions, also here we set the image which we will be manipulating, we add a Konva <Image> tag into our Stage, which you can see bellow, the prop that we are sending as image, is the source of the image, it has to be set as

let image = new window.Image();

image.src = ‘imageUrl’;

We also set the rectangle with which the user can select which part of the image he want’s to crop, and we will get into detail about that later.

<Stage

<Stage

       width={100}

       height={100}>

    <Layer>

        <Image

            image={img}

            rotation={this.state.rotation}

            ref={node => {

                this.state.imageNode = node;

            }}

            name={"image"}

        ></Image>

            <Rect

                ref={node => {

                    this.state.rect = node;

                }}

                width={100}

                height={100}

                rotation={this.state.rotation}

                x={0}

                y={0}

                draggable

                name={'rect'}

            />

            <TransformerComponent

                ref={node => {

                    this.state.transformNode = node;

                }}

                selectedShapeName={'rect'}

            />

        }

    </Layer>

</Stage>

 

 

Rotating an image around it’s center

When the rotation is 0 degrees, our x and y point for the image is in the top left corner of our image, which is displayed bellow :

 

This is important because when ever we rotate the image for 90 degrees each time, our x and y point shift to the next corner and height and width interchange depending on the rotation. This completely changes our calculations for positioning it around it’s center point, because the x and y props that we get from Konva about the image are in reference to the position of the image inside the Stage.

To make it more clear, at 0 degrees the x and y point will be in the top left corner, 90 degrees it will be in the top right corner, 180 degrees bottom right corner, 270 degrees bottom left corner, and when the user rotates the image to 360 we will instead set it to 0.

With all that said, our rotate function will look like this :

 

 

rotate(){

if(this.state.rotation == 270) { this.state.rotation = 0; }

    else {

        this.state.rotation = this.state.rotation + 90;

    }

   

    if(this.state.rotation == 0){

        this.state.imageNode.x(this.state.stage.attrs.width - this.state.imageNode.attrs.width)/2;

        this.state.imageNode.y(this.state.stage.attrs.height - this.state.imageNode.attrs.height)/2;

    }else if(this.state.rotation == 90){

        this.state.imageNode.x(this.state.stage.attrs.width - this.state.imageNode.attrs.height)/2 + this.state.imageNode.attrs.height;

        this.state.imageNode.y(this.state.stage.attrs.height - this.state.imageNode.attrs.width)/2;

    }else if(this.state.rotation == 180){

        this.state.imageNode.x(this.state.stage.attrs.width - this.state.imageNode.attrs.width)/2 + this.state.imageNode.attrs.width;

        this.state.imageNode.y(this.state.stage.attrs.height - this.state.imageNode.attrs.height)/2 + this.state.imageNode.attrs.height;

    }else if(this.state.rotation == 270){

        this.state.imageNode.x(this.state.stage.attrs.width - this.state.imageNode.attrs.height)/2;

        this.state.imageNode.y(this.state.stage.attrs.height - this.state.imageNode.attrs.width)/2 + this.state.imageNode.attrs.width;

    }

}

 

Cropping an image

  

In this example, the red line is the Stage(Canvas), the blue rectangle is the image we wan’t to crop, the white dotted rectangle is the part of the image the user want’s to crop.

Our crop function will look like this :

 

 

crop(){

this.state.imageNode.crop({

        x: this.state.rect.attrs.x - this.state.imageNode.attrs.x,

        y: this.state.rect.attrs.y - this.state.imageNode.attrs.y,

        width: this.state.rect.attrs.width,

        height: this.state.rect.attrs.height

    });

}

 

Here we access the crop function of the image, by accessing it’s reference which we set in the start of this article, the crop properties that we send to the crop function are relative to it’s position in reference to the image, not in reference to it’s position to the Stage(Canvas). Thus, our calculations for the starting point where the crop starts is :

 

  1. Coordinates

x: this.state.rect.attrs.x – this.state.imageNode.attrs.x

y: this.state.rect.attrs.y – this.state.imageNode.attrs.y,

 

 

Here we calculate the coordinates of the rectangle to get where it is positioned in reference to the image.

  1. Dimensions

width: this.state.rect.attrs.width

height: this.state.rect.attrs.height

 

This is a simple one, the width and height of the cropped image we want, are the width and height of the rectangle.

 

DOCS

The docs for React Konva can be found here, there you can look for a better understanding of the basics of React Konva, if we went through those that would be a very lengthy blog post, here we went through only some key functionalities that you can develop with the help of Konva.