refactor: @pixi/layers
This commit is contained in:
parent
16871b0919
commit
578e796090
|
@ -1,6 +1,3 @@
|
||||||
import {
|
|
||||||
Stage as PixiStage,
|
|
||||||
} from '@pixi/react';
|
|
||||||
import {SCALE_MODES} from '@pixi/constants';
|
import {SCALE_MODES} from '@pixi/constants';
|
||||||
import {BaseTexture} from '@pixi/core';
|
import {BaseTexture} from '@pixi/core';
|
||||||
import {createElement, useContext} from 'react';
|
import {createElement, useContext} from 'react';
|
||||||
|
@ -15,6 +12,7 @@ import RadiansContext from '@/context/radians.js';
|
||||||
|
|
||||||
import Ecs from './ecs.jsx';
|
import Ecs from './ecs.jsx';
|
||||||
import styles from './pixi.module.css';
|
import styles from './pixi.module.css';
|
||||||
|
import PixiStage from './stage.jsx';
|
||||||
|
|
||||||
BaseTexture.defaultOptions.scaleMode = SCALE_MODES.NEAREST;
|
BaseTexture.defaultOptions.scaleMode = SCALE_MODES.NEAREST;
|
||||||
|
|
||||||
|
|
339
app/react-components/pixi/stage.jsx
Normal file
339
app/react-components/pixi/stage.jsx
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
import { Application } from '@pixi/app';
|
||||||
|
import { Stage as LayerStage } from '@pixi/layers';
|
||||||
|
import { Ticker } from '@pixi/ticker';
|
||||||
|
import { AppProvider, PixiFiber } from '@pixi/react';
|
||||||
|
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const PROPS_DISPLAY_OBJECT = {
|
||||||
|
alpha: 1,
|
||||||
|
buttonMode: false,
|
||||||
|
cacheAsBitmap: false,
|
||||||
|
cursor: null,
|
||||||
|
filterArea: null,
|
||||||
|
filters: null,
|
||||||
|
hitArea: null,
|
||||||
|
interactive: false,
|
||||||
|
mask: null,
|
||||||
|
pivot: 0,
|
||||||
|
position: 0,
|
||||||
|
renderable: true,
|
||||||
|
rotation: 0,
|
||||||
|
scale: 1,
|
||||||
|
skew: 0,
|
||||||
|
transform: null,
|
||||||
|
visible: true,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -------------------------------------------
|
||||||
|
* Stage React Component (use this in react-dom)
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
*
|
||||||
|
* const App = () => (
|
||||||
|
* <Stage
|
||||||
|
* width={500}
|
||||||
|
* height={500}
|
||||||
|
* options={ backgroundColor: 0xff0000 }
|
||||||
|
* onMount={( renderer, canvas ) => {
|
||||||
|
* console.log('PIXI renderer: ', renderer)
|
||||||
|
* console.log('Canvas element: ', canvas)
|
||||||
|
* }}>
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* -------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
// dimensions
|
||||||
|
width: PropTypes.number,
|
||||||
|
height: PropTypes.number,
|
||||||
|
|
||||||
|
// will return renderer
|
||||||
|
onMount: PropTypes.func,
|
||||||
|
onUnmount: PropTypes.func,
|
||||||
|
|
||||||
|
// run ticker at start?
|
||||||
|
raf: PropTypes.bool,
|
||||||
|
|
||||||
|
// render component on component lifecycle changes?
|
||||||
|
renderOnComponentChange: PropTypes.bool,
|
||||||
|
|
||||||
|
children: PropTypes.node,
|
||||||
|
|
||||||
|
// PIXI options, see https://pixijs.download/v7.x/docs/PIXI.Application.html
|
||||||
|
options: PropTypes.shape({
|
||||||
|
autoStart: PropTypes.bool,
|
||||||
|
width: PropTypes.number,
|
||||||
|
height: PropTypes.number,
|
||||||
|
useContextAlpha: PropTypes.bool,
|
||||||
|
backgroundAlpha: PropTypes.number,
|
||||||
|
autoDensity: PropTypes.bool,
|
||||||
|
antialias: PropTypes.bool,
|
||||||
|
preserveDrawingBuffer: PropTypes.bool,
|
||||||
|
resolution: PropTypes.number,
|
||||||
|
forceCanvas: PropTypes.bool,
|
||||||
|
backgroundColor: PropTypes.number,
|
||||||
|
clearBeforeRender: PropTypes.bool,
|
||||||
|
powerPreference: PropTypes.string,
|
||||||
|
sharedTicker: PropTypes.bool,
|
||||||
|
sharedLoader: PropTypes.bool,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
onMount: noop,
|
||||||
|
onUnmount: noop,
|
||||||
|
raf: true,
|
||||||
|
renderOnComponentChange: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getCanvasProps(props)
|
||||||
|
{
|
||||||
|
const reserved = [
|
||||||
|
...Object.keys(propTypes),
|
||||||
|
...Object.keys(PROPS_DISPLAY_OBJECT),
|
||||||
|
];
|
||||||
|
|
||||||
|
return Object.keys(props)
|
||||||
|
.filter((p) => !reserved.includes(p))
|
||||||
|
.reduce((all, prop) => ({ ...all, [prop]: props[prop] }), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stage extends React.Component
|
||||||
|
{
|
||||||
|
_canvas = null;
|
||||||
|
_mediaQuery = null;
|
||||||
|
_ticker = null;
|
||||||
|
_needsUpdate = true;
|
||||||
|
app = null;
|
||||||
|
|
||||||
|
componentDidMount()
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
onMount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
options,
|
||||||
|
raf,
|
||||||
|
renderOnComponentChange,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
this.app = new Application({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
view: this._canvas,
|
||||||
|
...options,
|
||||||
|
autoDensity: options?.autoDensity !== false,
|
||||||
|
});
|
||||||
|
this.app.stage = new LayerStage();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development')
|
||||||
|
{
|
||||||
|
// workaround for React 18 Strict Mode unmount causing
|
||||||
|
// webgl canvas context to be lost
|
||||||
|
if (this.app.renderer.context?.extensions)
|
||||||
|
{
|
||||||
|
this.app.renderer.context.extensions.loseContext = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.ticker.autoStart = false;
|
||||||
|
this.app.ticker[raf ? 'start' : 'stop']();
|
||||||
|
|
||||||
|
this.app.stage.__reactpixi = { root: this.app.stage };
|
||||||
|
this.mountNode = PixiFiber.createContainer(this.app.stage);
|
||||||
|
PixiFiber.updateContainer(this.getChildren(), this.mountNode, this);
|
||||||
|
|
||||||
|
onMount(this.app);
|
||||||
|
|
||||||
|
// update size on media query resolution change?
|
||||||
|
// only if autoDensity = true
|
||||||
|
if (
|
||||||
|
options?.autoDensity
|
||||||
|
&& window.matchMedia
|
||||||
|
&& options?.resolution === undefined
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this._mediaQuery = window.matchMedia(
|
||||||
|
`(-webkit-min-device-pixel-ratio: 1.3), (min-resolution: 120dpi)`
|
||||||
|
);
|
||||||
|
this._mediaQuery.addListener(this.updateSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for reconciler changes
|
||||||
|
if (renderOnComponentChange && !raf)
|
||||||
|
{
|
||||||
|
this._ticker = new Ticker();
|
||||||
|
this._ticker.autoStart = true;
|
||||||
|
this._ticker.add(this.renderStage);
|
||||||
|
this.app.stage.on(
|
||||||
|
'__REACT_PIXI_REQUEST_RENDER__',
|
||||||
|
this.needsRenderUpdate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSize();
|
||||||
|
this.renderStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps)
|
||||||
|
{
|
||||||
|
const { width, height, raf, renderOnComponentChange, options }
|
||||||
|
= this.props;
|
||||||
|
|
||||||
|
// update resolution
|
||||||
|
if (
|
||||||
|
options?.resolution !== undefined
|
||||||
|
&& prevProps?.options.resolution !== options?.resolution
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.app.renderer.resolution = options.resolution;
|
||||||
|
this.resetInteractionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update size
|
||||||
|
if (
|
||||||
|
prevProps.height !== height
|
||||||
|
|| prevProps.width !== width
|
||||||
|
|| prevProps.options?.resolution !== options?.resolution
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle raf change
|
||||||
|
if (prevProps.raf !== raf)
|
||||||
|
{
|
||||||
|
this.app.ticker[raf ? 'start' : 'stop']();
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush fiber
|
||||||
|
PixiFiber.updateContainer(this.getChildren(), this.mountNode, this);
|
||||||
|
|
||||||
|
if (
|
||||||
|
prevProps.width !== width
|
||||||
|
|| prevProps.height !== height
|
||||||
|
|| prevProps.raf !== raf
|
||||||
|
|| prevProps.renderOnComponentChange !== renderOnComponentChange
|
||||||
|
|| prevProps.options !== options
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this._needsUpdate = true;
|
||||||
|
this.renderStage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSize = () =>
|
||||||
|
{
|
||||||
|
const { width, height, options } = this.props;
|
||||||
|
|
||||||
|
if (!options?.resolution)
|
||||||
|
{
|
||||||
|
this.app.renderer.resolution = window.devicePixelRatio;
|
||||||
|
this.resetInteractionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.renderer.resize(width, height);
|
||||||
|
};
|
||||||
|
|
||||||
|
needsRenderUpdate = () =>
|
||||||
|
{
|
||||||
|
this._needsUpdate = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
renderStage = () =>
|
||||||
|
{
|
||||||
|
const { renderOnComponentChange, raf } = this.props;
|
||||||
|
|
||||||
|
if (!raf && renderOnComponentChange && this._needsUpdate)
|
||||||
|
{
|
||||||
|
this._needsUpdate = false;
|
||||||
|
this.app.renderer.render(this.app.stage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// provide support for Pixi v6 still
|
||||||
|
resetInteractionManager()
|
||||||
|
{
|
||||||
|
// `interaction` property is absent in Pixi v7 and in v6 if user has installed Federated Events API plugin.
|
||||||
|
// https://api.pixijs.io/@pixi/events.html
|
||||||
|
// in v7 however, there's a stub object which displays a deprecation warning, so also check the resolution property:
|
||||||
|
const { interaction: maybeInteraction } = this.app.renderer.plugins;
|
||||||
|
|
||||||
|
if (maybeInteraction?.resolution)
|
||||||
|
{
|
||||||
|
maybeInteraction.resolution = this.app.renderer.resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren()
|
||||||
|
{
|
||||||
|
const { children } = this.props;
|
||||||
|
|
||||||
|
return <AppProvider value={this.app}>{children}</AppProvider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, errorInfo)
|
||||||
|
{
|
||||||
|
console.error(`Error occurred in \`Stage\`.`);
|
||||||
|
console.error(error);
|
||||||
|
console.error(errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount()
|
||||||
|
{
|
||||||
|
this.props.onUnmount(this.app);
|
||||||
|
|
||||||
|
if (this._ticker)
|
||||||
|
{
|
||||||
|
this._ticker.remove(this.renderStage);
|
||||||
|
this._ticker.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.stage.off(
|
||||||
|
'__REACT_PIXI_REQUEST_RENDER__',
|
||||||
|
this.needsRenderUpdate
|
||||||
|
);
|
||||||
|
|
||||||
|
PixiFiber.updateContainer(null, this.mountNode, this);
|
||||||
|
|
||||||
|
if (this._mediaQuery)
|
||||||
|
{
|
||||||
|
this._mediaQuery.removeListener(this.updateSize);
|
||||||
|
this._mediaQuery = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
const { options } = this.props;
|
||||||
|
|
||||||
|
if (options && options.view)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<canvas
|
||||||
|
{...getCanvasProps(this.props)}
|
||||||
|
ref={(c) => (this._canvas = c)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stage.propTypes = propTypes;
|
||||||
|
Stage.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
export default Stage;
|
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"@pixi/filter-adjustment": "^5.1.1",
|
"@pixi/filter-adjustment": "^5.1.1",
|
||||||
"@pixi/filter-color-matrix": "^7.4.2",
|
"@pixi/filter-color-matrix": "^7.4.2",
|
||||||
"@pixi/filter-glow": "^5.2.1",
|
"@pixi/filter-glow": "^5.2.1",
|
||||||
|
"@pixi/layers": "^2.1.0",
|
||||||
"@pixi/particle-emitter": "^5.0.8",
|
"@pixi/particle-emitter": "^5.0.8",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
"@pixi/spritesheet": "^7.4.2",
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
@ -3554,6 +3555,15 @@
|
||||||
"@pixi/core": "7.4.2"
|
"@pixi/core": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/canvas-renderer": {
|
||||||
|
"version": "7.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/canvas-renderer/-/canvas-renderer-7.4.2.tgz",
|
||||||
|
"integrity": "sha512-5qBrr4hJ3hQYMJwxIkKCnvMxL9m7aL26h4zbacK0KH7SQ0i+RCcZS2NzvPa451FtnhquIUjwuuWQyjPFdM5R7g==",
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@pixi/core": "7.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pixi/color": {
|
"node_modules/@pixi/color": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.2.tgz",
|
||||||
|
@ -3704,6 +3714,16 @@
|
||||||
"@pixi/sprite": "7.4.2"
|
"@pixi/sprite": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/layers": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/layers/-/layers-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-e3p3xaHSkVF1jLTfiTEsFGNzGRPffTO0UkKbwA9pfBzRHIBOFM2mCbVqcdhgHSMXRFYr2FoOp1+zK9jJifEUug==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@pixi/canvas-renderer": "^7.0.0",
|
||||||
|
"@pixi/core": "^7.0.0",
|
||||||
|
"@pixi/display": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pixi/math": {
|
"node_modules/@pixi/math": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.2.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@pixi/filter-adjustment": "^5.1.1",
|
"@pixi/filter-adjustment": "^5.1.1",
|
||||||
"@pixi/filter-color-matrix": "^7.4.2",
|
"@pixi/filter-color-matrix": "^7.4.2",
|
||||||
"@pixi/filter-glow": "^5.2.1",
|
"@pixi/filter-glow": "^5.2.1",
|
||||||
|
"@pixi/layers": "^2.1.0",
|
||||||
"@pixi/particle-emitter": "^5.0.8",
|
"@pixi/particle-emitter": "^5.0.8",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
"@pixi/spritesheet": "^7.4.2",
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user