0.7.0 • Published 1 year ago

react-2d-canvas v0.7.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

React 2D Canvas

Draw 2D shapes, images and text on an HTML Canvas element using a declarative JSX syntax. The library builds on top of custom elements from the Web Components standard.

React 2D Canvas makes it easy to create simple 2D canvas games by leveraging on the developer ergonomics that React offers.

React 2D Canvas is lightweight with focus on efficiently producing 2D drawings. Features such as audio, hit detection and animation are not built in. Users of React 2D Canvas are encouraged to incorporate suitable libraries to add auxiliary functionality or roll their own custom solutions as needed.

Table of Content

Change Log

0.7.0

  • Add <TextField> canvas element to render multiple lines of text

0.6.0

  • Make it possible to stop propagating mouse and wheel events from Shape components to the parent <Layer> component

0.5.0

  • Facilitate global zooming and panning
  • Add some code examples
  • Add support for keyboard and wheel events

Example Usage

Example canvas

import {
  Stage,
  Layer,
  ScaleMode,
  RoundedRectangle,
  Circle,
  Label
} from 'react-2d-canvas';

function App() {
  return (
    <Stage
      width={640}
      height={420} 
      backgroundColor="#2d002b"
      scaleMode={ScaleMode.SCALE_TO_FIT}
    >
      {/*Static background layer*/}
      <Layer>
        {[...Array(100).keys()].map(index => (
          <Circle
            key={index}
            x={Math.floor(Math.random() * 620) + 20}
            y={Math.floor(Math.random() * 400) + 20}
            radius={Math.floor(Math.random() * 2) + 1}
            backgroundColor="#fff"
            shadowColor="#fff"
            shadowOffsetX={2}
            shadowOffsetY={2}
            shadowBlur={10}
            opacity={Math.random()}
          />
        ))}
      </Layer>
      {/*Game action layer*/}
      <Layer>
        <Label
          x={360}
          y={50}
          color="#08f7fe"
          fontSize={40}
          fontFamily="Retro Gaming"
          shadowColor="#08f7fe"
          shadowBlur={8}
        >
          28.100.000
        </Label>
        {[...Array(4).keys()].map(column => (
          [...Array(2).keys()].map(row => (
            <RoundedRectangle
              key={`[${column},${row}]`}
              x={100 + column * 110 + (row % 2 ? 50 : 0)}
              y={100 + row * 50}
              radius={4}
              width={100}
              height={40}
              borderColor={row % 2 ? '#08f7fe' : '#f5d300'}
              backgroundColor={row % 2 ? '#fe53bb' : '#09fbd3'}
              borderWidth={6}
              shadowColor={row % 2 ? '#08f7fe' : '#f5d300'}
              shadowBlur={10}
            />
          ))
        ))}
        {[...Array(5).keys()].map(index => (
          <Circle
            key={index}
            x={280 - 15 * index}
            y={280 - 15 * index}
            radius={22}
            borderColor="#08f7fe"
            borderWidth={6}
            backgroundColor="#7122fa"
            shadowColor="#08f7fe"
            shadowBlur={10}
            scaleY={0.8}
            opacity={0.2 * index + 0.2}
            rotation={45 * Math.PI / 180}
          />
        ))}
        {[...Array(5).keys()].map(index => (
          <RoundedRectangle
            key={index}
            x={280 - 6 * index}
            y={370}
            width={160}
            height={50}
            radius={4}
            borderColor="#fe53bb"
            borderWidth={6}
            backgroundColor="#08f7fe"
            shadowColor="#fe53bb"
            shadowBlur={10}
            opacity={0.2 * index + 0.2}
          />
        ))}
      </Layer>
    </Stage>
  );
}

Browser Support

React 2D Canvas is supported by all modern web browsers. The support is mainly limited by the use of Autonomous custom elements.

The following browser versions are supported

  • Edge >79
  • Firefox >63
  • Chrome >54
  • Safari >10.1
  • Opera >41
  • Safari iOS >10.3
  • Android browser >94
  • Opera Mobile >64
  • Chrome for Android >94
  • Firefox for Android >92
  • UC browser for Android >12.12

API

All components and the ScaleMode name space are exported individually from the main package.

import {
  Stage,
  ScaleMode,
  Layer,
  Rectangle,
  /* etc */
} from 'react-2d-canvas';

\

The <Stage> component is the outermost container and should have one or more <Layer> child components. The <Stage> component handles dimensions and scaling.

import { Stage, ScaleMode } from 'react-2d-canvas';

<Stage width={600} height={400} scaleMode={ScaleMode.SCALE_TO_FIT}>
  {/*...*/}
</Stage>
Properties / AttributesDescriptionDefault value
scaleModeControlling how the child <Layer> components scale. Available options are ScaleMode.SCALE_TO_FIT or ScaleMode.SCALE_TO_COVER 
widthSetting the width of the child <Layer> components.300
heightSetting the height of the child <Layer> components.300
backgroundColorSetting the background color for the <Stage> component. Accepts a CSS color value. 

\

<Stage
  width={600}
  height={400}
  scaleMode={ScaleMode.SCALE_TO_FIT}
>
  <Layer>
    {/* Static background comoponents */}
  </Layer>
  <Layer>
    {/* Game play action and animation components */}
  </Layer>
  <Layer>
    {/* UI and interactive components */}
  </Layer>
</Stage>
AttributesDescriptionDefault value
scaleXHorizontal scaling1
scaleYVertical scaling1
offsetXHorizontal translation0
offsetYVertical translation0
tabIndexIndicates that the canvas element can be focused, which is required for keyboard events to be fired 

Each <Layer> component holds an HTML <canvas> element. Using multiple sibling <Layer> components is a good way of optimizing canvas redrawing when animating content. See for instance "Use multiple layered canvases for complex scenes" for a detailed explanation of this optimization strategy.

The following DOM event handlers passed to the <Layer> component will be forwarded to the underlying HTML <canvas> element. Note that the <Layer> component does currently not accept touch event handlers. (Child shape components accept touch event handlers, as mentioned below.)

  • onClick
  • onMouseMove
  • onMouseDown
  • onMouseUp
  • onDoubleClick
  • onContextMenu
  • onMouseOut
  • onMouseOver
  • onWheel
  • onKeyDown
  • onKeyUp
  • onKeyPress

Zooming and panning

Global zooming and panning can be implemented using the scaleX, scaleY, offsetX, and offsetY attributes of the <Layer> component. (See the pan and zoom example implementation, which relies on the mouse wheel to zoom in/out and click-and-drag to pan the layer.)

Shape Components

Shape components, such as <Rectangle>, <Circle>, and <Label> are available for representing different graphical elements and user interface controls.

All shape components accept a ref property which will be forwarded to the underlying element. Using a ref enables the possibilities to read properties / attributes on the element.

All shape components have the following common attributes:

Common AttributesDescriptionDefault value
xX-coordinate0
yY-coordinate0
backgroundColorBackground color expressed as a CSS color string 
borderColorBorder color expressed as a CSS color string
borderWidthBorder width in pixels1
opacityOpacity in the range of 0 to 11
originXOrigin on the x-axis in relation to the width of the shape, in the range of 0 to 1.A value of 0 will left align the shape, 0.5 will center align, and 1 will right align the shape. Also affects the rotation origin.0.5
originYSame as originX, but related to the y-axis.0.5
rotationRotation in radians. The rotation origin is controlled by the values of orignX and originY0
scaleXScaling factor in horizontal direction.1
scaleYScaling factor in the vertical direction.1
shadowColorShadow color expressed as a CSS color string. 
shadowBlurLevel of shadow blur.0
shadowOffsetXDistance that shadows will be offset horizontally.0
shadowOffsetYDistance that shadows will be offset vertically.0
borderDashString of comma separated numbers that define the border dash pattern. 
zIndexDraw priority among children. Child components with higher zIndex will be drawn on top of siblings with lower zIndex.0

Event Handlers

All shapes accept the following ui event handlers:

  • onClick
  • onMouseMove
  • onMouseDown
  • onMouseUp
  • onDoubleClick
  • onContextMenu
  • onMouseOut
  • onMouseOver
  • onMouseOver
  • onTouchStart
  • onTouchMove
  • onTouchEnd
  • onTouchCancel
  • onWheel

Nesting and Inheritance

Shape components can well be nested. Child components will be affected by the following attributes of their parent component:

Attributes affected by parentDescription
xChild's x-coordinate will be an offset of the parent's x-coordinate
ySame as above, but for the y-coordinate
opacityChild's opacity will be multiplied by the parent's opacity
rotationChild's rotation will be increased by the parent's rotation

Nesting components makes it possible to update the relative position, opacity and rotation of a group of child components, by just changing the corresponding property on their common ancestor.

\

<Rectangle
  x={100}
  y={100}
  width={75}
  height={150}
  borderColor="#333"
  backgroundColor="#666"
/>

The <Rectangle> component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
widthPixel width.0
heightPixel height.0
backgroundImagePath to image. 

\

<RoundedRectangle
  x={100}
  y={100}
  width={75}
  height={150}
  radius={20}
  borderColor="#333"
  backgroundColor="#666"
/>

The <RoundedRectangle> component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
widthPixel width.0
heightPixel height.0
radiusCorner radius.0
backgroundImagePath to image. 

\

<Circle
  x={100}
  y={100}
  radius={20}
  borderColor="#333"
  backgroundColor="#666"
/>

The <Circle> component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
radiusRadius.0
backgroundImagePath to image. 

\

<Arc
  x={100}
  y={100}
  radius={20}
  borderColor="#333"
  startAngle={45 * Math.PI / 180}
  endAngle={180 * Math.PI / 180}
/>

The <Arc> component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
radiusRadius.0
startAngleStart angle in radians measured from 12'o clock.0
endAngleEnd angle in radians measured from 12'o clock.0
counterclockwiseBoolean value indicating direction of drawing from startAngle to endAngle.false
backgroundImagePath to image. 

\

<Sector
  x={100}
  y={100}
  radius={20}
  borderColor="#333"
  startAngle={45 * Math.PI / 180}
  endAngle={180 * Math.PI / 180}
/>

The <Sector> (ie. circular sector) component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
radiusRadius.0
startAngleStart angle in radians measured from 12'o clock.0
endAngleEnd angle in radians measured from 12'o clock.0
counterclockwiseBoolean value indicating direction of drawing from startAngle to endAngle.false
backgroundImagePath to image. 

\

<Label
  x={100}
  y={100}
  color="red"
  fontFamily="Helvetica"
  fontSize={30}
  fontStyle="italic"
  fontWeight="bold"
  align="center"
  baseline="middle"
  maxWidth={40}
>
  A single line of text
</Label>

The <Label> component accepts all common attributes (except originX and originY), and the additional attributes listed below.

Use the align and baseline attributes to change the origin of a <Label>.

Note that the <Label> component does not accept width nor height attributes since the dimensions are derived from the text content, but there are read-only width and height element properties that can be accessed using a ref to the underlying element.

AttributesDescriptionDefault value
colorText color expressed as CSS color string. 
fontSizePixel size of the text.10
fontFamilyFont family name of the text."sans-serif"
fontStyleFont style expressed using one of the following values normal, italic, oblique. 
fontWeightFont weight expressed in CSS font weight string or number.normal
baselineBaseline of the text using one of the following values top, hanging, middle, alphabetic, ideographic, bottom."alphabetic"
alignHorizontal alignment of the text using one of the following values left, right, start, end, center."start"
maxwidthPixel width at which point the text will be cropped and appended with ellipses to fit within the maxWidth.Infinity

\

<TextField
  x={100}
  y={100}
  width={150}
  color="red"
  fontFamily="Helvetica"
  fontSize={30}
  fontStyle="italic"
  fontWeight="bold"
  align="center"
  baseline="middle"
  maxWidth={40}
>
  Text that will break into multiple lines
</TextField>

The <TextField> component accepts all common attributes (except originX and originY), and the additional attributes listed below.

Use the align and baseline attributes to change the origin of a <TextField>.

Note that the <TextField> component does not accept a height attributes since the height is derived from the text content, but there is a read-only height element properties that can be accessed using a ref to the underlying element.

A block of multiple lines can be adjusted horizontally using the align attribute. However, the use of the baseline attribute is less intuitive as it operates on individual lines. To vertically adjust a block of multiple lines relative a parent element, the y coordinate of the <TextField> must be adjusted. See the TextField example for this can be done for dynamic text content.)

AttributesDescriptionDefault value
colorText color expressed as CSS color string. 
fontSizePixel size of the text.10
fontFamilyFont family name of the text."sans-serif"
fontStyleFont style expressed using one of the following values normal, italic, oblique. 
fontWeightFont weight expressed in CSS font weight string or number.normal
baselineBaseline of the text using one of the following values top, hanging, middle, alphabetic, ideographic, bottom."alphabetic"
alignHorizontal alignment of the text using one of the following values left, right, start, end, center."start"
widthPixel width at which point the line of text will be broken to fit within the width.200
lineHeightRelative line height.1.2

\

<Image
  x={100}
  y={100}
  width={48}
  height={48}
  src={myIcon}
/>

The <Image> component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
widthPixel width.0
heightPixel height.0
srcPath to image. 

\

<Polygon
  x={100}
  y={100}
  borderColor="#333"
  backgroundColor="#666"
  radius={48}
  sides={6}
/>

The <Polygon> (ie. regular polygon, such as a hexagon) component accepts all common attributes, and the additional attributes listed below.

AttributesDescriptionDefault value
radiusRadius.0
sidesNumber of sides.0
backgroundImagePath to image. 

Event propagation

UI events don't propagate from children to parent Shape Components, as you would expect from events in a normal DOM tree. However, mouse events and wheel events handled by a Shape Component will typically propagate to the parent </Layer> component.

Call event.stopPropagation() in a mouse or wheel event handler of a Shape Component to prevent those events from being propagated to the parent <Layer> component. (See the stop event propagation example implementation.)

License

MIT

0.7.0

1 year ago

0.5.0

2 years ago

0.6.0

2 years ago

0.5.1

2 years ago

0.1.0

2 years ago

0.3.0

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.4.0

2 years ago

0.3.1

2 years ago

0.0.10

2 years ago

0.0.11

2 years ago

0.0.12

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.5

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.4

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago