PixiJs初体验,教你快速上手这款2D渲染引擎

2,721 阅读5分钟

API文档 官网

介绍

Pixi.js是一个用JavaScript写的2D渲染引擎,可以用来在浏览器里做交互图形、动画和游戏等应用,主打支持硬件GPU渲染的WebGL API,如浏览器不支持WebGL,Pixi则会退用HTML5 Canvas来渲染。

本篇主要说一些基本的使用,由于官方文档是v4.5.5版本的,且部分example已过时,故使用最新版v6.0.4且兼容v5.3.10写了个demodemo源码地址

PixiJsDemo4.gif

使用

使用import方式引入时可以这么引入

import * as PIXI from 'pixi.js'

可以把一些常用的API设置别名方便调用

let Application = PIXI.Application,
    Container = PIXI.Container,
    TextureCache = PIXI.utils.TextureCache,
    Sprite = PIXI.Sprite,
    Rectangle = PIXI.Rectangle,
    Graphics = PIXI.Graphics;

大致流程为:

  1. 创建一个画布
  2. 将画布添加到对应的dom内部
  3. 创建一个显示对象,比如文本,多边形,精灵图之类
  4. 将显示对象加入到舞台中,舞台大小默认根据内部元素大小而定

一个示例

let app = new PIXI.Application({width: 800, height: 800, transparent: true});

//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);

// 创建一个文本的style
let style = new PIXI.TextStyle({
    fontFamily: "Arial",
    fontSize: 36,
    fill: "white",
    stroke: '#ff3300',
    strokeThickness: 4,
    dropShadow: true,
    dropShadowColor: "#000000",
    dropShadowBlur: 4,
    dropShadowAngle: Math.PI / 6,
    dropShadowDistance: 6,
  });
// 创建一个文本显示对象
let message = new PIXI.Text("Hello Pixi!", style);

message.zIndex = 2

// 创建一个矩形显示对象
let rectangle = new Graphics();
rectangle.beginFill(0x66CCFF);
rectangle.lineStyle(4, 0xFF3300, 0.5); // 宽度,颜色,透明度
rectangle.drawRect(2, 2, 64, 64);
rectangle.endFill();

rectangle.zIndex = 1;

let container = new Container();
container.sortableChildren = true; // 将子级显示对象变得可排序层级
container.addChild(message)
container.addChild(rectangle)

app.stage.addChild(container) // 将容器加入舞台中

stage可以理解为根部的container,表示为画布上的舞台,舞台的大小根据内容的大小而定,并非充满整个canvas,如果内容很小,而把stage大小设置成和canvas一样大,那么内容就会被强行放大而导致模糊,当内容被其他的container包裹时亦是如此。

文本以及矩形即显示对象DisplayObjectDisplayObject是所有可以被渲染在屏幕上的类的基类,DisplayObject继承了EventEmitterEventEmitter主要提供一个通用的事件的发送和接受功能

绘制一个矩形

let rectangle = new Graphics();
// 填充颜色
rectangle.beginFill(0x66CCFF);
// 绘制边框
rectangle.lineStyle(4, 0xFF3300, 0.5); // 宽度,颜色,透明度
// 定义矩形起始坐标,以及宽高
rectangle.drawRect(2, 2, 64, 64);
rectangle.endFill();

app.stage.addChild(rectangle)

这里4像素,并非像css一样,从元素的外轮廓开始绘制,4像素会有2像素在矩形外部,2像素在矩形内部

加载一个精灵图

import imgSrc from './lion-check.png';

// ... 

app.loader.add(imgSrc).load(setup);

// 图片加载后的回调
function setup() {
    // 将图片加入文理缓存
    let texture = PIXI.utils.TextureCache[imgSrc];
    let lion = new PIXI.Sprite(texture);
    app.stage.addChild(lion);
}

官方文档中的loader相关代码

PIXI.loader
  .add("images/anyImage.png")
  .load(setup);

这里PIXI中并没有loader,而是Aplication构造出来的实例app中才有

显示对象的层级关系

  • 层级关系和addChild调用顺序有关,先插入的层级低,显示在下面,最后插入的显示在最上层
  • 也可以通过将这些元素放入到一个container中,将该containersortableChildren属性设置为true,然后修改他们的zIndex值来更改层级,zIndex值越低层级越低

image.png

事件处理

事件类型大致分为:

//兼容鼠标和触摸屏的共同触发
type InteractionPointerEvents = "pointerdown" | "pointercancel" | "pointerup" | "pointertap" | "pointerupoutside" | "pointermove" | "pointerover" | "pointerout";
//触摸屏触发事件   
type InteractionTouchEvents = "touchstart" | "touchcancel" | "touchend" | "touchendoutside" | "touchmove" ;
//鼠标触发事件
type InteractionMouseEvents = "rightdown" | "mousedown" | "rightup" | "mouseup" | "rightclick" | "click" | "rightupoutside" | "mouseupoutside" | "mousemove" | "mouseover" | "mouseout";
1. 给一个显示对象绑定鼠标按下事件

clickDemo.gif

let message = new PIXI.Text("文本", style);
message.interactive = true; // 将 message 变成可交互对象
message.buttonMode = true; // 将指针变为手的形状
message.on('pointerdown', e => {
    // 阻止事件冒泡
    e.stopPropagation();
})

首先要让显示对象变成可交互,将interactive设置为true,否则绑定事件也无效。buttonMode即使不设置也可以交互,设置之后鼠标指针会变成点击按钮的手指形状。类似css设置cursor: pointer

2. 拖拽事件

dragDemo.gif

由于pixi没有直接提供拖拽事件,所以我们自己实现

let style = new PIXI.TextStyle({/* */});

let message = new PIXI.Text("我可以拖动", style);
message.interactive = true;
message.buttonMode = true;
app.stage.interactive = true;

let selectedTarget;
let offsetX = 0;
let offsetY = 0;

message.on('pointerdown', e => {
    e.target.alpha = 0.5;
    selectedTarget = e.target;
    let { x, y } = e.data.global;
    // 计算出鼠标坐标相对于元素内部的坐标,便于之后设置偏移
    offsetX = x - selectedTarget.x;
    offsetY = y - selectedTarget.y;
    app.stage.on('pointermove', onMove)
})

function onMove(e) {
    let { x, y } = e.data.global;
    let point = {
        x: x - offsetX,
        y: y - offsetY
    }
    selectedTarget.parent.toLocal(point, null, selectedTarget.position);
}
message.on('pointerup', e => {
    selectedTarget.alpha = 1;
    app.stage.removeListener('pointermove', onMove)
})

app.stage.addChild(message);

鼠标相对于画布的坐标存于事件对象的data.global中,官方的example调用为

e.global

现改为

e.data.global

toLocal源码位于display/src/DisplayObject.ts

/**
 * Calculates the local position of the display object relative to another point.
 *
 * @param {PIXI.IPointData} position - The world origin to calculate from.
 * @param {PIXI.DisplayObject} [from] - The DisplayObject to calculate the global position from.
 * @param {PIXI.Point} [point] - A Point object in which to store the value, optional
 *  (otherwise will create a new Point).
 * @param {boolean} [skipUpdate=false] - Should we skip the update transform
 * @return {PIXI.Point} A point object representing the position of this object
 */
toLocal<P extends IPointData = Point>(position: IPointData, from?: DisplayObject, point?: P, skipUpdate?: boolean): P
{
    /* 
        省略...
    */
}

第一个参数position和第三个参数都带有xy的信息

3. 滚动事件

2f0FAI.gif

滚动方案直接使用pixi-scrollbox,该插件依赖pixi-viewport

import img from '@/images/bg.jpg';

/*
 省略部分代码
*/

const scrollbox = new Scrollbox({ 
    boxWidth: 400, 
    boxHeight: 200, 
    fade: true, // 滚动条自动消失
    divWheel: app.view,
    interaction: app.renderer.plugins.interaction // 如果不加上,无法检测鼠标滚轮
})

app.loader.add(img).load(setup);

app.stage.interactive = true;

function setup() {
    let texture = PIXI.utils.TextureCache[img];
    let bg = new PIXI.Sprite(texture);
    const sprite = scrollbox.content.addChild(bg)
    sprite.width = 3840 / 4
    sprite.height = 2160 / 4
    // sprite.tint = 0xff0000
    scrollbox.update()
    app.stage.addChild(scrollbox)
}

demo源码

github地址

最后求个赞~

image.png