TutorialsCourses

React Art and Paths.js

Intro

Paths.js is a cool library. It comes with 3 levels of generating paths.

  • Low level which helps you generate paths/lines.
  • Mid level which generates paths for shapes
  • High level which takes a set of data and generates graphs

All of these are great when working with react-art because it's just returning data. I'll say it once, and a million more but libraries that just generate data make it easy to traverse and render with react-art.

Paths

UPDATE:

Sebastian Markbage (the creator of ReactART and ART), informed me that ReactART itself has a Path implementation. I realized this but didn't think much of it. However per the discussion here https://discuss.reactjs.org/t/react-art-with-paths-js/492 the ART path is faster as it takes advantage of the current mode to create the most efficient path instead of taking a string and converting it back into native for instructions for canvas rendering.

The ReactART.Path has similar methods. Check out the discuss thread for a link to the implementation to find out the supported methods. I'm currently working on ReactART documentation so expect that soon.

Example:

var path = Path()
  .moveto(10, 20)
  .lineto(30, 50)
  .lineto(25, 28)
  .qcurveto(27, 30, 32, 27)
  .closepath();

This is can just be plugged right into react-art Shape element.

Like so

var React = require("react"),
  ReactArt = require("react-art"),
  Surface = ReactArt.Surface,
  Shape = ReactArt.Shape,
  Path = require("paths-js/path");

var Demo = React.createClass({
  getInitialState: function () {
    return {
      to: {
        x: 30,
        y: 50,
      },
    };
  },
  getPath: function () {
    var path = Path()
      .moveto(10, 20)
      .lineto(this.state.to.x, this.state.to.y)
      .lineto(25, 28)
      .qcurveto(27, 30, 32, 27)
      .closepath();

    return path.print();
  },
  startAnimating: function () {
    if (this.state.to.x === 100) {
      this.addToPosition = -1;
    } else if (this.state.to.x === 29) {
      this.addToPosition = 1;
    }

    this.state.to.x += this.addToPosition;
    this.state.to.y += this.addToPosition;

    this.setState(this.state);
  },
  componentDidMount: function () {
    this.addToPosition = 1;
    setInterval(this.startAnimating, 17);
  },
  render: function () {
    return (
      <div>
        <Surface width={500} height={500}>
          <Shape d={this.getPath()} stroke="#000" strokeWidth={1} />
        </Surface>
      </div>
    );
  },
});

module.exports = Demo;

Simple Shapes

Now react-art already comes with a few different shapes but paths.js have a few built in as well, like Rectangle and Bezier curve.

var rectangle = Rectangle({
  top: 10,
  bottom: 3,
  left: -2,
  right: 5,
});

And how that looks is very similar to the previous example

var React = require("react"),
  ReactArt = require("react-art"),
  Surface = ReactArt.Surface,
  Group = ReactArt.Group,
  Shape = ReactArt.Shape,
  Rectangle = require("paths-js/rectangle"),
  Bezier = require("paths-js/bezier");

var Demo = React.createClass({
  getPath: function () {
    var rectangle = Rectangle({
      top: 10,
      bottom: 3,
      left: -2,
      right: 5,
    });

    return rectangle.path.print();
  },
  getBez: function () {
    var points = [
      [1, 50],
      [50, 100],
      [100, 3],
      [4, 0],
    ];
    var curve = Bezier({
      points: points,
      tension: 0.2,
    });

    return curve.path.print();
  },
  render: function () {
    return (
      <div>
        <Surface width={500} height={500}>
          <Group x={100} y={100}>
            <Shape d={this.getPath()} stroke="#000" strokeWidth={1} />
          </Group>
          <Group x={200} y={200}>
            <Shape d={this.getBez()} stroke="#000" strokeWidth={1} />
          </Group>
        </Surface>
      </div>
    );
  },
});

module.exports = Demo;

Graphs

var pie = Pie({
  data: [
    { name: "Italy", population: 59859996 },
    { name: "Mexico", population: 118395054 },
    { name: "France", population: 65806000 },
    { name: "Argentina", population: 40117096 },
    { name: "Japan", population: 127290000 },
  ],
  accessor: function (x) {
    return x.population;
  },
  compute: {
    color: function (i) {
      return somePalette[i];
    },
  },
  center: [20, 15],
  r: 30,
  R: 50,
});

Some code of it in action

var React = require("react"),
  ReactArt = require("react-art"),
  Surface = ReactArt.Surface,
  Group = ReactArt.Group,
  Shape = ReactArt.Shape,
  Pie = require("paths-js/Pie");

var pie = Pie({
  data: [
    { name: "Italy", population: 59859996 },
    { name: "Mexico", population: 118395054 },
    { name: "France", population: 65806000 },
    { name: "Argentina", population: 40117096 },
    { name: "Japan", population: 127290000 },
  ],
  accessor: function (x) {
    return x.population;
  },
  compute: {
    color: function (i) {
      return "#000";
    },
  },
  center: [20, 15],
  r: 30,
  R: 50,
});

var Demo = React.createClass({
  getPie: function () {
    return pie.curves.map(function (shape) {
      return (
        <Group>
          <Text
            fill="#A6BD8A"
            font='bold 12px "Arial"'
            x={shape.sector.centroid[0] - 12}
            y={shape.sector.centroid[1]}
          >
            {shape.item.name}
          </Text>
          <Shape
            d={shape.sector.path.print()}
            stroke={shape.color}
            strokeWidth={1}
          />
        </Group>
      );
    });
  },
  render: function () {
    return (
      <div>
        <Surface width={500} height={500}>
          <Group x={50} y={45}>
            {this.getPie()}
          </Group>
        </Surface>
      </div>
    );
  },
});

module.exports = Demo;

Conclusion

These examples may look boring but they just show off a bit of the control you can have with react-art and a simple path generator. Not only that but because we aren't depending on the DOM in any case these examples should also work on react-native. Combined with some tweening you could make some very effective graphs that animate. That is a topic for another time.