PixiJS入门指导

1,504 阅读6分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

先前完成镜像计划时要选择一个2D Canvas框架,多方比对后,选择了基于WebGLPixiJS

介绍

官方网站对PixiJS介绍如下:

PixiJS 的核心是一个渲染系统,它使用 WebGL(或可选的 Canvas)来显示图像和其他 2D 视觉内容。

它提供了完整的场景图,并提供交互支持以启用处理点击和触摸事件。

它是现代 HTML5 世界中 Flash 的自然替代品,但提供了更好的性能和像素级效果,超出了 Flash 所能达到的范围。

它非常适合在线游戏、教育内容、交互式广告、数据可视化……任何需要复杂图形的基于 Web 的应用程序。再加上 Cordova 和 Electron 等技术,PixiJS 应用程序可以作为移动和桌面应用程序分布在浏览器之外。

总结以下,PixiJS的优势:

  1. 轻量,以渲染功能为核心。
  2. 移植性,轻易在前端项目中引用。
  3. 完整且最新的说明文档。

在本文章中,我们先来完成以下图片的内容。

image.png

安装

  • CDN引入全局对象PIXI
<script src="https://pixijs.download/release/pixi.js"></script>
  • NPM引入
npm install --save pixi.js
import * as PIXI from "pixi.js";

基础知识

APP

先在Html中添加对应的标签,用以存放画布内容

<div class="pixiDom" ref="pixiDom"></div>

接着创建全局app对象,附加到页面对应标签中。

let app = new PIXI.Application({ width: 640, height: 360 });

// 或 document.body.appendChild(app.view);
pixiDom.appendChild(app.view);

这里可以传入多个属性值,宽高为基本的属性,一些常用属性包括:

属性类型描述
widthnumber宽度
heightnumber高度
viewHTMLCanvasElementCanvas元素
autoDensityboolean根据css像素resize
backgroundColornumber(0x)背景色
backgroundAlphanumber透明度
powerPreferencestring"high-performance"设置高性能
...

下面介绍几类常用的Pixi对象,容器、图形、文字、精灵。

Container

Container表示容器,在Pixi中使用的最为频繁,可以理解为html中的div标签,容器(包括其它各种对象)有着它们的位置position,枢纽pivot等常用属性对象,且能够通过addChild来添加子对象,通过removeChild来删除子对象,类似DOM的增删。

let container = new PIXI.Container();
container.addChild(mask);

app.stage.addChild(container);
// app.stage.removeChild(container);

position && pivot

一个容器的位置position是相对于其父容器的(0,0)来进行相对于本容器枢纽的位置(0,0)来定位,我们常常需要做出动态的居中效果,有以下两种写法:

const setCenter = (son, parent) => {
  son.position.x = parent.width / 2 - son.width / 2;
  son.position.y = parent.height / 2 - son.height / 2;
};

这种方法类似于Html中的一种居中的策略,先移动父元素的50%,再反向根据本元素的宽度(高度)移动50%,如下:

.parent {
    position: relative;
    height: 200px;
    widthL: 200px;
}
.son {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
}

但是以上Pixi实现居中的方法,在实现旋转或缩放效果时,会需要设置旋转中心pivot,而旋转中心又会影响其位置,因此下面的方法也是常用的居中策略。

const setCenter = (son, parent) => {
  son.position.x = parent.width / 2;
  son.position.y = parent.height / 2;

  son.pivot.x = son.width / 2;
  son.pivot.y = son.height / 2;
};

在该居中策略中,实际上是枢纽pivot,或者说中心,距离父元素的左侧一半,和顶部一半。

Graphics

Graphics表示图形,可以借助它来绘制矩形Rect/圆形Circle/椭圆Ellipse/多边形Polygon。

经常借助Graphics来为容器绘制Container的矩形遮罩:

let mask = new PIXI.Graphics();

// Graphics.beginFill(color?: number, alpha?: number)
mask.beginFill(0xff387d);

// Graphics.lineStyle(width: number, color?: number, alpha?: number, alignment?: number, native?: boolean)
mask.lineStyle({ color: 0xffffff, width: 4, alignment: 0 });

// Graphics.drawPolygon(...path: number[] | PIXI.Point[]):
// Graphics.drawCircle(x: number, y: number, radius: number)
// Graphics.drawRect(x: number, y: number, width: number, height: number)
mask.drawRect(0, 0, 640, 700);

mask.endFill();

container.addChild(mask);

所有图形绘制前需要调用beginFill来确定填充颜色及透明度,在绘制结束后需要调用endFill来应用绘制。

在确定填充颜色后,可以指定画笔样式lineStyle来确定绘制的图形的边的样式,然后就可以调用drawRect等来绘制相应的图形了。

Text

Text即表示文字。文字具备其相应的CSS属性,如下:

let innerText = new PIXI.Text(text, {
    fontSize: 80,
    fill: 0xffffff,
    stroke: "#4a1850",
    strokeThickness: 3,
});
container.addChild(innerText);

TextStyle

文字的样式包含以下几部分:字体,外观,阴影,布局(在官方文档中还包含着**Utilities**实用,此处不提)

  • 字体包含着文字的大小,字体族等。

  • 外观确定文字的填充颜色fill,轮廓stroke等。

  • 阴影通过dropShadow定义,包括dropShadowColor,dropShadowBlur等。

  • 布局主要影响文字的位置及间距等,通过align确定对齐方式,wordWrapwordWrapWidth确定文字间隙等。

为了将文字样式保存,可以定义一个TextStyle,以做到在创建文本时,多次利用Style

// from Pixi examples
const style = new PIXI.TextStyle({
    fontFamily: 'Arial',
    fontSize: 36,
    fontStyle: 'italic',
    fontWeight: 'bold',
    fill: ['#ffffff', '#00ff99'], // gradient
    stroke: '#4a1850',
    strokeThickness: 5,
    dropShadow: true,
    dropShadowColor: '#000000',
    dropShadowBlur: 4,
    dropShadowAngle: Math.PI / 6,
    dropShadowDistance: 6,
    wordWrap: true,
    wordWrapWidth: 440,
    lineJoin: 'round',
});

const richText = new PIXI.Text('Rich text with a lot of options and across multiple lines', style);

Button

基于以上已学到的知识,我们来完成一个按钮“组件”,由于按钮本身可以设置颜色,所以我们以Graphics为父容器,在其中嵌入Text子元素,可传入TextStyle样式。

这里涉及到点击事件,通过查阅,我们需要设置button的两个属性,interactive可交互,buttonMode按钮模式,接下来就可以用监听的方式添加click事件,如下:

// let button = new PIXI.Graphics();
button.interactive = true;
button.buttonMode = true;
button.on("click", (event) => {
    clickHandler && clickHandler(event);
});

将以上因素结合,我们便可完成一个newButton组件,创建Graphics,增加点击事件,创建Text并添加到父元素中,最后将按钮

const newButton = (text, style, clickHandler) => {
  let button = new PIXI.Graphics();
  button.beginFill(0xffffff);   // 可传参定制
  button.drawRoundedRect(0, 0, 200, 50, 5);   // 可传参定制
  button.endFill();
  
  button.interactive = true;
  button.buttonMode = true;
  button.on("click", (event) => {
    clickHandler && clickHandler(event);
  });
  
  let innerText = new PIXI.Text(text, style);
  button.addChild(innerText);
  setCenter(innerText, button);

  return button;
};

Sprite

Sprite称精灵,在各种动画框架中均可见到它的身影,可以通过图片来创建。

let sprite = PIXI.Sprite.from('/imgs/E.png');

container.appendChild(sprite);

Ticker

这里的Ticker,个人理解为类似RequestAnimationFrame的逐帧响应函数,每一帧将会触发一次相应函数。

app.ticker.add((delta) => {
    sprite.rotation += 0.1;
});

根据以上便可实现Sprite的旋转效果。

雪碧图

通常为了实现动态效果,可以连续切换不同图片,Pixi支持雪碧图的方式。主要通过PIXI.extras.AnimatedSprite对象操作雪碧图,其传入的参数为纹理数组,通过PIXI.utils.TextureCache来切片图片,获取多个frame

推荐查阅这篇文章: 学习 PixiJS — 动画精灵 - 掘金 (juejin.cn)

总结

Pixi在使用上确实非常舒适,且很容易就嵌入到各种框架中。

在我的项目中使用Pixi绘制了三个页面,开始界面/介绍界面/主界面,均进行一定的封装,比如setCentersetRotatenewButton等,这些封装在小游戏开发的过程中意义非凡,关于上述内容的基本实践,可以查看我的项目,代码在/src/pixi/index.js文件中。 MrPluto0/arVision (github.com)

相关

PixiJS: Getting Started

PixiJS API Documentation

MrPluto0/arVision (github.com)

学习 PixiJS — 动画精灵 - 掘金 (juejin.cn)