Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/michalochman/react-pixi-fiber

Write PixiJS applications using React declarative style.
https://github.com/michalochman/react-pixi-fiber

pixi pixijs react reactjs renderer

Last synced: 4 days ago
JSON representation

Write PixiJS applications using React declarative style.

Lists

README

        


ReactPixiFiber


ReactPixiFiber – React Fiber renderer for PixiJS


ReactPixiFiber is a JavaScript library for writing PixiJS applications using React declarative style in React 16 and above.


For React <16.0.0 see react-pixi.


npm


License


CircleCI


codecov


styled with prettier


gitter

## Demo

See [Rotating Bunny](https://codesandbox.io/s/q7oj1p0jo6) demo.

Also, please explore our [CodeSandbox](https://codesandbox.io/) templates:
* [Hello world using JavaScript](https://codesandbox.io/s/react-pixi-fiber-template-ohk6z)
* [Hello world using TypeScript](https://codesandbox.io/s/react-pixi-fiber-typescript-template-613ly)

and examples:
* [Rotating Bunny](https://codesandbox.io/s/q7oj1p0jo6)
* [AnimatedSprite using CustomComponent](https://codesandbox.io/s/react-pixi-fiber-demo-animatedsprite-d6udu)
* [Aligning texts](https://codesandbox.io/s/react-pixi-fiber-text-alignment-th5eg)
* [Sharing Redux state](https://codesandbox.io/s/react-pixi-fiber-with-redux-g4k7n)
* [Using animated](https://codesandbox.io/s/9qyxrljyo)

## 🚀 Migrating from version `0.x.y`? 🚀

Read [migration guide](#migrating-from-react-pixi-fiber0xy-before-version-100).

## Installing

The current version assumes [React] >16.0.0 and [PixiJS] >4.4.0

yarn add react-pixi-fiber prop-types pixi.js

or

npm install react-pixi-fiber prop-types pixi.js --save

Refer to next sections to see usage examples.

This package works flawlessly with [Create React App](https://github.com/facebookincubator/create-react-app) – see examples below, they already use it.

## Usage


With ReactDOM (React 18 and above)

```jsx harmony
import { createRoot } from "react-dom/client";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";

function Bunny (props) {
return ;
}

const container = document.getElementById("container");
const root = createRoot(container);

root.render(


,
);
```

This example will render [`PIXI.Sprite`] object into a [Root Container] of [`PIXI.Application`] on the page.

The HTML-like syntax; [called JSX](https://reactjs.org/docs/introducing-jsx.html) is not required to use with this renderer, but it makes code more readable. You can use [Babel](https://babeljs.io/) with a [React preset](https://babeljs.io/docs/plugins/preset-react/) to convert JSX into native JavaScript.

---


With ReactDOM (React 16 and 17)

```jsx harmony
import { render } from "react-dom";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";

function Bunny(props) {
return ;
}

const container = document.getElementById("container");
render(


,
container
);
```

This example will render [`PIXI.Sprite`] object into a [Root Container] of [`PIXI.Application`] on the page.

The HTML-like syntax; [called JSX](https://reactjs.org/docs/introducing-jsx.html) is not required to use with this renderer, but it makes code more readable. You can use [Babel](https://babeljs.io/) with a [React preset](https://babeljs.io/docs/plugins/preset-react/) to convert JSX into native JavaScript.

---


Without ReactDOM

```jsx harmony
import { render, Text } from "react-pixi-fiber";
import * as PIXI from "pixi.js";

// Setup PixiJS Application
const canvasElement = document.getElementById("container")
const app = new PIXI.Application({
backgroundColor: 0x10bb99,
view: canvasElement,
width: 800,
height: 600,
});

render(
,
app.stage
);
```

This example will render [`PIXI.Text`] object into a [Root Container] of PIXI Application (created as `app`) inside the `` element on the page.

## Running Examples

1. Run `yarn install` (or `npm install`) in the repository root.
2. Run `yarn install` (or `npm install`) in the `examples` directory.
3. Run `yarn start` (or `npm run start`) in the `examples` directory.
4. Wait few seconds and browse examples that will open in new browser window.

## Migrating from `[email protected]` (before version `1.0.0`)


Changed built-in Stage and the one returned by createStageClass() to have the same API

It is now possible to get `ref` to built-in `Stage`.

Unless you are using class-based `Stage` component explicitly in your application, for example you are extending it, you should prefer to use built-in `Stage` instead of creating it with `createStageClass()`.

Data available on the `Stage` "instance":
* `_app` - PIXI.Application instance
* `_canvas` - HTMLCanvasElement instance
* `props` - props passed to Stage component

For example:
```js
import * as React from "react";
import { Stage, Text } from "react-pixi-fiber";

const width = 600;
const height = 400;
const options = {
backgroundColor: 0x56789a,
width: width,
height: height
};
const style = {
width: width,
height: height
};

function App() {
const stageRef = React.useRef()
React.useEffect(() => {
// Access PIXI.Application instance
console.log(stageRef.current?._app.current)
// Access HTMLCanvasElement instance
console.log(stageRef.current?._canvas.current)
// Access props passed to Stage component
console.log(stageRef.current?.props)
}, [])

return (



);
}
```

---


Changed PIXI.Application exposed by Stage to be React ref

This is only relevant if you were using `createStageClass()` to create `Stage` component, as it was impossible to get `ref` when using built-in `Stage` as if was a function component, which triggered `Warning: Function components cannot be given refs` error.

For example:
```diff
import * as React from "react";
import { createStageClass, Text } from "react-pixi-fiber";

const Stage = createStageClass()

const width = 600;
const height = 400;
const options = {
backgroundColor: 0x56789a,
width: width,
height: height
};
const style = {
width: width,
height: height
};

function App() {
const stageRef = React.useRef()
React.useEffect(() => {
- console.log(stageRef.current?._app.renderer)
+ console.log(stageRef.current?._app.current.renderer)
}, [])

return (



);
}
```

---


Changed oldProps in customApplyProps to not be initialised when the component is first rendered

Make sure to check if `oldProps` is initialised before trying to read properties from it.

For example:
```diff
import { Container, CustomPIXIComponent } from "react-pixi-fiber"

const TYPE = "CustomContainer"
const behavior = {
customApplyProps: function (instance, oldProps, newProps) {
- const { customProp: oldCustomProp, ...otherOldProps } = oldProps
+ const { customProp: oldCustomProp, ...otherOldProps } = oldProps ?? {}
const { customProp, ...otherNewProps } = newProps
if (customProp !== oldCustomProp) {
// Do something when customProp value have changed
}
this.applyDisplayObjectProps(otherOldProps, otherNewProps)
},
customDisplayObject: function ({ customProp, ...props }) {
const container = new PIXI.Container(props)
if (customProp === "foo") {
// Do something when customProp is equal to "foo"
}
return container
},
}

export default CustomPIXIComponent(behavior, TYPE)
```

---


Changed applyProps to applyDisplayObjectProps

`react-pixi-fiber` now needs to know the type of component (e.g. `"Sprite"`) to properly apply the props.

For example:
```diff
-import { applyProps } from "react-pixi-fiber"
+import { applyDisplayObjectProps } from "react-pixi-fiber"

function ApplyAnimatedValues(instance, props) {
if (instance instanceof PIXI.DisplayObject) {
- applyProps(instance, {}, props)
+ // Component has custom way of applying props - use that
+ if (typeof instance._customApplyProps === "function") {
+ instance._customApplyProps(instance, {}, props)
+ }
+ // Component doesn't have custom way of applying props - use default way
+ else {
+ const type = instance.constructor.name
+ applyDisplayObjectProps(type, instance, {}, props)
}
} else {
return false
}
}
```

Refer to the implementation, when in doubt:
* old `applyProps` -> https://github.com/michalochman/react-pixi-fiber/blob/64e8e9f991f51b407f3af108da732e186429454a/src/ReactPixiFiber.js#L43
* new `applyDisplayObjectProps` -> https://github.com/michalochman/react-pixi-fiber/blob/3a9b71b8d18180117bf70459dd6b4419c5ef1c21/src/ReactPixiFiberComponent.js#L161

---

## Migrating from `react-pixi`

It is possible to use React Pixi Fiber as a drop-in replacement for `react-pixi`.

There are two options:

Changing import or require statements

Change:

```js
import ReactPIXI from "react-pixi";
// or
const ReactPIXI = require("react-pixi");
```

to:

```js
import ReactPIXI from "react-pixi-fiber/react-pixi-alias";
// or
const ReactPIXI = require("react-pixi-fiber/react-pixi-alias");
```

---

Using webpack resolve alias

```js
resolve: {
alias: {
"react-pixi$": "react-pixi-fiber/react-pixi-alias"
}
}
```

---

## API

### Components

React Pixi Fiber currently supports following components out of the box (but read [Custom Components](#custom-components) section if you need more):

#### ``

Renders [Root Container] of any [`PIXI.Application`].

Expects **one** the following props:
* `app` - pass your own [`PIXI.Application`] instance,
* `options` - pass only the [`PIXI.Application`] options.

#### ``

Renders [`PIXI.Container`].

#### ``

Renders [`PIXI.Graphics`].

#### ``

Renders [`PIXI.ParticleContainer`] (or [`PIXI.particles.ParticleContainer`] if you're using PixiJS 4).

#### ``

Renders [`PIXI.Sprite`].

#### ``

Renders [`PIXI.TilingSprite`] (or [`PIXI.extras.TilingSprite`] if you're using PixiJS 4).

#### ``

Renders [`PIXI.Text`].

#### ``

Renders [`PIXI.BitmapText`] (or [`PIXI.extras.BitmapText`] if you're using PixiJS 4).

#### ``

Renders [`PIXI.NineSlicePlane`].

### Props

[Similarly](https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html) to ReactDOM in React 16,
ReactPixiFiber is not ignoring unknown [`PIXI.DisplayObject`] members – they are all passed through. You can read
more about [Unknown Prop Warning](https://reactjs.org/warnings/unknown-prop.html) in ReactDOM.

#### Custom Props / Plugins

In case you are using PixiJS plugins, such as [`pixi-layers`](https://github.com/pixijs/pixi-layers), ReactPixiFiber can
recognize these custom props by using the following `CustomPIXIProperty` API:

`CustomPIXIProperty(maybeComponentType, propertyName, validator)` accepts:
* `maybeComponentType` – a ReactPixiFiber component, an array of ReactPixiFiber components or `undefined`/`null`. Passing `undefined` or `null` will apply custom property to all ReactPixiFiber components.
* `propertyName` – a name of the custom property as string. ReactPixiFiber will also check that the casing is correct.
* `validator` – optional function that will be called with value provided and should return `true` if the value is valid, `false` otherwise.

For example:

```js
import { Container, Sprite } from "react-pixi-fiber";

const group = new PIXI.display.Group(0, true);

// if you just want to get rid of Unknown Prop Warning:
CustomPIXIProperty(Container, "parentGroup");
CustomPIXIProperty(undefined, "zIndex");

// if you want to be strict in the values that are provided
CustomPIXIProperty(Container, "parentGroup", value => value instanceof PIXI.display.Group);
CustomPIXIProperty([Container, Sprite], "zIndex", value => Number.isFinite(value));

function App() {
return (





{/* `parentgroup` below will trigger prop warning, as the letter casing is incorrect */}


{/* `zindex` below will trigger prop warning, as the letter casing is incorrect */}



)
}
```

#### Setting values for Point and ObservablePoint types

For setting properties on PixiJS types that are either [`PIXI.Point`]s or [`PIXI.ObservablePoint`]s you can use either
and array of integers or a comma-separated string of integers in the following forms: `[x,y]`, `"x,y"`, `[i]`, `"i"`.

In the case where two integers are provided, the first will be applied to the `x` coordinate and the second will be
applied to the `y` coordinate. In the case where a single integer if provided, it will be applied to both coordinates.

You can still create your own PIXI `Point` or `ObservablePoint` objects and assign them directly to the property.
These won't actually replace the property but they will be applied using the original object's `.copy()` method.

### Context – Accessing `PIXI.Application` instance created by ``

`PIXI.Application` is automatically provided using the following definition (either as a prop or in context):
* `app` – an instance of PixiJS Application, with properties like:
* `loader` – Loader instance to help with asset loading,
* `renderer` – WebGL or CanvasRenderer,
* `ticker` – Ticker for doing render updates,
* `view` – reference to the renderer's canvas element.


Using withApp Higher-Order Component (with all React versions)

To get `app` prop in your component you may wrap it with `withApp` higher-order component:

```jsx harmony
import { render } from "react-dom";
import { Sprite, Stage, withApp } from "react-pixi-fiber";
import bunny from "./bunny.png";

class RotatingBunny extends Component {
state = {
rotation: 0,
};

componentDidMount() {
// Note that `app` prop is coming through `withApp` HoC
this.props.app.ticker.add(this.animate);
}

componentWillUnmount() {
this.props.app.ticker.remove(this.animate);
}

animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};

render() {
return (

);
}
}
RotatingBunny.propTypes = {
app: PropTypes.object.isRequired,
};

const RotatingBunnyWithApp = withApp(RotatingBunny);

render(


,
document.getElementById("container")
);
```

---


Using New Context API directly (with React 16.3.0 and newer)

```jsx harmony
import { render } from "react-dom";
import { AppContext, Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";

class RotatingBunny extends Component {
state = {
rotation: 0,
};

componentDidMount() {
// Note that `app` prop is coming directly from AppContext.Consumer
this.props.app.ticker.add(this.animate);
}

componentWillUnmount() {
this.props.app.ticker.remove(this.animate);
}

animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};

render() {
return (

);
}
}
RotatingBunny.propTypes = {
app: PropTypes.object.isRequired,
};

render(


{app => (

)}

,
document.getElementById("container")
);
```

---


Using Legacy Context API directly (with React older than 16.3.0)

This approach is not recommended as it is easier to just use `withApp` HoC mentioned above.

```jsx harmony
import { render } from "react-dom";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";

class RotatingBunny extends Component {
state = {
rotation: 0,
};

componentDidMount() {
// Note that `app` is coming from context, NOT from props
this.context.app.ticker.add(this.animate);
}

componentWillUnmount() {
this.context.app.ticker.remove(this.animate);
}

animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};

render() {
return (

);
}
}
// Note that here we tell React to apply `app` via legacy Context API
RotatingBunny.childContextTypes = {
app: PropTypes.object,
};

render(


,
document.getElementById("container")
);
```

---

### Custom Components

ReactPixiFiber can recognize your custom components using API compatible with `react-pixi`.

`CustomPIXIComponent(behavior, type)` accepts a `behavior` object with the following 4 properties and a `type` string.

#### `customDisplayObject(props)`

Use this to create an instance of [PIXI.DisplayObject].

This is your entry point to custom components and the only required method. Can be also passed as `behavior` of type `function` to `CustomPIXIComponent`.

#### `customApplyProps(displayObject, oldProps, newProps)` (optional)

Use this to apply `newProps` to your `Component` in a custom way.

Note: this replaces the default method of transfering `props` to the specified `displayObject`. Call `this.applyDisplayObjectProps(oldProps,newProps)` inside your `customApplyProps` method if you want that.

#### `customDidAttach(displayObject)` (optional)

Use this to do something after `displayObject` is attached, which happens **after** `componentDidMount` lifecycle method.

#### `customWillDetach(displayObject)` (optional)

Use this to do something (usually cleanup) before detaching, which happens **before** `componentWillUnmount` lifecycle method.

#### Simple Graphics example

For example, this is how you could implement `Rectangle` component:
```javascript
// components/Rectangle.js
import { CustomPIXIComponent } from "react-pixi-fiber";
import * as PIXI from "pixi.js";

const TYPE = "Rectangle";
export const behavior = {
customDisplayObject: props => new PIXI.Graphics(),
customApplyProps: function(instance, oldProps, newProps) {
const { fill, x, y, width, height } = newProps;
instance.clear();
instance.beginFill(fill);
instance.drawRect(x, y, width, height);
instance.endFill();
}
};
export default CustomPIXIComponent(behavior, TYPE);
```

```jsx harmony
// App.js
import { render } from "react-pixi-fiber";
import * as PIXI from "pixi.js";
import Rectangle from "./components/Rectangle"

// Setup PixiJS Application
const canvasElement = document.getElementById("container")
const app = new PIXI.Application(800, 600, {
view: canvasElement
});

render(
,
app.stage
);
```

## Testing

## Caveats

## FAQ

### Is it production ready?

Yes and it's awesome! It is battle tested and backed up by [Kalamba Games](https://kalambagames.com/games/) since the conception in the beginning of 2018 (after [migrating from `react-pixi`](#migrating-from-react-pixi)) and now also used by other game studios.

### What version of PixiJS I can use?

PixiJS v4, v5 and v6 are supported.

### Can I use it in my TypeScript project?

Sure thing! We've got you covered.

### Can I use already existing [`PIXI.Application`]?

Yes, you can pass `app` property to `Stage` component, e.g. ``.

### Can I migrate from `react-pixi`?

Yes, it is easy, read [migration guide](#migrating-from-react-pixi).

### Is server-side rendering supported?

No, unfortunately it is not supported right now.

## Contributing

The main purpose of this repository is to be able to render PixiJS objects inside React 16 Fiber architecture.

Development of React Pixi Fiber happens in the open on GitHub, and I would be grateful to the community for any contributions, including bug reports and suggestions.

Read below to learn how you can take part in improving React Pixi Fiber.

### [Code of Conduct](https://github.com/michalochman/react-pixi-fiber/blob/master/CODE_OF_CONDUCT.md)
React Pixi Fiber has adopted a Contributor Covenant Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://github.com/michalochman/react-pixi-fiber/blob/master/CODE_OF_CONDUCT.md) so that you can understand what actions will and will not be tolerated.

### [Contributing Guide](https://github.com/michalochman/react-pixi-fiber/blob/master/CONTRIBUTING.md)

Read the contributing guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Pixi Fiber.

### Contact

You can help others and discuss in our [gitter channel](https://gitter.im/react-pixi-fiber/Lobby).

## License

ReactPixiFiber is [MIT licensed]((https://github.com/michalochman/react-pixi-fiber/blob/master/LICENSE)).

## Credits

### [`react-pixi`]

For making PIXI available in React for the first time.

### [React Fiber Architecture](https://github.com/acdlite/react-fiber-architecture)

For deeply explaining the concepts of Fiber architecture.

### [Building a custom React renderer](https://github.com/nitin42/Making-a-custom-React-renderer)

For helping me understand how to build an actual renderer.

### [React ART](https://github.com/facebook/react/tree/master/packages/react-art)

On which this renderer was initially based.

### [React] Contributors

For making an awesome project structure and documentation that is used in similar fashon here.

[PixiJS]: https://github.com/pixijs/pixi.js
[React]: https://github.com/facebook/react
[Root Container]: http://pixijs.download/release/docs/PIXI.Application.html#stage
[`PIXI.Application`]: http://pixijs.download/release/docs/PIXI.Application.html
[`PIXI.BitmapText`]: http://pixijs.download/release/docs/PIXI.BitmapText.html
[`PIXI.Container`]: http://pixijs.download/release/docs/PIXI.Container.html
[`PIXI.DisplayObject`]: http://pixijs.download/release/docs/PIXI.DisplayObject.html
[`PIXI.extras.BitmapText`]: https://pixijs.download/v4.8.8/docs/PIXI.extras.BitmapText.html
[`PIXI.extras.TilingSprite`]: https://pixijs.download/v4.8.8/docs/PIXI.extras.TilingSprite.html
[`PIXI.Graphics`]: http://pixijs.download/release/docs/PIXI.Graphics.html
[`PIXI.NineSlicePlane`]: http://pixijs.download/release/docs/PIXI.NineSlicePlane.html
[`PIXI.ObservablePoint`]: http://pixijs.download/release/docs/PIXI.ObservablePoint.html
[`PIXI.ParticleContainer`]: http://pixijs.download/release/docs/PIXI.ParticleContainer.html
[`PIXI.particles.ParticleContainer`]: https://pixijs.download/v4.8.8/docs/PIXI.particles.ParticleContainer.html
[`PIXI.Point`]: http://pixijs.download/release/docs/PIXI.Point.html
[`PIXI.Sprite`]: http://pixijs.download/release/docs/PIXI.Sprite.html
[`PIXI.Text`]: http://pixijs.download/release/docs/PIXI.Text.html
[`PIXI.TilingSprite`]: http://pixijs.download/release/docs/PIXI.TilingSprite.html
[`react-pixi`]: https://github.com/Izzimach/react-pixi