High performance canvas> rendering for React components

Last update: May 8, 2022

react-canvas

Introductory blog post

React Canvas adds the ability for React components to render to <canvas> rather than DOM.

This project is a work-in-progress. Though much of the code is in production on flipboard.com, the React canvas bindings are relatively new and the API is subject to change.

Motivation

Having a long history of building interfaces geared toward mobile devices, we found that the reason mobile web apps feel slow when compared to native apps is the DOM. CSS animations and transitions are the fastest path to smooth animations on the web, but they have several limitations. React Canvas leverages the fact that most modern mobile browsers now have hardware accelerated canvas.

While there have been other attempts to bind canvas drawing APIs to React, they are more focused on visualizations and games. Where React Canvas differs is in the focus on building application user interfaces. The fact that it renders to canvas is an implementation detail.

React Canvas brings some of the APIs web developers are familiar with and blends them with a high performance drawing engine.

Installation

React Canvas is available through npm:

npm install react-canvas

React Canvas Components

React Canvas provides a set of standard React components that abstract the underlying rendering implementation.

<Surface>

Surface is the top-level component. Think of it as a drawing canvas in which you can place other components.

<Layer>

Layer is the the base component by which other components build upon. Common styles and properties such as top, width, left, height, backgroundColor and zIndex are expressed at this level.

<Group>

Group is a container component. Because React enforces that all components return a single component in render(), Groups can be useful for parenting a set of child components. The Group is also an important component for optimizing scrolling performance, as it allows the rendering engine to cache expensive drawing operations.

<Text>

Text is a flexible component that supports multi-line truncation, something which has historically been difficult and very expensive to do in DOM.

<Image>

Image is exactly what you think it is. However, it adds the ability to hide an image until it is fully loaded and optionally fade it in on load.

<Gradient>

Gradient can be used to set the background of a group or surface.

  render() {
    ...
    return (
      <Group style={this.getStyle()}>
        <Gradient style={this.getGradientStyle()} 
                  colorStops={this.getGradientColors()} />
      </Group>
    );
  }
  getGradientColors(){
    return [
      { color: "transparent", position: 0 },
      { color: "#000", position: 1 }
    ]
  }

<ListView>

ListView is a touch scrolling container that renders a list of elements in a column. Think of it like UITableView for the web. It leverages many of the same optimizations that make table views on iOS and list views on Android fast.

Events

React Canvas components support the same event model as normal React components. However, not all event types are currently supported.

For a full list of supported events see EventTypes.

Building Components

Here is a very simple component that renders text below an image:

var React = require('react');
var ReactCanvas = require('react-canvas');

var Surface = ReactCanvas.Surface;
var Image = ReactCanvas.Image;
var Text = ReactCanvas.Text;

var MyComponent = React.createClass({

  render: function () {
    var surfaceWidth = window.innerWidth;
    var surfaceHeight = window.innerHeight;
    var imageStyle = this.getImageStyle();
    var textStyle = this.getTextStyle();

    return (
      <Surface width={surfaceWidth} height={surfaceHeight} left={0} top={0}>
        <Image style={imageStyle} src='...' />
        <Text style={textStyle}>
          Here is some text below an image.
        </Text>
      </Surface>
    );
  },

  getImageHeight: function () {
    return Math.round(window.innerHeight / 2);
  },

  getImageStyle: function () {
    return {
      top: 0,
      left: 0,
      width: window.innerWidth,
      height: this.getImageHeight()
    };
  },

  getTextStyle: function () {
    return {
      top: this.getImageHeight() + 10,
      left: 0,
      width: window.innerWidth,
      height: 20,
      lineHeight: 20,
      fontSize: 12
    };
  }

});

ListView

Many mobile interfaces involve an infinitely long scrolling list of items. React Canvas provides the ListView component to do just that.

Because ListView virtualizes elements outside of the viewport, passing children to it is different than a normal React component where children are declared in render().

The numberOfItemsGetter, itemHeightGetter and itemGetter props are all required.

var ListView = ReactCanvas.ListView;

var MyScrollingListView = React.createClass({

  render: function () {
    return (
      <ListView
        numberOfItemsGetter={this.getNumberOfItems}
        itemHeightGetter={this.getItemHeight}
        itemGetter={this.renderItem} />
    );
  },

  getNumberOfItems: function () {
    // Return the total number of items in the list
  },

  getItemHeight: function () {
    // Return the height of a single item
  },

  renderItem: function (index) {
    // Render the item at the given index, usually a <Group>
  },

});

See the timeline example for a more complete example.

Currently, ListView requires that each item is of the same height. Future versions will support variable height items.

Text sizing

React Canvas provides the measureText function for computing text metrics.

The Page component in the timeline example contains an example of using measureText to achieve precise multi-line ellipsized text.

Custom fonts are not currently supported but will be added in a future version.

css-layout

There is experimental support for using css-layout to style React Canvas components. This is a more expressive way of defining styles for a component using standard CSS styles and flexbox.

Future versions may not support css-layout out of the box. The performance implications need to be investigated before baking this in as a core layout principle.

See the css-layout example.

Accessibility

This area needs further exploration. Using fallback content (the canvas DOM sub-tree) should allow screen readers such as VoiceOver to interact with the content. We've seen mixed results with the iOS devices we've tested. Additionally there is a standard for focus management that is not supported by browsers yet.

One approach that was raised by Bespin in 2009 is to keep a parallel DOM in sync with the elements rendered in canvas.

Running the examples

npm install
npm start

This will start a live reloading server on port 8080. To override the default server and live reload ports, run npm start with PORT and/or RELOAD_PORT environment variables.

A note on NODE_ENV and React: running the examples with NODE_ENV=production will noticeably improve scrolling performance. This is because React skips propType validation in production mode.

Using with webpack

The brfs transform is required in order to use the project with webpack.

npm install -g brfs
npm install --save-dev transform-loader brfs

Then add the brfs transform to your webpack config

module: {
  postLoaders: [
    { loader: "transform?brfs" }
  ]
}

Contributing

We welcome pull requests for bug fixes, new features, and improvements to React Canvas. Contributors to the main repository must accept Flipboard's Apache-style Individual Contributor License Agreement (CLA) before any changes can be merged.

GitHub

https://github.com/Flipboard/react-canvas
Comments
  • 1. port to React 0.14

    Please note that this breaks backward compatibility with older version of React because of the important changes done in react-0.14. So don't increment a patch in the new release but increment a minor or a major (I guess a minor right?). I think it's sane to increment at least a minor anyway, because trying to be backward compatible on this is IMO dangerous.

    Justification of changes:

    • react/lib have moved to fbjs/lib ( like done here https://github.com/reactjs/react-art/pull/59/files )
    • fbjs itself doesn't exposes Object.assign but relies on https://github.com/facebook/fbjs/blob/master/package.json#L29
    • in React 0.14, getDOMNode() is deprecated.
    • as React 0.14 is split into react and react-dom, ReactDOM is used properly in examples

    see also https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html

    Also, react-dom don't need to be added in peerDependencies, which means another target could be possible (imagine, you could render server side with a server-side canvas implementation)

    Reviewed by gre at 2015-10-22 11:05
  • 2. Can not resolve 'fs' in module 'linebreak'

    The linebreaker require 'fs' from native Node.js:

    # src/linebreaker.coffee
    UnicodeTrie = require 'unicode-trie'
    fs = require 'fs'
    base64 = require 'base64-js'
    

    But webpack seems not support native Node.js module:

    ERROR in ./~/react-canvas/~/linebreak/src/linebreaker.js
    Module not found: Error: Cannot resolve module 'fs' in /Users/rui/Sandbox/react-canvas-layout.test/node_modules/react-canvas/node_modules/linebreak/src
    resolve module fs in /Users/rui/Sandbox/react-canvas-layout.test/node_modules/react-canvas/node_modules/linebreak/src
    

    I am trying to use browserify instead of webpack.

    Reviewed by differui at 2015-06-29 03:04
  • 3. Rotten Tomatoes Sample App

    This isn't really an issue, but I wasn't sure how to get in touch. I built a Rotten Tomatoes app to try out React Canvas and thought people would be interested in looking at scroll performance.

    The demo is at http://vivekpanyam.com/samples/reactcanvas/

    Reviewed by VivekPanyam at 2015-02-12 04:26
  • 4. Support Unicode line breaking

    This adds support for correctly breaking lines of unicode text in languages where there are no spaces between the words, such as CJK languages. It uses the linebreak module, which is an implementation of the Unicode line breaking algorithm. The module is also used by my PDFKit project.

    Also adds support for force breaks (e.g. newlines) in the text as well, as a side benefit. :smile:

    Tested by adding some Chinese text to the timeline demo:

    screen shot 2015-02-10 at 4 02 12 pm

    Reviewed by devongovett at 2015-02-11 00:06
  • 5. RFC: Unit testing

    With new pull requests being merged each day, we should look into establishing unit tests and a requirement for all pull requests to have unit tests attached, or we may start to run into regressions.

    Did you have test coverage for your internal Flipboard app @mjohnston? Any preferred tooling?

    Reviewed by brentvatne at 2015-02-14 19:39
  • 6. Fix #108: add crossOrigin prop on Image to allow getImageData/WebGL

    This fixes issue #108,

    It enables crossOrigin on all Image (see CORS enabled image).

    Without crossOrigin=true, if you try this code on any react-canvas app that uses images:

    var c = document.createElement("canvas");
    ctx = c.getContext("2d");
    page=document.querySelector("canvas");
    c.width=page.width;
    c.height=page.height;
    ctx.drawImage(page, 0, 0);
    ctx.getImageData(0, 0, page.width, page.height); // Screenshot the app :)
    

    there will be following exception:

    Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
    

    You need this feature when:

    • you want to use getImageData to screenshot/video capture the application canvas.
    • you want to draw the Canvas into WebGL (e.g: to add a cool effect on it). (this second reason brought me here :) )
    Reviewed by gre at 2015-08-22 20:48
  • 7. A similar canvas solution(Quamolit) here

    I'm very interested in this project since I was trying is a similar approach named Quamolit here: https://github.com/Quamolit Project Quamolit is not yet done. Actually I'm feeling quite stressful to push it forward due to transitions. Anyway here's what I think on declarative programming based on canvas:

    Why use canvas

    I made an expriment before with React Animations(http://repo.cirru.org/fractal-editor/index.html) but got such a conclusion:

    • DOM manipulations(sort) will break CSS transitions, so DOM is not the best solution for animations. Especally when there's a lot of animations. https://dribbble.com/shots/1912396-Jumping-loader

    My solution

    So I created Quamolit, in which each element has it unique id, and its internal state. Then even after sorting, given one element is not destroyed, it will just make transition to its new position or state. In such way, an element can even jump to another branch if its id stays.

    Meanwhile, in elements' lifecycles, there are 'delay', 'entering', 'stable', 'changing', 'postpone', 'leaving'. In most of these periods, there are transitions. By doing this, I think creating animations can be much easier.

    At last I took a solution which is quite different with React in syntax. Here's an early example:

    https://github.com/Quamolit/todolisting/blob/master/src/app/container.coffee

    module.exports = Quamolit.createComponent
      name: 'container'
    
      stores:
        todos: [todoStore, 'all']
    
      getInitialState: -> text: ''
    
      getKeyframe: -> x: 0, y: 0
      getEnteringKeyframe: -> x: -40, y: 0
      getLeavingKeyframe: ->
        @getEnteringKeyframe()
    
      render: ->
        header = [
          handler {x: 0, y: -140}, {}
        ]
    
        items = @state.todos.map (data, index) =>
          order = index
          line {x: 0, y: (80 * order - 80)}, {data: data, id: "line.#{data.id}", isComposing: false}
    
        header.concat items
    

    I'm not sure if that helps react-canvas. Just want to say I'm really interested in such solutions on programming in canvas.

    Reviewed by tiye at 2015-02-11 07:25
  • 8. Add support for various mouse events

    react-canvas is exactly the thing I've been looking for, but it doesn't have support for mouse events yet. This branch adds support for various mouse events. Need to create an example/write tests to make sure that they all work.

    Very interested in feedback/suggestions.

    Reviewed by garbles at 2015-02-11 07:41
  • 9. Use React Hot Loader for live-editing examples

    Don't know if you're into that, but this gives you React Hot Loader powered live-editing for the examples. I noticed it doesn't update <Text> but a lot of other components (in examples and source) auto-update fine.

    It's still npm start.

    Reviewed by gaearon at 2015-02-10 21:41
  • 10. Module not found: Error: Cannot resolve module 'transform'

    Hi,

    I'm using webpack with babel and ES6 + JSX compilation to compile an app and I wanted to take up react-canvas as a module to depend on. I'm having a bit of trouble getting it to compile using webpack. I'm getting the error below.

    ERROR in /Users/nolski/code/.../~/react-canvas/lib/ReactCanvas.js
    Module not found: Error: Cannot resolve module 'transform' in /Users/nolski/code/.../node_modules/react-canvas/lib
     @ /Users/nolski/code/.../~/react-canvas/lib/ReactCanvas.js 14:15-39
    

    I have installed the required dependencies as listed in the README and I think I wrote my webpack.config.js file correctly. Am I missing something?

    module.exports = {
        entry: './app.jsx',
        output: {
            filename: '../pages/queuepage.js'
        },
        module: {
            loaders: [
                {
                    test: /\.jsx?$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'babel',
                    query: {
                        presets: ['react', 'es2015'],
                        retainLines: true
                    }
                },
            ],
            postLoaders: [
                { loader: "transform?brfs" }
            ]
        },
        resolve: {
            // you can now require('file') instead of require('file.jsx')
            extensions: ['', '.js', '.json', '.jsx']
        },
        devtool: 'source-map'
    };
    
    Reviewed by Nolski at 2016-09-15 23:08
  • 11. Installation issues with latest version of React

    Hello,

    I tried installing react-canvas today and received the following error during installation:

    npm WARN peerDependencies The peer dependency [email protected]^0.13.0-beta.1 included from react-canvas will no
    npm WARN peerDependencies longer be automatically installed to fulfill the peerDependency 
    npm WARN peerDependencies in npm 3+. Your application will need to depend on it explicitly.
    npm ERR! Linux 3.19.0-32-generic
    npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install" "react-canvas"
    npm ERR! node v4.2.2
    npm ERR! npm  v2.14.7
    npm ERR! code EPEERINVALID
    
    npm ERR! peerinvalid The package [email protected] does not satisfy its siblings' peerDependencies requirements!
    npm ERR! peerinvalid Peer [email protected] wants [email protected]^0.13.0-beta.1
    npm ERR! peerinvalid Peer [email protected] wants [email protected]^0.14.2
    
    npm ERR! Please include the following file with any support request:
    

    After requiring react-canvas in my application, I got the following error in the Chrome developer console.

    Error: Cannot find module 'react/lib/invariant' from '/path/to/app/node_modules/react-canvas/lib'

    I would appreciate any help in resolving this issue - thanks!

    Reviewed by Carpetfizz at 2015-11-12 21:54
  • 12. [proposal] source of dependency change

    • this request is to fix the problem occurring because of https://github.blog/2021-09-01-improving-git-protocol-security-github/#when-are-these-changes-effective
    Reviewed by DevOpJadeja at 2022-01-11 09:23
  • 13. How to download canvas data (toDataURL("image/png"))

    Hi, first of all, thanks for this great lib.

    I want to modify an image by having some text drawn on it, and download the result.

    Is it possible to achieve this using react-canvas?

    https://stackoverflow.com/a/13198699/11126070 https://stackoverflow.com/a/26554277/11126070

    Thanks for helping!

    Reviewed by pedro-surf at 2020-11-19 04:13
  • 14. Error on install dependencies

    I'm not has success in run the command "npm i react-canvas", due to an error on one dependency. bellow are the errors.

    I realize that the path is different at your project. "github.com/popham/scroller"

    npm install react-canvas npm ERR! C:\Program Files\Git\cmd\git.EXE ls-remote -h -t git://github.com/mjohnston/scroller.git npm ERR! npm ERR! fatal: unable to connect to github.com: npm ERR! github.com[0: 18.228.52.138]: errno=No such file or directory npm ERR! npm ERR! npm ERR! exited with error code: 128

    npm ERR! A complete log of this run can be found in:

    Reviewed by wilsondonizetti at 2020-01-06 13:55
  • 15. Whether react 16.12 is supported?

    I wrote one based on the official example, and the following error occurred:

    ./node_modules/react-canvas/lib/ContainerMixin.js Module not found: Can't resolve 'react-dom/lib/ReactMultiChild' in 'D:\wu\react\learn\symbol\node_modules\react-canvas\lib'

    I don't know where to start when I see this kind of mistake.

    Reviewed by wutiange at 2020-01-02 07:05
  • 16. when Surface's enableCSSLayout is set to true, Imageโ€˜s position is not right

    `

         <Surface top={0} left={0}  width={size.width} height={size.height} enableCSSLayout={true}>    
    
            <Image style={this.getImageStyle()} src={iconUrl}/>  
    
            <Text style={this.getTextStyle()}>Text</Text>    
    
         </Surface>
    

    `

    the right position is at the top border , but I have two boder in the canvas, and the icon is at the secoend

    2018-03-20_152912

    Reviewed by IvyLady at 2018-03-20 05:46
๐Ÿฆโ€ข [Work in Progress] React Renderer to build UI interfaces using canvas/WebGL
๐Ÿฆโ€ข [Work in Progress] React Renderer to build UI interfaces using canvas/WebGL

React Ape React Ape is a react renderer to build UI interfaces using canvas/WebGL. React Ape was built to be an optional React-TV renderer. It's mainl

May 12, 2022
Connect, discover, be free to choose between WebGL / Canvas (PIXI) / DOM or any other UI renderer
Connect, discover, be free to choose between WebGL / Canvas (PIXI) / DOM or any other UI renderer

React Liberty Be free to choose between WebGL / Canvas / DOM / Native or any other UI renderer This is a React library designed to abstract renderer b

Sep 10, 2021
React render tracker โ€“ a tool to discover performance issues related to unintentional re-renders and unmounts
React render tracker โ€“ a tool to discover performance issues related to unintentional re-renders and unmounts

React Render Tracker React Render Tracker โ€“ a tool to discover performance issues related to unintended re-renders. React Render Tracker (RRT) present

May 4, 2022
React JSX Renderer is a React Component for rendering JSX to React nodes.

React JSX Renderer A React Component for Rendering JSX. Description React JSX Renderer is a React Component for rendering JSX to React nodes.

Jan 9, 2022
React server side rendering support for Fastify with Next

React server side rendering support for Fastify with Next

May 11, 2022
Server-side rendering for your Webpack-built applications (e.g. React)

webpack-isomorphic-tools is a small helper module providing very basic support for isomorphic (universal) rendering when using Webpack.

Apr 26, 2022
render React components to Sketch โš›๏ธ๐Ÿ’Ž
render React components to Sketch โš›๏ธ๐Ÿ’Ž

render React components to Sketch; tailor-made for design systems Quick-start ??โ€ First, make sure you have installed Sketch version 50+, & a recent n

May 10, 2022
Render React components to DMX light systems.

React DMX Render React components to DMX light systems. The problem Controlling a handful of lights with DMX is pretty simple and straightforward. How

Apr 11, 2022
Reactron is a React component visualizer that allows you to traverse an app's fiber tree and render components individually.
Reactron is a React component visualizer that allows you to traverse an app's fiber tree and render components individually.

Reactron Empowering the Development Process from Top to Bottom A tool for the complete and total visualization of your React application Reactron bols

Mar 14, 2022
๐Ÿ‡จ๐Ÿ‡ญ A React renderer for Three.js (web and react-native)
๐Ÿ‡จ๐Ÿ‡ญ A React renderer for Three.js (web and react-native)

react-three-fiber react-three-fiber is a React renderer for threejs on the web and react-native. npm install three react-three-fiber These demos are r

May 15, 2022
๐Ÿ™ A minimal implementation of react-dom using react-reconciler
๐Ÿ™ A minimal implementation of react-dom using react-reconciler

react-tiny-dom react-tiny-dom is a minimal implementation of react-dom as custom renderer using React 16 official Renderer API. The purpose of this pr

May 10, 2022
๐Ÿ‡จ๐Ÿ‡ญ A React renderer for Three.js (web and react-native)
๐Ÿ‡จ๐Ÿ‡ญ A React renderer for Three.js (web and react-native)

react-three-fiber react-three-fiber is a React renderer for threejs on the web and react-native. npm install three react-three-fiber These demos are r

May 19, 2022
[ Unmaintained due to raphamorim/react-ape ] React Renderer for low memory applications
[ Unmaintained due to raphamorim/react-ape ] React Renderer for low memory applications

React-TV ยท react-tv: React Renderer for low memory applications. react-tv-cli: React Packager for TVs. Currently under development. import React from

May 10, 2022
React PDF viewer - A React component to view a PDF document
React PDF viewer - A React component to view a PDF document

A React component to view a PDF document. It's written in TypeScript, and powered by React hooks completely.

May 14, 2022
Osd-react-renderer - A custom React renderer for OpenSeadragon

osd-react-renderer A custom React renderer for OpenSeadragon viewer, built using

Jan 24, 2022
๐ŸŒˆ React for interactive command-line apps
๐ŸŒˆ React for interactive command-line apps

React for CLIs. Build and test your CLI output using components. Ink provides the same component-based UI building experience that React offers in the

May 13, 2022
โš›๏ธ A React renderer for Figma
โš›๏ธ A React renderer for Figma

React Figma A React renderer for Figma. Use React components as a source for your designs. ?? Compatible with react-native, react-sketchapp, react-pri

May 10, 2022
Create amazing 360 and VR content using React

React 360 React 360 is a framework for the creation of interactive 360 experiences that run in your web browser. It pairs modern APIs like WebGL and W

May 10, 2022
โƒ A react null renderer

Nothing to see here ... Quite so. This package allows you to bring Reacts high-level component abstraction to Node, or wherever you need it. Why not m

Apr 18, 2022