Skip to content

Instantly share code, notes, and snippets.

@jmfirth
Created April 7, 2017 19:27
Show Gist options
  • Save jmfirth/a7face42d237a0e226efc467c3c6cfc0 to your computer and use it in GitHub Desktop.
Save jmfirth/a7face42d237a0e226efc467c3c6cfc0 to your computer and use it in GitHub Desktop.
React and Pixi.js
import * as React from 'react';
import Stage from './Stage';
import DraggableSquare from './DraggableSquare';
interface State {
x: number;
y: number;
width: number;
height: number;
color: number;
}
export default class App extends React.Component<null, State> {
state: State = {
x: 10,
y: 10,
width: 100,
height: 100,
color: 0x00FF00,
};
rafHandle?: number;
square = new DraggableSquare({
onMouseOver: () => this.setState({ color: 0xFF0000 }),
onMouseOut: () => this.setState({ color: 0x00FF00 }),
onDrag: (x, y) => this.setState({ x, y }),
});
componentDidMount() {
this.update(0);
}
componentWillUnmount() {
if (this.rafHandle) {
cancelAnimationFrame(this.rafHandle);
}
}
update(tick: number) {
this.rafHandle = requestAnimationFrame(this.update.bind(this));
}
render() {
const { x, y, width, color } = this.state;
return (
<div className="App">
<h1>Drag me!</h1>
<Stage
width={400}
height={400}
displayObjects={[
this.square.draw(x, y, width, color)
]}
/>
</div>
);
}
}
{"react/index.d.ts":"https://unpkg.com/@types/react/index.d.ts","react-dom/index.d.ts":"https://unpkg.com/@types/react-dom/index.d.ts","pixi.js/index.d.ts":"https://unpkg.com/@types/pixi.js/index.d.ts"}
import * as PIXI from 'pixi.js';
interface Position {
x: number;
y: number;
}
type DraggableSquareDragFn = (x: number, y: number) => void;
interface DraggableSquareOptions {
onMouseOver?: () => void;
onMouseOut?: () => void;
onDrag?: DraggableSquareDragFn;
}
export default class DraggableSquare {
graphics = new PIXI.Graphics;
x = 0;
y = 0;
onDrag?: DraggableSquareDragFn;
initialShapePosition: Position | null = null;
initialClickPosition: Position | null = null;
constructor({ onMouseOver, onMouseOut, onDrag }: DraggableSquareOptions = {}) {
this.onDrag = onDrag;
this.graphics.interactive = true;
if (onMouseOver) { this.graphics.on('mouseover', onMouseOver); }
if (onMouseOut) { this.graphics.on('mouseout', onMouseOut); }
if (onDrag) {
this.graphics.on('pointerdown', this.handlePointerDown.bind(this));
this.graphics.on('pointerup', this.handlePointerUp.bind(this));
this.graphics.on('pointermove', this.handlePointerMove.bind(this));
}
}
handlePointerDown(e: PIXI.interaction.InteractionEvent) {
this.initialShapePosition = { x: this.x, y: this.y };
this.initialClickPosition = {
x: (e.data.originalEvent as PointerEvent).clientX,
y: (e.data.originalEvent as PointerEvent).clientY
};
}
handlePointerUp = () => { this.initialShapePosition = null; };
handlePointerMove(e: PIXI.interaction.InteractionEvent) {
if (this.onDrag && this.initialShapePosition && this.initialClickPosition) {
const currentClickPosition = {
x: (e.data.originalEvent as PointerEvent).clientX,
y: (e.data.originalEvent as PointerEvent).clientY
};
this.onDrag(
this.initialShapePosition.x + currentClickPosition.x - this.initialClickPosition.x,
this.initialShapePosition.y + currentClickPosition.y - this.initialClickPosition.y,
);
}
}
draw(x: number, y: number, size: number, color: number): PIXI.Graphics {
this.x = x;
this.y = y;
this.graphics.clear();
this.graphics.beginFill(color);
this.graphics.drawRect(x, y, size, size);
this.graphics.endFill();
return this.graphics;
}
}
<div id="root" />
import * as React from 'react';
import { render } from 'react-dom';
import App from './App';
render(<App />, document.getElementById('root'));
{"react":"15.4.2","react-dom":"15.4.2","pixi.js":"latest"}
import * as React from 'react';
import * as PIXI from 'pixi.js';
// import * as stageParser from './stageParser';
interface Props {
width?: number | string;
height?: number | string;
renderer?: PIXI.WebGLRenderer | PIXI.CanvasRenderer;
stage?: PIXI.Container;
onCreated?: (renderer: PIXI.WebGLRenderer | PIXI.CanvasRenderer, stage: PIXI.Container) => void;
displayObjects?: PIXI.DisplayObject[];
}
export default class Stage extends React.Component<Props, void> {
renderer: PIXI.WebGLRenderer | PIXI.CanvasRenderer;
stage: PIXI.Container;
container: HTMLDivElement;
componentDidMount() {
const { renderer, stage, onCreated } = this.props;
const { clientWidth: width, clientHeight: height } = this.container;
if (renderer) {
this.renderer = renderer;
} else {
this.renderer = PIXI.autoDetectRenderer(width, height);
this.renderer.clearBeforeRender = true;
this.renderer.autoResize = true;
}
this.container.appendChild(this.renderer.view);
this.stage = stage || new PIXI.Container();
if (onCreated) {
onCreated(this.renderer, this.stage);
}
this.update();
}
componentWillUnmount() {
this.renderer.destroy();
}
componentWillReceiveProps(next: Props) {
if (next === this.props) { return; }
if (next.width !== this.props.width || next.height !== this.props.height) {
this.resize();
}
this.update();
}
resize() {
const { clientWidth: width, clientHeight: height } = this.container;
this.renderer.resize(width, height);
}
clear() {
this.stage.children.forEach(child => this.stage.removeChild(child));
}
draw() {
const { displayObjects = [] } = this.props;
// this.clear();
displayObjects.map(displayObject => this.stage.addChild(displayObject));
}
update() {
this.draw();
this.renderer.render(this.stage);
}
render() {
const { width, height } = this.props;
return (
<div
style={{ width, height }}
ref={container => { this.container = container; }}
/>
);
}
}
@jmfirth
Copy link
Author

jmfirth commented Apr 7, 2017

View this in the TypeScript Playground.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment