使用PixiJS和React创建一个画布的教程

2,064 阅读10分钟

曾几何时,在网络上创建丰富的交互式图形体验只能通过现已不复存在的Adobe Flash播放器插件来实现。但是,WebGL的出现改变了这个游戏。它带来了一个在网络上丰富的可视化时代。

WebGL是一个JavaScript渲染API,提供高性能的2D和3D互动图形能力和GPU加速。与其前身不同,该API可以在任何兼容的网络浏览器中工作,无需插件。然而,WebGL有一个更陡峭的学习曲线,所以有几个框架已经建立在该API之上,以减轻直接使用它的复杂性。

下面是一些基于WebGL的最流行的框架。

在这篇文章中,我们将回顾你需要知道的一切,以开始使用PixiJS,一个超级快速的WebGL框架,用于渲染2D图形,与React。本文的演示部分将包括几个PixiJS动画实例。

跳到前面。

前提条件

要继续学习本教程,你应该具备以下条件。

  • 关于React及其概念的基础知识
  • 对PixiJS(通常被称为PIXI)的基本了解

在React中实现PixiJS

PixiJS的设计是为了与HTML5一起工作,开箱即用。在React这样的库中实现它需要一个辅助库,如ReactPixi,它将促进PixiJS应用程序的集成和渲染。

ReactPixi是一个开源库,用于在React中渲染高性能的PixiJS应用程序。该库提供了有用的组件,使用React的声明式风格,使编写PixiJS应用程序更加容易和快速。

让我们通过一个例子来看看如何在React中实现和渲染一个PixiJS应用程序。

设置一个React项目

作为第一步,我们需要设置一个React项目样本并安装所需的软件包。

让我们在终端运行以下命令,用create-react-app引导一个React应用。

npx create-react-app pixi-react-app

create-react-app将在pixi-react-app 文件夹中安装最新版本的React。然而,在撰写本文时,ReactPixi并不支持React v18。因此,在我们的项目中使用该库之前,我们需要将React降级到17版。

要做到这一点,首先,打开项目根目录下的package.json 文件,替换以下几行代码。

"react": "^18.0.0"
"react-dom": "^18.0.0"

用这个片段。

"react": "^17.0.2"
"react-dom": "^17.0.2"

接下来,转到index.js 文件并替换以下代码。

import ReactDOM from 'react-dom/client'

用这个命令。

import ReactDOM from 'react-dom';

另外,替换下面的代码。

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

用这个片段。

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

最后,删除项目根目录下的node_modules 文件夹并运行。

npm install

运行上述命令后,你的React项目将被降级到v17。现在你可以继续安装PixiJS和ReactPixi。

npm install pixi.js @inlet/react-pixi

创建一个PixiJS画布

设置好项目后,清理一下模板代码和文件。接下来,打开App.js 文件,并从ReactPixi导入Stage 组件。

import Stage from "@inlet/react-pixi"

PIXI并不直接向DOM渲染对象。相反,它有一个渲染器,可以生成一个HTML5画布,作为浏览器中精灵和纹理等对象的显示区域。我们刚刚导入的Stage 组件包含了创建PIXI渲染器的基础代码。

该组件也是一个根容器对象,在PIXI中称为Stage 。这个Stage 对象是一个根容器,它容纳了我们希望PIXI显示给渲染器的所有对象。

你可以像这样把Stage 组件添加到App.js 组件中。

const App = () => {
  return(
  <Stage>
  </Stage>
)}

现在,通过在终端内运行npm start ,启动开发服务器。你应该在浏览器中看到一个长方形的画布元素,类似于下面的图片。

Black Rectangle

呈现的黑色方块是PIXI将显示对象的显示区域。

Stage 组件接受可用于配置画布的属性。例如,widthheight 道具的设置是为了确定画布的尺寸。

const App = () => {
  return(
  <Stage width={300} height={300}>
  </Stage>
)}

我们还可以使用options 道具来设置更多的属性,如抗锯齿(antialias),autoDensity ,和backgroundColor ,到Stage 组件。

const App = () => {
  return(
  <Stage 
  width={300} 
  height={300} 
  options={{ 
  backgroundColor: 0x012b30, 
  antialias: true 
  }}>

  </Stage>
)}

现在,让我们来看看我们如何将物体渲染到画布上。

渲染精灵

Sprites是PixiJS的构建块。这些特殊的图像可以用代码进行操作和控制。在属性的帮助下,精灵对于制作交互式和动画式的图形非常有用,这些属性可以让我们控制它们的位置、大小和一系列其他功能。

用ReactPixi渲染精灵是相当直接的。该库提供了一个Sprite 组件,可用于向浏览器加载和渲染图像。这个组件避免了在加载到渲染器之前将精灵图像转换为WebGL纹理的复杂问题。

要使用Sprite 组件,请像这样在App.js 文件中导入它。

import { Stage, Sprite} from '@inlet/react-pixi'

Sprite 组件提供了几个有用的属性,可以用来操作和控制精灵图像的外观和位置。一个例子是image 属性,它接受要渲染到画布上的图像的相对或绝对URL。

向画布加载精灵就像在Stage JSX标签内嵌套Sprite 组件一样简单,并将指向图像源的URL传递给image 道具。

...
import wizard from "./wizard.png";

const App = () => {
  return(
  <Stage ...>
      <Sprite image={wizard} />
  </Stage>
)} 

在你加载图像并保存你的项目后,你应该在浏览器中看到Sprite 图像在画布中呈现。

Sprite in Canvas

感谢LuizMelo将这个精灵免费提供给我们使用。

Sprite 组件的一些可选的道具包括widthheight 道具以及xy 道具。widthheight 属性用于操纵精灵的尺寸,而xy 属性则用于定位精灵。x 道具将精灵定位在X轴上,而y 道具将其定位在Y轴上。

<Sprite image={wizard} x={150} y={150}/>

渲染图形

我们可以用ReactPixi做更多事情,而不仅仅是向渲染器加载图像。该库提供了一个Graphics 组件,利用PIXI的低级绘图工具,使我们能够绘制不同种类的形状和线条,还可以向渲染器添加纹理。

让我们使用Graphics 组件来为渲染器绘制一个矩形图形。

首先,从ReactPixi导入Graphics 组件,并将其嵌套在Stage 标签中。

import { Stage, Sprite, Graphics} from '@inlet/react-pixi'

接下来,用下面的代码创建一个函数,并把它作为一个值传到Graphics draw 这个道具里面。

const App = () => {

    const draw = g => {
      g.beginFill(0x0033cc, 1) 
      g.drawRect(250, 150, 150, 120)
      g.endFill()
    };

  return(
  <Stage ...>
      <Sprite image={wizard} />
      <Graphics draw={draw}/>
  </Stage>
)} 

在draw函数里面,我们将用beginFill 方法设置形状的填充颜色,然后用drawRect 方法绘制形状。drawRect'的参数分别是xyheight ,和width 属性。

在你保存项目后,一个蓝色的方框应该出现在我们之前加载的画布内的Sprite 图片旁边。

Sprite Blue Rectangle

我们可以通过在draw 函数内添加一个lineStyle 方法并赋予它以下属性来给这个形状一个轮廓。

  const draw = g => {
    ...
    g.lineStyle(4,0xff0000,1) 
    g.endFill()
  };

Blue Rectangle Red Outline

draw 道具是一个回调函数,每次其属性发生变化时都会被调用。所以一定要通过在React的useCallback 钩子里面声明draw 函数来记忆它。

  const draw = useCallback(g => {
    g.beginFill(0x0033cc, 1)
    g.lineStyle(4,0xff0000,1) 
    g.drawRect(250, 150, 150, 120)
    g.endFill()
  },[]);

渲染文本

ReactPixi的Text 组件利用PIXI的Text 对象来向渲染器显示文本。该组件有两个主要道具。text一个是接受要显示的文本,另一个是style ,它接受一个定义文本属性的对象。

<Text
    text="Hello World"
    style={
      new TextStyle({
        align: 'center',
        fill: ['#ffffff', '#00ff99'], // gradient
        stroke: '#01d27e',
        strokeThickness: 5,
        letterSpacing: 20,
  />        

要在你的项目中使用Text 组件,首先从ReactPixi导入它。

import { Stage, Sprite, Graphics, Text } from '@inlet/react-pixi';

接下来,把它添加到Stage ,并传递给它一些textStyle 的道具。

const textStyle = new TextStyle({
  align: "center",
  fontWeight: "bold",
  fill: ["#26f7a3", "#01d27e"],
  stroke: "#eef1f5",
  strokeThickness: 1,
  letterSpacing: 5,
  wordWrap: false,
  wordWrapWidth: 350
})

const App = () => (
  <Stage ...>
     ….
     <Text text="Hello Logrocketer" style={textStyle}/>
     ….
  </Stage>
);

保存项目后,我们看到文本 "Hello Logrocketer "被渲染到画布上。

Hello Logrocketer Text

添加过滤器

ReactPixi提供了一个withFilters 函数,简化了向渲染器中的对象添加过滤器的过程。该函数使用PIXI的全局对象来访问过滤器对象及其属性。

为了给我们的精灵添加过滤器,我们必须首先从PixiJS中导入withFilters 函数和PIXI 全局对象,像这样。

import { Stage, Sprite, Graphics, Text, withFilter } from '@inlet/react-pixi';
import * as PIXI from 'pixi.js';

withFilters 函数接受两个参数,一个容器组件和一个对象配置。

const Filters = withFilters(Container, {
  blur: PIXI.filters.BlurFilter
});

Container 参数给了withFilters 函数一个容器对象的特性。

如前所述,容器是一个对象,用于将其他要在渲染器中显示的对象分组。我们在Container 上设置的过滤器将被应用于嵌套在容器中的对象。

第二个参数是过滤器配置,包含过滤器的偏好。这就是我们从PIXI 全局对象中访问filter 对象,并设置我们想要应用于容器的过滤器的种类。在本例中,我们使用BlurFilter 对象的属性来应用一个blur 过滤器。

不要忘记像这样从ReactPixi导入Container 组件。

import { Stage, Sprite, Graphics, Text, withFilter, Container } from '@inlet/react-pixi';

现在我们已经配置了一个过滤器容器,让我们把它应用到我们的Sprite 。将Filters 容器添加到Stage ,并将Sprite 组件嵌套其中。然后,将一个blur 的道具传递给容器,并赋予它以下属性。

const App = () => (
  <Stage>
     ….
    <Filters blur={{ blur: 5 }} >
      <Sprite image={wizard} .../>
    </Filters>
     ….
  </Stage>
);

这将在渲染器中为精灵添加一个模糊的过滤效果。你可以通过改变blur 这个道具的值来调整模糊效果。

Blurred Canvas

动画精灵

在PIXI中,需要一个循环器,也被称为游戏循环器,以使物体产生动画。gameLoop 函数每秒钟被调用60次,嵌套在该函数内的每段代码也被同样频繁地调用。

例如,下面的代码将使巫师精灵以每帧1像素的速度向左移动。

function setup() {
  app.ticker.add((delta) => gameLoop(delta));
}
function gameLoop(delta) {
  wizard.x -= 1;
}

循环函数是使用PIXI的ticker事件创建的;它相当于JavaScript的requestAnimationFrame 方法。ReactPixi提供了一个useTick Hook,它将一个监听器绑定到ticker事件上。通过它,我们可以对Sprite 的位置和状态进行动画处理。

在我们开始制作动画之前,我们首先要从ReactPixi和React中分别导入useTickuseState Hooks。

import { useTick, ... } from '@inlet/react-pixi';
import {..., useState} from 'react'

接下来,让我们为向导Sprite 创建一个新的组件,并将其嵌套在App.js 组件的容器中。

...
const Wizard = () =>{
  return(
    <Sprite
      image={wizard}
      x={x}
      y={y}
      anchor={0.5}
      scale={1.3}
    />
  )
}

const App = () => (
  <Stage ...>
    <Wizard />
     ….
  </Stage>
);

现在。在Sprite 组件的函数内添加以下代码。

import { Stage, Container, Sprite, Text, Graphics, withFilters,useTick } from '@inlet/react-pixi'
import { useState, useCallback} from "react";
import * as PIXI from "pixi.js"
import wizard from "./wizard.png";

let i = 0;

const Wizard = () =>{
  const [x, setX] = useState(0)
  const [y, setY] = useState(0)

  useTick(delta =>{
    i += 0.05 * delta;
    setX(Math.sin(i) * 100)
    setY(Math.sin(i/1.5) * 100)
   });

  return(
    <Sprite
      image={wizard}
      x={x}
      y={y}
      anchor={0.5}
      scale={1.3}
    />
  )
}
const App = () => (
  <Stage ...>
    <Wizard />
     ….
  </Stage>
);

在上面的代码块中,我们声明一个变量,i ,并给它赋值为0 。接下来,我们使用useState Hook创建两个状态变量,xy ,其初始值为0 。然后,我们调用useTick Hook并传递给它一个回调函数。

在回调函数中,我们给i 变量分配一个delta 的值,并将该变量的值乘以100的正弦结果设置为xy 状态变量。

最后,我们将xy 状态变量的值分别传递给Sprite'的xy 道具。

保存代码后,你的精灵的动画效果应该类似于下面显示的向导。

Sprite Dragged Around

结语

在本文中,我们演示了如何创建一个PIXI画布,将精灵、文本和图形对象加载到渲染器中,为对象添加过滤器,以及为对象制作动画。

本文的目的是让你了解如何在React中实现PixiJS应用程序。