Open trezy opened 5 months ago
Thanks for your work on this @trezy!
I wonder how much the pixi/react API night change? I know the Pixi 8 API changes a bit, but I'm wondering if the next version of pixi/react is expected to have its own set of API changes on top of that?
@Nantris: Yeah, the new API for Pixi React will be subtly different from v7. For example, here's a basic application with v7 vs the same application with v8:
// Pixi React v7
import { Container, Sprite, Stage, Text } from '@pixi/react';
export const MyComponent = () => {
return (
<Stage options={{ background: 0xffffff }}>
<Sprite
anchor={{ x: 0.5, y: 0.5 }}
image="https://pixijs.io/pixi-react/img/bunny.png"
x={400}
y={270} />
<Container x={400} y={330}>
<Text
anchor={{ x: 0.5, y: 0.5 }}
text="Hello World" />
</Container>
</Stage>
);
};
// Pixi React v8
import { Container, Sprite, Text } from 'pixi.js'
import { Application, useAsset, useExtend } from '@pixi/react';
export const MyComponent = () => {
useExtend({ Container, Sprite, Text })
const texture = useAsset({ src: 'https://pixijs.com/assets/bunny.png' })
return (
<Application background={0xffffff} >
{texture && (
<sprite
anchor={{ x: 0.5, y: 0.5 }}
texture
x={400}
y={270} />
)}
<container x={400} y={330}>
<text
anchor={{ x: 0.5, y: 0.5 }}
text={'Hello World'} />
</container>
</Application>
);
};
A few important differences to notice:
useExtend
Because we don't import anything directly from Pixi React, we need to let Pixi React know which Pixi components are available. This allows developers to import only what they need from Pixi.js and avoid a bloated build. This hook just wraps the extend
function (also exported from Pixi React v8), which is available if you'd rather declare your extends
once at the root of your application.useAsset
This new hook wraps the Asset.add
/Asset.load
API, making it easier to load and cache assets within a Pixi React application. It also makes it possible to use <sprite>
components conditionally based on the loaded state of their texture, which is necessary since Pixi.Sprite doesn't like being created without a texture.<Application>
, not <Stage>
We wrap our components with <Application>
component, not a <Stage>
component. As with all JSX components in v8, <Application>
maps 1:1 with Pixi.Application
, so you can set all the same properties on it that you would when creating a new Pixi app outside of React (e.g. new Pixi.Application({ background: 0xffffff })
).sprite
component uses the texture
property, not the image
property. As with the <Application>
component, all properties are mapped 1:1 with their non-JSX counterparts. Changing these properties also works as you would expect with any other JSX component.Once released, this syntax will always work. However, I intend to add a couple of other features that will add even more utility to the library:
attach
API
Similar to react-three-fiber, you'll eventually be able to create your dependent properties as child nodes. For example, the following component would handle creating a texture and setting it on the parent <sprite>
:
<sprite anchor={{ x: 0.5, y: 0.5 }} x={400} y={270}>
<texture src={'https://pixijs.com/assets/bunny.png'} />
</sprite>
<text>
components with a child text node, rather than setting the text
property:
<text anchor={{ x: 0.5, y: 0.5 }}>
Hello World
</text>
Pixi.Application
, automatically creating the renderer, ticker, and a handful of other pieces that you don't need to think about. I'd like to make it easy to create your own custom Pixi applications, allowing developers to customise the application and manage the minutiae on their own.:tada: This issue has been resolved in version 8.0.0-alpha.1 :tada:
The release is available on GitHub release
Your semantic-release bot :package::rocket:
:tada: This issue has been resolved in version 8.0.0-beta.1 :tada:
The release is available on GitHub release
Your semantic-release bot :package::rocket:
Just wanted to drop in and say thank you for the hard work. I've been using the v8 beta and its such a huge improvement. Very excited for the release ❤️
+1 to the above. Very excited to see this drop!
This is exactly the feature I wanted. The points I found lacking in Pixi were documentation and React compatibility, and I had the same thought while using @react-three/fiber. I’m very excited and if I can contribute in any way, I would love to!
ref
sI sadly don't have time to debug further, but I wanted to report anyway: ref
s are seemingly broken?
Application
refconst Issue = () => {
const ref = useRef(new Application());
return <Application ref={ref} onInit={(app) => {
console.log(ref.current, app); // `app` is not the ref!
}}/>
}
ref.current
!= appInOnInitref
will not be used. In the code, it looks like the given ref is overwritten in createRoot
- createRoot
always creates a new Application
instance. It should use the given ref if available. <container />
, etc.) refconst Issue = () => {
const ref = useRef(new Container());
const [state, setState] = useState(1); // cause re-renders somehow, and then inspect actual UIDs (see below)
return <container ref={ref} />
}
-<container />
(and so far, every other element) re-creates a new instance of their respective Container
/ Sprite
etc. class
Note: Just to make this clear, I gave all of those components a fixed, pre-created ref that was unused, and they still re-created itself.
You can verify this by inspecting the uid
s of the Pixi containers in the Application
instance:
app.stage
should always be 2 IIRC.ref
I made had the correct UID, unchanged, but wasn't used.Screenshot of a small debug utility
These UIDs should be 2,3,4, etc.
Code to reproduce this small menu Note: Quick and dirty.
interface ContainerInfo {
constructor: string;
uid: Container["uid"];
label: Container["label"];
scale: { x: number; y: number };
position: { x: number; y: number };
eventMode: Container["eventMode"];
cursor: Container["cursor"];
interactiveChildren: Container["interactiveChildren"];
visible: Container["visible"];
alpha: Container["alpha"];
children: ContainerInfo[];
}
const buildContainerInfo = (container: Container): ContainerInfo => {
const children = container.children.map((child) => buildContainerInfo(child));
return {
constructor: container.constructor.name.startsWith("_")
? container.constructor.name.slice(1)
: container.constructor.name,
uid: container.uid,
label: container.label,
scale: { x: container.scale.x, y: container.scale.y },
position: { x: container.x, y: container.y },
eventMode: container.eventMode,
cursor: container.cursor,
interactiveChildren: container.interactiveChildren,
visible: container.visible,
alpha: container.alpha,
children,
};
};
const Debug = () => {
const app = useApp();
const [info, setInfo] = useState<ContainerInfo | null>(null);
useTick(() => {
debounce(() => {
setInfo(buildContainerInfo(app.stage));
}, 200)();
});
if (!info) return null;
return (
<>
<div>
Stage{" "}
<small>
{info.constructor} ({info.uid})
</small>
</div>
<div>
{info.children.map((child) => (
<div key={child.uid}>
{child.label}
<small>
{child.constructor} ({child.uid})
</small>
</div>
))}
</div>
</>
);
};
@nightgrey Please create a new issue with these details. Leaving it in the comments of another ticket will result in the issue being lost in the clutter.
@trezy Thank you so much for your work! Would you say this is stable enough to start making hobby projects with it already? I would really love the new v8 performance
@P3ntest Yup! I've been using it in hobby projects already. 😁
This looks super cool! Currently looking at this option as a replacement for our current setup with svg elements and react-spring. Any thoughts on how the new architecture would handle something like the react-spring library for elements that are reactive and dynamic, or if it would still be necessary in the first place? For example, react-three/fiber suggests that for rapidly changing values, you either use something like react-spring or their useFrame
hook. Thanks!
This looks super cool! Currently looking at this option as a replacement for our current setup with svg elements and react-spring. Any thoughts on how the new architecture would handle something like the react-spring library for elements that are reactive and dynamic, or if it would still be necessary in the first place? For example, react-three/fiber suggests that for rapidly changing values, you either use something like react-spring or their
useFrame
hook. Thanks!
For what it's worth, I've used both pixi-react v7 and v8 in small games with a lot of animations, once with only regular React state, and once with MobX, and it worked just fine. Contrary to popular opinion, React is perfectly capable of handling 60 fps, although you might have to keep an eye on performance and optimize rerenders sometimes.
amazing work; I personally have lots of small issues; some easy to work around, some less:
react contexts behave weirdly
some fishy business involving either or both Suspense and useApplication, causing un-necessary re-renders and slowing things a bit
some conflicts with three fiber
but it looks super promising ! thanks
Overview
This issue will track progress on version 8 of Pixi React. This new major version is a complete rewrite of the library and will support Pixi.js v8.
Thesis
The v7 codebase of Pixi React has served its purpose, but it has become burdensome to maintain. Especially with the release of Pixi.js v8 and the significant number of breaking changes it introduced, it makes more sense to rebuild this library from scratch than to continue supporting the legacy codebase.
For this complete rewrite, we'll take a new approach to the library's implementation by introducing a custom React Pixi pragma, an
extend
API, and reflecting all Pixi components as React components. This rewrite is heavily influenced by the prior art of@react-three/fiber
, and I've been receiving significant help from @CodyJasonBennett and @krispya.A new pragma
React allows custom reconcilers to tap into its re/rendering logic. React Pixi has been using a custom reconciler for some time, but the library provided custom components that used this reconciler to manage their own rendering lifecycle. These components need to be maintained, and their logic could require changes depending on how the core Pixi.js library changed.
With a new JSX pragma, we can eliminate the need for custom components. Instead, we reflect Pixi.js components directly into the pragma and proxy their JSX props back as Pixi.js component props. This allows us to expose all Pixi.js components now and in the future with no changes to React Pixi.
The
extend
APITo provide all components as a pragma would typically require us to import the entirety of Pixi.js into the Pixi React library, making tree shaking difficult and significantly increasing the build size for anybody trying to use Pixi.js v8. Instead, we're leveraging an
extend
API allowing users to import only the Pixi.js APIs they require, thus cutting down on bundle sizes.The API will be available as both a Vanilla
extend
method and auseExtend
React hook. An example of what this will look like:Exposing all of Pixi.js
Combining the new pragma and the
extend
API, we can use the entirety of Pixi.js via JSX. Any properties that you would normally set directly on a Pixi.js component will be managed via JSX props, while the components will be exposed directly through their refs. This enables lots of creative use cases, e.g. defining Pixi.jsFilter
s in JSX...More intuitive support down the road
The
BlurFilter
implementation above is a great example of something I've already got an eye towards improving. I'd like to create anattach
API similar to that of@react-three/fiber
, allowing non-directly-rendered components (like filters, textures, etc) to be automatically attached to their parent components. I'd also like to add support for creatingText
components with normal JSX text nodes.In short, I'd like to make the library as intuitive as possible. I'll add basic documentation for React Pixi, but the hope is that most APIs will be best served by the core Pixi.js documentation.
Development process
I've been using the
dev
branch as a sort of test bed for this update. Every update that's pushed to thedev
branch of the repo will be deployed to thedev
tag on npm. That said, these builds are not stable. Many of them will be completely broken until we're in a more stable place with the update (hopefully soon!). I do not recommend installing from thedev
tag. You have been warned.Once we're ready for user feedback, I'll post to the Official Pixi.js Discord. Make sure to join and enable notifications if you want to know when new versions are ready for testing. 😁