Недопустимое значение 0 передано в checkMaxIfStatementsInShader при попытке использовать холст через перехватчик React useRef

Я создал простое приложение 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 в императивном (а не декларативном) стиле, и он, кажется, работает достаточно хорошо.

Я нашел (возможно, похожую) проблему на PixiJs GitHub: github.com/pixijs/pixijs/issues/4531

lua_python_java 01.05.2024 17:44

Похоже, это в некоторой степени связано с компонентом React.StrictMode, что обнажает проблему с app.destroy(true, true); в функции очистки useEffect хука в PixiGame. Закомментирование StrictMode в main.jsx или закомментирование app.destroy(true, true); в PixieGame, кажется, устраняет ошибку, но я почти уверен, что это не правильное решение. Судя по этой проблеме, касающейся повторного использования холста, она не является распространенной ошибкой и не имеет решения. Я думаю, придерживайтесь первого примера, который работает для вас.

Drew Reese 01.05.2024 17:45

Однако с существующим кодом и компонентом React.StrictMode это, скорее всего, проблема только в непроизводственных сборках, поэтому вам может потребоваться закомментировать любую из строк кода, которые я вызывал выше во время циклов разработки, чтобы приложение отображалось, и не забудьте убедиться в этом. чтобы раскомментировать и протестировать производственную сборку вашего приложения.

Drew Reese 01.05.2024 17:48
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
3
115
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня не было времени играть с ним, но это может помочь.

Описана функция App.destroy() для удаления элемента холста из DOM, поэтому, когда ваш useEffect запускает очистку, он уничтожает элемент холста из JSX.

в первом примере вы создаете новый элемент холста при каждом запуске useEffect, но во втором фрагменте кода он создается один раз в JSX -> удаляется при извлечении -> и не создается повторно, что приводит к ошибке.

Возможно, вы и правы, Влад. Я попробовал изменить вызов на app.destroy(true, { removeView: false });, чтобы сохранить элемент Canvas, но это пока не помогло...

Alexander Farber 11.05.2024 13:42

Другие вопросы по теме