Я создал простое приложение React, которое использует PixiJS и работает хорошо.
Соотношение сторон холста 1:1 поддерживается с помощью ResizeObserver независимо от ширины окна браузера, как вы можете видеть здесь:
Однако мне пришлось использовать обходной путь: я позволил PIXI.Application динамически создавать элемент холста, а затем назначал его React childRef.current, как показано ниже:
const PixiGame = () => {
const parentRef = useRef(null);
const childRef = useRef(null); // as a workaround I have to assign the canvas element below, created by PIXI app
useEffect(() => {
const parentElement = parentRef.current;
const app = new Application({
backgroundColor: 0xccffcc,
width: 800,
height: 800,
});
// the PIXI app has created a canvas element - make it a child of the parent div
const childElement = app.view;
childElement.id = "child";
childElement.classList.add("child");
childRef.current = childElement;
parentElement.appendChild(childElement);
Когда я пытаюсь пойти более естественным путем и объявить элемент холста в JSX аналогичного приложения, я получаю сообщение об ошибке:
Error: Invalid value of `0` passed to `checkMaxIfStatementsInShader`
at checkMaxIfStatementsInShader (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:5189:11)
at _BatchRenderer2.contextChange (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:8129:27)
at Runner.emit (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:5340:22)
at ContextSystem.initFromContext (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:8619:155)
at ContextSystem.initFromOptions (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:8631:10)
at ContextSystem.init (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:8603:168)
at Runner.emit (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:5340:22)
at StartupSystem.run (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:11443:27)
at new _Renderer2 (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:13203:478)
at autoDetectRenderer (https://masterdetailpixidynamic-hn0o--5173--41fbae16.local-corp.webcontainer.io/node_modules/.vite/deps/pixi__js-legacy.js?v=b911e8ad:12931:14)
Вот код, который я пытаюсь использовать, с элементом холста, определенным в JSX:
const PixiGame = () => {
const parentRef = useRef(null);
const childRef = useRef(null);
useEffect(() => {
const parentElement = parentRef.current;
const childElement = childRef.current; // here I just use the existing canvas element, defined in JSX below
const app = new Application({
backgroundColor: 0xccffcc,
width: 800,
height: 800,
view: childElement,
});
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
const minDimension = Math.floor(Math.min(width, height));
// maintain the 1:1 aspect ratio of the child element
childElement.style.width = `${minDimension}px`;
childElement.style.height = `${minDimension}px`;
}
});
resizeObserver.observe(parentElement);
const background = new Graphics();
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
if ((i + j) % 2 == 0) {
background.beginFill(0xccccff);
background.drawRect(i * 100, j * 100, 100, 100);
background.endFill();
}
}
}
app.stage.addChild(background);
const texture = Texture.from("https://pixijs.com/assets/bunny.png");
const bunny = new Sprite(texture);
bunny.anchor.set(0.5);
bunny.x = 50;
bunny.y = 50;
bunny.width = 100;
bunny.height = 100;
app.stage.addChild(bunny);
return () => {
resizeObserver.unobserve(parentElement);
resizeObserver.disconnect();
parentElement.removeChild(childElement);
app.destroy(true, true);
};
}, []);
return (
<div className = "verticalFlexContainer">
<div className = "hint">Game No. 1 Score1:Score2</div>
<div className = "parent" ref = {parentRef}>
<canvas className = "child" id = "child" ref = {childRef}></canvas>
</div>
<div className = "status">A game hint to do this and that...</div>
</div>
);
};
Похоже, ошибка каким-то образом связана с жизненным циклом React или, возможно, с его строгим режимом...
Я пытался напечатать childRef.current и parentRef.current, и они никогда не были нулевыми.
Есть ли у кого-нибудь предложение о том, как определить холст в JSX-части моего приложения и при этом иметь возможность использовать его в коде Pixi?
PS: Да, я попробовал CSS aspect-ratio: 1, и он мне не помог, поэтому я использую ResizeObserver.
PPS: Нет, я бы предпочел не использовать пакет «@pixi/react», потому что мне хотелось бы использовать PixiJS в императивном (а не декларативном) стиле, и он, кажется, работает достаточно хорошо.
Похоже, это в некоторой степени связано с компонентом React.StrictMode, что обнажает проблему с app.destroy(true, true); в функции очистки useEffect хука в PixiGame. Закомментирование StrictMode в main.jsx или закомментирование app.destroy(true, true); в PixieGame, кажется, устраняет ошибку, но я почти уверен, что это не правильное решение. Судя по этой проблеме, касающейся повторного использования холста, она не является распространенной ошибкой и не имеет решения. Я думаю, придерживайтесь первого примера, который работает для вас.
Однако с существующим кодом и компонентом React.StrictMode это, скорее всего, проблема только в непроизводственных сборках, поэтому вам может потребоваться закомментировать любую из строк кода, которые я вызывал выше во время циклов разработки, чтобы приложение отображалось, и не забудьте убедиться в этом. чтобы раскомментировать и протестировать производственную сборку вашего приложения.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


У меня не было времени играть с ним, но это может помочь.
Описана функция App.destroy() для удаления элемента холста из DOM, поэтому, когда ваш useEffect запускает очистку, он уничтожает элемент холста из JSX.
в первом примере вы создаете новый элемент холста при каждом запуске useEffect, но во втором фрагменте кода он создается один раз в JSX -> удаляется при извлечении -> и не создается повторно, что приводит к ошибке.
Возможно, вы и правы, Влад. Я попробовал изменить вызов на app.destroy(true, { removeView: false });, чтобы сохранить элемент Canvas, но это пока не помогло...
Я нашел (возможно, похожую) проблему на PixiJs GitHub: github.com/pixijs/pixijs/issues/4531