Back to blog

Monday, October 14, 2024

How to develop canva clone project with ReactJS and LidoJS

cover

Building a design tool like Canva is an exciting and educational project that showcases how you can leverage JavaScript libraries to create dynamic, interactive applications. LidoJS, a React-based library for building user interfaces, provides the ideal foundation for creating such tools. In this article, we'll guide you through creating a simple version of a Canva-like design tool using LidoJS.

Key Features of the Canva Clone

Here are the main features we'll implement:

  1. Canvas Workspace: A blank canvas where users can add and manipulate elements.
  2. Adding Elements: Users can add rectangles to the canvas.
  3. Draggable Elements: Users can move elements around the canvas.
  4. Layer Management: Control over the z-index of elements.
  5. Basic Editing Tools: Resizing, rotating, and deleting elements.
  6. Download Functionality: Export the canvas as an image.

Prerequisites

To follow this guide, you'll need:

  • React.js: Since LidoJS is built on React, basic knowledge of React is required.
  • LidoJS: This will handle the interactive UI components of the design tool.

Step-by-Step Guide

1. Set Up the React and LidoJS Project

Download LidoJS source follow your license and install it in your project. Currently, we are using pnpm for package manager so you can run below command to install packages.

pnpm install

2. Creating a Canvas Workspace

The first step is to create a blank canvas where users can add elements. We'll use LidoJS Editor component for this purpose:

import { FontData } from '@lidojs/design-core';
import { Editor, GetFontQuery, PageControl, DesignFrame } from '@lidojs/design-editor';

const Page = () => {
  return (
    <Editor config={config} getFonts={getFonts} uploadImage={uploadImage}>
      <DesignFrame data={data} />
    </Editor>
  );
}

3. Adding Elements

LidoJS already supported elements like Rect, Text, Image, etc. You can use these components to add elements to editor.

import { ShapeType, BoxSize } from '@lidojs/design-core';

const Component = () => {
  const { actions } = useEditor();

  const addShape = (type: ShapeType, { width, height }: BoxSize) => {
    actions.addShapeLayer({
      type: {
        resolvedName: 'ShapeLayer',
      },
      props: {
        shape: type,
        position: {
          x: 0,
          y: 0,
        },
        boxSize: {
          width: width,
          height: height,
        },
        rotate: 0,
        color: '#5E6278',
      },
    });
  }
  const addShape = (type: ShapeType, { width, height }: BoxSize) => {
    actions.addShapeLayer({
      type: {
        resolvedName: 'ShapeLayer',
      },
      props: {
        shape: type,
        position: {
          x: 0,
          y: 0,
        },
        boxSize: {
          width: width,
          height: height,
        },
        rotate: 0,
        color: '#5E6278',
      },
    });
  }

  const addImage = ({thumb, url, width, height}: {thumb: string; url: string; width: number; height: number;}) => {
    actions.addImageLayer(
      { thumb, url },
      { width, height }
    );
  }
  // similar for other layers
}

4. Making Elements Draggable

By default, all layers are draggable in LidoJS. To control the draggability of elements you can define different layer types.

const ImageLayer: LayerComponent<ImageLayerProps> = ({
 // render layer
});

ImageLayer.info = {
  name: 'Image',
  type: 'Image', // Currently we support these layer types: 'Image' | 'Text' | 'Shape' | 'Root' | 'Group' | 'Frame' | 'Svg' | 'Video' | 'Line' | 'ScaleAndResize' | 'ScaleOnly'
};

5. Add Layer Management

We have build-in layer management in LidoJS. You can use LayerManager component to manage layers.

import { LayerId, PageContext } from '@lidojs/design-core';
import { getPosition } from '@lidojs/design-utils';
import ReverseTransformLayer from './layer/ReverseTransformLayer';
import LayerContent from './LayerContent';
import SortableLayer from './SortableLayer';

const Component = () => {
  const { layers, actions, activePage } = useEditor((state) => ({
    layers:
      state.pages[state.activePage] && state.pages[state.activePage].layers,
    activePage: state.activePage,
  }));
  const layerList = useMemo(() => {
    if (!layers) {
      return;
    }
    return reverse(layers['ROOT'].data.child.map((layerId) => layers[layerId]));
  }, [layers]);

  const handleSelectLayer = (layerId: LayerId) => {
    actions.selectLayers(
      activePage,
      layerId,
      dataRef.current.isMultipleSelect ? 'add' : 'replace'
    );
  };
  const handleDragEnd = ({ active, over }) => {
    setActiveId(null);
    if (!over) {
      return;
    }
    if (active.id !== over.id) {
      const newIndex =
        layerList.length - layerList.findIndex((f) => f.id === over.id) - 1;
      actions.moveLayerPosition(activePage, active.id, newIndex);
    }
  };

  return (
    <DndContext
      modifiers={[restrictToVerticalAxis]}
      sensors={sensors}
      onDragCancel={() => setActiveId(null)}
      onDragEnd={handleDragEnd}
      onDragStart={({ active }) => {
        if (!active) {
          return;
        }
        setActiveId(active.id as LayerId);
      }}
    >
      <SortableContext
        items={(layerList || []).map((layer) => layer.id)}
        strategy={rectSortingStrategy}
      >
        {createPortal(
          <DragOverlay>
            {activeLayer ? (
              <LayerContent layer={activeLayer} />
            ) : null}
          </DragOverlay>,
          document.body
        )}
      </SortableContext>
    </DndContext>
  )
}

6. Basic Editing Tools

All layers are resizable and rotatable by default in LidoJS so you don't need to handle these actions manually.

7. Download Functionality

We already have built-in functionality to export the design as an image in LidoJS. You can check in our demo. We have download button on the top of each page.

Final Thoughts

Building a Canva-like design tool using LidoJS is a great way to explore the capabilities of the library and learn how to create interactive UI components. The project covers essential features like adding elements, making them draggable, and exporting the design as an image. LidoJS provides a flexible and powerful foundation for building dynamic user interfaces, making it an excellent choice for projects that require interactive elements and real-time updates.