Introduction

Oracle JavaScript Extension Toolkit (Oracle JET) empowers developers to build efficient and responsive web applications. It leverages the Virtual DOM (VDOM) architecture, which allows for the creation of VComponents. In this post, I will guide you through the process of creating a VComponent in Oracle JET that includes a drawing pad.

Prerequisites

Ensure that you have Node.js and npm installed on your computer and that you have installed Oracle JET CLI globally. You should also have an Oracle JET application set up and ready for development.

Step 1: Create a new VComponent

Navigate to your Oracle JET application’s root folder and create a new VComponent using the Oracle JET CLI command:

npx @oracle/ojet-cli create component drawingpad-component 

Step 2: Define the VComponent

Now, let’s define the structure of our drawing pad VComponent. Open the drawingPad-component.tsx file and define the component using JSX syntax. Here is the entire code for the drawing pad component. Obviously you can modify to suit your needs, but you can also just copy the code below and paste it :

import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import "ojs/ojbutton";

import { h, Component, ComponentChild } from "preact";
import componentStrings = require("ojL10n!./resources/nls/drawingpad-component-strings");
import "css!./drawingpad-component-styles.css";

type Props = {
  footerText?: string;
}


type State = {
  drawing: boolean
  context?: CanvasRenderingContext2D;
};
/**
 * 
 * @ojmetadata version "1.0.0"
 * @ojmetadata displayName "A user friendly, translatable name of the component"
 * @ojmetadata description "A translatable high-level description for the component"
*/
@customElement("drawingpad-component")
export class DrawingpadComponent extends Component < ExtendGlobalProps < Props >, State> {
  
private canvasRef: any = null;

constructor(props: Readonly<{}>) {
  super(props);
  this.state = { drawing: false };
}

 render(props: Props, state: State): ComponentChild {
  return (
    <div>
    <canvas width={500} height={500}
      ref={(ref) => (this.canvasRef = ref)}
      onMouseDown={this.handleMouseDown}
      onMouseUp={this.handleMouseUp}
      onMouseMove={this.handleMouseMove}
    />

    <oj-button  onojAction={this.clearCanvas}>Clear</oj-button>
</div>
  );  }
  
componentDidMount() {
  this.setState({
    context: this.canvasRef.getContext('2d'),
    drawing: false
  });
}

private clearCanvas = () => {
  const context = this.state.context;
  if (context) {
    context.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
  }
};

private handleMouseDown = (event: MouseEvent) => {
  const rect = this.canvasRef.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  this.setState({ drawing: true });
  const context = this.state.context;
  if (context) {
    context.beginPath();
    context.moveTo(x, y);
  }
};


private handleMouseUp = (event: MouseEvent) => {
  this.setState({ drawing: false });

};

private handleMouseMove = (event: MouseEvent) => {
  if (!this.state.drawing) return;
  const rect = this.canvasRef.getBoundingClientRect();
  this.draw(event.clientX - rect.left, event.clientY - rect.top);
};

draw(x: number, y: number) {
  const context = this.state.context;
  if (context && this.state.drawing) {
    context.lineTo(x, y);
    context.stroke();
  }
}
}

In this example, we’re creating a canvas and implementing basic drawing functionality. When the mouse button is pressed down, drawing becomes true, and when the button is released, drawing becomes false. During mouse movement, if drawing is true, we draw on the canvas.

Step 3: Style the VComponent

You can add custom styling to your VComponent in the drawingPad.css file. For instance, you might want to define the size and border of the drawing pad:

canvas {
  width: 500px;
  height: 500px;
  border: 1px solid black;
}

Step 4: Use the VComponent

To use your new DrawingPad VComponent, import it into the desired view, and include it in the render method:

import { h } from "preact";
import { customElement, GlobalProps } from 'ojs/ojvcomponent';
import { DrawingpadComponent } from 'drawingpad-component/loader'

export function Content() {
  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <DrawingpadComponent/>
    </div>
  );
};

In this example, I added the DrawingpadComponent component to the index.tsx view. Adjust according to your application structure and the specific view where you want to include the drawing pad.

Now that you’ve created and included the DrawingPad VComponent in your application, you can run and test it.

Click the play button to see a movie of the component in action.

Note regarding HTML5 canvas and 2D:

The 2d context is a built-in object in HTML5 that provides properties and methods for drawing on the canvas. When you have a canvas element in your HTML, you can access this context using the getContext() method of the canvas element, like so:

let canvas = document.getElementById('myCanvas');
let ctx = canvas.getContext('2d');

In this example, ctx is the context object, and it’s through this object that you can draw on the canvas. Here are some of the things you can do with the 2D context:

  • Draw rectangles: You can use the fillRect(x, y, width, height) method to draw a filled rectangle, or the strokeRect(x, y, width, height) method to draw a rectangle outline.
  • Draw paths: You can use methods like moveTo(x, y), lineTo(x, y), and arc(x, y, radius, startAngle, endAngle) to create paths, and then use the fill() or stroke() method to draw the path.
  • Change colors: You can use the fillStyle and strokeStyle properties to change the colors used by the fill and stroke methods.
  • Change line styles: You can use the lineWidth, lineCap, and lineJoin properties to change the style of the lines drawn by the stroke method.
  • Draw text: You can use the fillText(text, x, y) or strokeText(text, x, y) method to draw text.
  • Draw images: You can use the drawImage(image, dx, dy) or drawImage(image, dx, dy, dWidth, dHeight) method to draw images.
  • Save and restore the drawing state: You can use the save() and restore() methods to save the current drawing state (colors, line styles, etc.) to a stack, and then restore it later. This is useful when you want to make temporary changes to the drawing state without affecting the rest of your drawing code.

In the drawingPad VComponent which was created in this post, we are using the 2D context to draw lines on the canvas when the user moves the mouse while holding down the mouse button. The lineTo(x, y) method is used to extend the current path to a new point, and the stroke() method is used to draw the path. You’re also using the beginPath() method to start a new path each time the user presses the mouse button, and the clearRect(x, y, width, height) method to clear the canvas when the user clicks the “Clear” button.