This is the first day of my participation in the Gwen Challenge in November. Check out the details: the last Gwen Challenge in 2021

preface

The company wanted to make an online drawing, and I really wanted to cry when this demand fell on me. Fortunately, god helped me, I found Konva, and the official document was very conscientious, not only the description was clear, but also there were online examples, and it was very convenient to directly start the development.

So let’s break down the requirements, bit by bit!

The base application

The most basic application of online drawing is drag-and-drop elements, such as dragging an image or shape across a canvas to scale or rotate the image.

The canvas is

, and each Layer is

.

Drag and drop elements

Konva has many shapes built in, such as circles, rectangles, etc. The following example is “Star”, here is a

to try:

import { Circle, Rect, Stage, Layer, Text, Star } from 'react-konva'
import Konva from 'konva'

const Shape = () = > {
    const [star, setStar] = useState({
        x: 300.y: 300.rotation: 20.isDragging: false,})const handleDragStart = () = >{ setStar({ ... star,isDragging: true})},const handleDragEnd = (e: any) = >{ setStar({ ... star,x: e.target.x(),
            y: e.target.y(),
            isDragging: false})},return (
        <Stage width={1000} height={600}>
            <Layer>
                <Star
                    key="starid"
                    id="starid"
                    x={star.x}
                    y={star.y}
                    numPoints={5}
                    innerRadius={20}
                    outerRadius={40}
                    fill="#89b717"
                    opacity=0.8} {
                    draggable
                    rotation={star.rotation}
                    shadowColor="black"
                    shadowBlur={10}
                    shadowOpacity=0.6} {
                    shadowOffsetX={star.isDragging ? 10 : 5}
                    shadowOffsetY={star.isDragging ? 10 : 5}
                    scaleX={star.isDragging ? 1.2 : 1}
                    scaleY={star.isDragging ? 1.2 : 1}
                    onDragStart={handleDragStart}
                    onDragEnd={handleDragEnd}
                />
            </Layer>
        </Stage>)}Copy the code

Some basic properties can be configured for Star, such as: X and Y refer to the coordinate position of the element on the canvas, and rotaition refers to the rotation Angle of the element. Fill refers to the element’s fill color, scaleX, scaleY refers to the element’s enlargement ratio on the X and y axes, and so on.

While dragging, we need to add drag events to the element, such as: Add handleDragStart to change the isDragging attribute so that it deshapes when dragging; Add onDragEnd event, change isDragging and x, Y attributes, change drag position, turn off drag deformation effects, etc.

React-dnd has some properties similar to those of “React-dnd”, but it is much more convenient than react-dnd when using drag events.

Import images

There are two ways to import images: use react-hooks or call the React lifecycle function.

After installing konva’s official library use-Image, we’ll wrap the image component:

import { Image } from 'react-konva'
import useImage from 'use-image'

const KonvaImage = ({ url = ' ' }) = > {
    const [image] = useImage(url)

    return <Image image={image} />
}

export default KonvaImage
Copy the code

deformation

To deform an element, you need to reference Konva’s Transformer component, which can scale and rotate the element. The following code displays Transformer components on which the boundBoxFunc property exists after an element is selected. This function is called when the user triggers the deformation behavior of the element and returns a message containing the deformed element (newBox in the code below).

import React, { useState, useEffect, useRef } from 'react'
import { Image, Transformer } from 'react-konva'
import Konva from 'konva'
import useImage from 'use-image'

const KonvaImage = ({ url = ' ', isSelected = false }) = > {
    const [image] = useImage(url)
    const imgRef = useRef()
    const trRef = useRef()

    useEffect(() = > {
        if (isSelected) {
            trRef.current.nodes([imgRef.current])
            trRef.current.getLayer().batchDraw()
        }
    }, [isSelected])
    return (
        <>
            <Image image={image} draggable ref={imgRef} />
            {isSelected && (
                <Transformer
                    ref={trRef}
                    boundBoxFunc={(oldBox, newBox) = > {
                        // limit resize
                        if (newBox.width < 5 || newBox.height < 5) {
                            return oldBox
                        }
                        const { width, height } = newBox
                        // console.log('width', width);
                        // console.log('height', height);
                        return newBox
                    }}
                />
            )}
        </>)}export default KonvaImage
Copy the code

Synthetic images

Adding a ref to the Stage outputs the canvas base64, which is then turned into an image that triggers a download in the browser.

function downloadURI(uri: string, name: string) {
  var link = document.createElement('a');
  link.download = name;
  link.href = uri;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

  const exportToImage = () = > {
    const uri = stageRef.current.toDataURL();
    downloadURI(uri, 'stage.png'); }; . <button onClick={exportToImage}>export</button>
      <Stage width={1200} height={1000} ref={stageRef}>
Copy the code

If the address of the image crosses the domain, the KONVA component of the image will not display, so you need to set a CORS header for the image server, or do a layer of forwarding in between. And add the Crossorigin attribute to the code layer to enable CORS. Otherwise, errors will be reported on canvas toBlob(), toDataURL(), and getImageData().

Image cross domain Settings

Konva encapsulated use-image provides cross-domain attributes as follows

import useImage from 'use-image'

const [image, status] = useImage(url, 'anonymous')

<Image image={image} />
Copy the code

If cross-domain problems are still displayed and images cannot be generated, add cross-domain headers on the server side or do a layer of forwarding.

reference

  1. Konva official documentation
  2. The Crossorigin attribute in the IMG element