You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React组件中插入原生HTML元素?PixiJS+Next.js舞台优化

在Next.js中使用PixiJS创建高效的等比缩放舞台

解决React直接插入原生DOM元素的错误

你遇到的Objects are not valid as a React child错误,是因为React的JSX语法不支持直接渲染原生DOM元素对象(比如HTMLSpanElement)。如果要手动创建DOM元素,要么用React的createElementAPI,要么通过useRef获取容器后再挂载原生DOM。

但针对PixiJS的场景,我们不需要手动创建普通DOM元素,而是要把Pixi生成的canvas挂载到React组件里,正确的方式是用useRef和useEffect配合。


PixiJS + Next.js 高效实现等比缩放舞台方案

因为你要处理大量游戏实体,避免React重渲染带来的性能损耗,核心思路是让PixiJS完全接管渲染循环和实体管理,只通过React提供挂载容器,不把Pixi实体放到React状态中。

步骤1:安装依赖

首先安装PixiJS:

npm install pixi.js

步骤2:实现核心组件

下面是完整的Next.js组件示例,包含等比缩放逻辑:

import { useEffect, useRef } from 'react';
import { Application, Container, Sprite, Texture } from 'pixi.js';

export default function PixiGameStage() {
  // 用于挂载Pixi canvas的容器ref
  const containerRef = useRef(null);
  // 保存Pixi应用实例,避免重复初始化
  const appRef = useRef(null);
  // 舞台容器,用于统一缩放
  const stageRef = useRef(new Container());

  useEffect(() => {
    if (!containerRef.current || appRef.current) return;

    // 1. 初始化Pixi应用
    const app = new Application({
      width: window.innerWidth,
      height: window.innerHeight,
      backgroundColor: 0x1099bb,
      resolution: window.devicePixelRatio || 1,
      autoDensity: true,
    });
    containerRef.current.appendChild(app.view);
    appRef.current = app;

    // 将自定义舞台添加到Pixi应用的根容器
    app.stage.addChild(stageRef.current);

    // 2. 初始化等比缩放逻辑
    const targetAspectRatio = 16 / 9; // 你想要的目标宽高比
    function resizeStage() {
      const windowWidth = window.innerWidth;
      const windowHeight = window.innerHeight;
      const windowAspectRatio = windowWidth / windowHeight;

      let scale = 1;
      if (windowAspectRatio > targetAspectRatio) {
        // 窗口比目标宽,按高度缩放
        scale = windowHeight / (app.screen.height / targetAspectRatio);
      } else {
        // 窗口比目标高,按宽度缩放
        scale = windowWidth / app.screen.width;
      }

      // 应用缩放并居中舞台
      stageRef.current.scale.set(scale);
      stageRef.current.position.set(
        (windowWidth - app.screen.width * scale) / 2,
        (windowHeight - app.screen.height * scale) / 2
      );
    }

    // 初始执行一次缩放
    resizeStage();
    // 监听窗口 resize 事件
    window.addEventListener('resize', resizeStage);

    // 3. 添加游戏实体示例(直接操作Pixi对象,不经过React状态)
    const texture = Texture.from('https://pixijs.com/assets/bunny.png');
    const bunny = new Sprite(texture);
    bunny.anchor.set(0.5);
    bunny.x = app.screen.width / 2;
    bunny.y = app.screen.height / 2;
    stageRef.current.addChild(bunny);

    // 4. Pixi自己的渲染循环(处理实体运动、旋转等)
    app.ticker.add((delta) => {
      bunny.rotation += 0.01 * delta;
      // 这里可以直接更新所有游戏实体的状态,不会触发React重渲染
    });

    // 组件卸载时清理资源
    return () => {
      window.removeEventListener('resize', resizeStage);
      app.destroy(true);
      appRef.current = null;
    };
  }, []);

  return <div ref={containerRef} style={{ width: '100vw', height: '100vh', overflow: 'hidden' }} />;
}

关键说明

  • 避免React重渲染:所有Pixi实体的创建、更新、删除都直接通过Pixi的API操作,不存入React的useState或useReducer中,这样React组件不会因为实体变化而触发重渲染,完全由Pixi的ticker管理渲染循环。
  • 等比缩放实现:通过计算窗口宽高比和目标宽高比的差异,动态调整舞台的缩放比例,并将舞台居中,保证内容不会拉伸变形。
  • Next.js兼容:使用useEffect在客户端初始化Pixi(因为Pixi需要DOM环境,Next.js的SSR阶段无法运行),空的依赖数组确保只初始化一次。

批量管理大量实体的技巧

如果有成百上千个Isogenic实体,可以:

  • 创建一个Pixi容器专门存放所有实体,统一管理
  • 用对象池复用实体,减少频繁创建销毁的开销
  • 只在实体需要添加/移除时操作这个容器,更新实体状态直接修改Pixi对象的属性,全程不涉及React状态

内容的提问来源于stack exchange,提问作者Hydroper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:45:29