Pixi.js
安装
node
npm i pixi.js
import * as PIXI from 'pixi.js'
cdn js
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
基本使用
引用成功
let type = PIXI.utils.isWebGLSupported ? 'webGL' : 'canvas';
PIXI.utils.sayHello(type);
全局对象
PIXI.Application
应用类PIXI.utils
工具库PIXI.Loader
加载器类PIXI.BaseTexture
基础纹理类PIXI.Texture
纹理类PIXI.Sprite
精灵类PIXI.AnimatedSprite
动画精灵类PIXI.TilingSprite
叠加精灵类PIXI.Text
文字类PIXI.Graphics
图形类PIXI.Container
Container类
创建应用
应用会自动集成 renderer
, ticker
和根 container
;
// 创建并编辑应用
let app = new PIXI.Application({
width: 100,
height: 100
});
document.body.appendChild(app.view); // app.view即canvas元素(画布)
// 更多的可覆盖的配置
let app2 = new PIXI.Application({
width: 100,
height: 100,
antialias: true, // 是否抗锯齿
transparent: true, // 是否透明,默认为黑色
resolution: 1 // 分辨率
forceCanvas: true // 强制renderer使用canvasAPI
});
document.body.appendChild(app2.view);
// renderer渲染器 修改应用/重渲染画布
app.renderer.backgroundColor = 0xCC00FF; // 16进制颜色
app.renderer.autoResize = true; // 重设大小时自适应分辨率
app.render.resize(window.innerWidth, window.innerHeight);
纹理(Texture)
BaseTexture
每个纹理基于一个基础纹理,基础纹理创建与转换:
-
Image
// 1. 创建Image实例 var img = new Image(); img.setAttribute("src", "/images/awards.png"); // 2. 转纹理 // 注:即使图片当前未加载完成,PIXI能在将来的图片加载完成时自动同步src值 var baseTexture = new PIXI.BaseTexture(img); // 纹理物料实例 var texture = new PIXI.Texture(baseTexture); // 纹理实例 var sprite = new PIXI.Sprite(texture); app.stage.addChild(sprite);
-
canvas元素
let basetexture = new BaseTexture.fromCanvas($canvas); // 纹理物料fromCanvas实例 let texture = new Texture(basetexture); // 纹理实例
Texture
const texture = Texture.from('assets/image.png');
const sprite1 = new Sprite(texture);
const sprite2 = new Sprite(texture);
精灵(Sprite)
精灵: 作为stage容器的子项并绘制在view上的元素;纹理直接对精灵负责;
纹理: Pixi使用WebGL在GPU上渲染图像,图像无法直接被GPU渲染,需要经过图像 -> 纹理 -> 显示图像的过程;
纹理创建
- 通过图片文件
- 通过雪碧图
- 通过纹理贴图
图片文件创建
-
PIXI.Loader加载图像资源
const loader = new PIXI.Loader(); loader.add("url/path") // 允许传入多个文件路径的数组 .load(setup);
-
图像的纹理,创建精灵并添加至舞台
function setup(loader, resources){ var texture = loader.resources["url/path"].texture; var sprites = new PIXI.Sprites(texture); app.stage.addChild(sprite); }
附:
loader.resources["url/path"].texture
获取图像资源的纹理对象外,通过PIXI.utils.TextureCache("url/path")
工具方法允许从纹理缓存中取出对应资源的纹理对象;- stage是一个pixi容器,不同于app.view(画布元素),stage用于容纳pixi其它对象的对象,对stage的操作也会反应在view上;
- 移除精灵,即
app.stage.removeChild(sprite)
或隐藏精灵sprite.visible = false
雪碧图创建
// 1. 加载雪碧图资源
const loader = new Loader().add({
name: "sprite_game",
url: "./images/sprite_game.png"
}).load(setup);
function setup(loader, resources){
// 2. 创建纹理
var texture = utils.TextureCache["sprite_game"];
// 3. 创建矩形选区,裁剪纹理
var rect = new Rectangle(96, 64, 32, 32);
texture.frame = rect;
// 4. 创建精灵并添加至舞台
var sprite = new Sprite(texture);
app.stage.addChild(sprite);
}
纹理贴图集创建
// 1. 软件生成纹理贴图集json/png(此处使用TexturePacker工具)
// 2. 加载纹理贴图集资源
new Loader().add({
name: "sprites",
url: "./images/sprite.json"
})
.load(setup);
function setup(loader, resources){
// 3. 获取纹理贴图集中纹理各种方式
// 3.1 从纹理贴图集textures 中获取
var sprites = resources["sprites"].textures;
var txure_dungeon = sprites["dungeon.png"];
var txure_explorer = sprites["explorer.png"];
var txure_treasure = sprites["treasure.png"];
var txure_door = sprites["door.png"];
// 游戏背景
var dungeon = new Sprite(txure_dungeon);
// 探索者——角色
var explorer = new Sprite(txure_explorer);
explorer.position.set(68, app.view.height / 2 - explorer.height / 2);
// 宝物
var treasure = new Sprite(txure_treasure);
treasure.position.set(416, app.view.height / 2 - treasure.height / 2);
// 门——入口
var door = new Sprite(txure_door);
door.position.set(32, 0);
app.stage.addChild(dungeon, explorer, treasure, door);
// 3.2 从纹理缓存中获取
var txure_blob = utils.TextureCache["blob.png"];
// 生成位置随机但不重叠的blob
var blobNum = 6; // 生成blob数
var diff = ((416 - 90) - 32 * 10) / 2; // 差值计算,横向可容量10个,居中差值为diff
var baseX = 90 + diff; // 生成范围的基础X
var baseY = 24; // 生成范围的基础Y
var NumX = 10; // 横向格子数
var NumY = 19; // 纵向格子数
var randomPositionArr = randomInt([0, NumX - 1], [0, NumY - 1], blobNum); // 不重复的随机坐标的数组
for(var i = 0; i < blobNum; i++){
var blob = new Sprite(txure_blob);
var x = baseX + randomPositionArr[i].x * blob.width;
var y = baseY + randomPositionArr[i].y * blob.height;
blob.position.set(x, y);
app.stage.addChild(blob);
}
}
// 不重复的随机坐标生成
function randomInt(spaceX, spaceY, count){
var arr = [];
do{
let offsetItem = {
x: spaceX[0] + Math.floor(Math.random() * (spaceX[1] - spaceX[0] + 1)),
y: spaceX[0] + Math.floor(Math.random() * (spaceY[1] - spaceY[0] + 1)),
}
var isRepeat = arr.some(item => item.x === offsetItem.x && item.y === offsetItem.y);
if(!isRepeat) arr.push(offsetItem);
}while(
arr.length < count
);
return arr;
}
注: 多个精灵添加到stage时,默认层级规则为新添加的覆盖已存在的sprite;
控制精灵
换肤
// 纹理更换
sprite.texture = newTexture;
定位
// 定位属性
sprite.x = 50;
sprite.y = 50;
// set方法
sprite.position.set(50, 50);
移动精灵
// 1. app.ticker(添加处理到每帧执行)
app.ticker.add(move);
function move(){
explorer.x += 1.5;
if(explorer.x >= 392) app.ticker.remove(move);
}
// 2. requestAnimationFrame
(function move(){
explorer.x += 2;
if(explorer.x <= 392) requestAnimationFrame(move);
})()
// 控制移动速度
explorer.vx = 1;
explorer.vy = 1;
app.ticker.add(move);
function move(){
explorer.x += explorer.vx;
explorer.y += explorer.vy;
if(explorer.x >= 392) app.ticker.remove(move);
}
document.addEventListener("click", () => { explorer.vx = 2;explorer.vy = 2 });
大小
// 访问/获取宽高
sprite.width = 100;
sprite.height = 100;
缩放
// 缩放属性
sprite.scale.x = 0.5;
sprite.scale.y = 0.5;
// set方法
sprite.scale.set(2, 2);
层级
// 设置容器允许排序层级
container.sortableChildren = true;
// 精灵层叠
sprite.zIndex = -1;
旋转
sprite.rotation = Math.PI / 2; // 90°弧度值
sprite.angel = 90; // 角度
永远基于原点(默认左上角)旋转,通过设置anchor或pivot实现中心旋转;
anchor(锚点): 纹理基准点,默认为sprite原点(左上),修改锚点即修改纹理渲染参考点的相对位置;
// 锚点位置
sprite.anchor.x = 0.5; // 相对于原点左偏移50%
sprite.anchor.y = 0.5; // 相对于原点上偏移50%
// set方法
sprite.anchor.set(1, 1);
pivot(原点): sprite基准点,默认sprite的(x,y)点作为左上角,修改pivot可以修改坐标基准点的相对偏移位置;
sprite.pivot.set(sprite.width/2, sprite.height/2);
修改anchor即对纹理渲染加偏移量,sprite渲染偏移到精灵原点为中心处;
修改pivot位置是基准点改变,sprite原点位置即基准点来实现中心旋转;
动画精灵(AnimatedSprite)
创建动画
const Atlas = [0,1,2,3].map(index => {
const frame = new Rectagle(index * 100, 0, 100, 100);
return new PIXI.Texture(texture, frame);
});
const animatedSprite = PIXI.AnimatedSprite(Atlas);
控制动画
播放:animatedSprite.play()
停止:animatedSprite.stop()
速度缩放:animatedSprite.animationSpeed = 0.15
(默认值1)
关闭循环:animatedSprite.loop = false
是否正在播放中:animatedSprite.playing === true
跳到指定帧数并播放:animatedSprite.gotoAndPlay(6)
跳到指定帧数并暂停:animatedSprite.gotoAndStop(3)
问题
帧图过长:pixi.js
对图片转GPU纹理存在尺寸限制,可通过多列排布单帧图避免宽/高超过限制
叠加精灵(TilingSprite)
创建叠加
// 纹理 精灵视口宽高
const tiling = new PIXI.TilingSprite(texture,width,height);
叠加位置
tiling.tilePosition.x -= 10;
tiling.tilePosition.y -= 10;
// 边界重置
if (tiling.tilePosition.x === -tiling.width) tiling.tilePosition.x = 0;
文字(Text)
// 绘制文字
const text = new Text("这是一段文字...");
text.style = {
fontFamily: "Arial",
fontSize: 24,
fill: "red",
wordWrap: true, // 是否允许换行
wordWrapWidth: 50, // 换行时宽度值
breakWords: true, // 单词是否可截断
align: "center",
};
text.x = 0;
text.y = 50;
app.stage.addChild(text);
// 直接调整文字颜色
text.style.fill = "white";
图形(Graphics)
绘制形状
// 绘制图形
const graphics = new Graphics();
graphics.beginFill(0x000080); // 开始填充
graphics.lineStyle(1, 0xc0c0c0); // 绘制边框
// 绘制动作
// graphics.drawRect(0, 0, 61.8, 30); // 矩形
// graphics.drawCircle(10, 10, 20); // 圆形
// graphics.drawEllipse(x, y, 61.8, 30); // 椭圆形
// graphics.drawRoundedRect(0, 0, 61.8, 30, 10); // 圆角矩形
// graphics.moveTo(0, 0); // 线条
// graphics.lineTo(80, 50);
// graphics.alpha = 0; // 转为透明层
graphics.drawPolygon([-32, 64, 32, 64, 0, 0]); // 多边形
// shadowMask.closePath(); // 自动闭合路径
graphics.endFill(); // 结束填充
graphics.x = 100;
graphics.y = 100;
app.stage.addChild(graphics);
纹理填充
const panel = new PIXI.Graphics();
// 通过矩阵调整纹理居中
const xOffset = -((texture.width - this.width) >> 1);
const matrix = new PIXI.Matrix();
matrix.tx = xOffset;
// 纹理填充
panel.beginTextureFill({ texture: panelTexture, matrix });
panel.drawRect(0, 0, options.width, options.height);
panel.endFill();
容器(Container)
将多个Sprite组合到Container分组,方便整体管理;
创建组合
let blobAndExplorer = new Container();
blobAndExplorer.addChild(blobSprite, explorerSprite);
blobAndExplorer.position.set(10, 10);
app.stage.addChild(blobAndExplorer);
Container
属性
console.log("blobAndExplorer成员", blobAndExplorer.children);
console.log("blobAndExplorer位置", blobAndExplorer.x, blobAndExplorer.y);
// toGlobal 将容器当做全局计算精灵相对位置
console.log(
"toGlobal计算exolorer相对blobAndExplorer位置",
blobAndExplorer.toGlobal(explorerSprite.position)
);
Sprite
相关属性
console.log("explorerSprite父容器", explorerSprite.parent);
console.log("explorerSprite位置", explorerSprite.x, explorerSprite.y);
console.log(
"explorerSprite全局位置",
explorerSprite.getGlobalPosition().x,
explorerSprite.getGlobalPosition().y
);
// toLocal 基于精灵计算另一精灵位置
console.log(
"toLocal计算exolorer相对blobSprite位置",
blobSprite.toLocal(explorerSprite.position, blobSprite)
);
当 sprite
从一个 Container
放到新 Container
时,会先被旧 Container
中移除;
粒子容器
粒子容器上精灵位置直接在GPU上计算,来实现高性能的方式来组合精灵;当你需要大量的精灵或粒子时可以使用。
代价是精灵仅支持:x
、y
、width
、height
、scale
、pivot
、alpha
、visible
属性,支持基本的对象转换(位置、比例、旋转)和一些高级功能,如着色;但不支持 masking
, children
, filters
等高级功能;
创建粒子容器
let superFastContaier = new ParticleContainer({
maxSize, properties, batchSize, autoResize
});
区域
矩形选区:new Rectangle(x, y, width, height)
圆形选区:new Circle(x, y, radius)
圆角矩形:new RoundedRectangle(x, y, width, height, radius)
精灵边界区域:sprite.getBounds()
边界重叠检测:rectangle.intersets(rect)
事件
绑定
sprite.on('touchend', this.handle.bind(this))
可交互
sprite.interactive = true
加载器(Loader)
定义加载资源的名称
const loader = new PIXI.loader.add("avatar", "url/path/avatar.png") // 定义该资源为变量avatar
.load(callback);
// 访问
let texture = loader.resources.avatar.texture;
注: 对于常用的resources,定义名称是非常重要的;但变量名称并不能替代资源url作为资源唯一标识;
监听资源加载进度
let images_list = ["./images/yanhua.webp", "./images/paopao.webp"];
loader
.add(images_list)
.on("progress", (loader, resource) => { // 监听progress事件,监听写法与jquery类似
console.log(loader.progress + "%", resource.url); // loader进度与当前加载完成资源
})
.load((loader, resources) => {
console.log("加载完成");
});
Loader配置
Loader实例
const loader = new PIXI.Loader({
"url/path", // 资源公共路径
12 // 同时加载资源数,默认10
});
loader.onProgress.add(() => {}); // 每加载/错误时
loader.onError.add(() => {}); // 每错误时
loader.onLoad.add(() => {}); // 每加载时
loader.onComplete.add(() => {}); // 全部加载完成时
add方法
// 单个资源配置
// 参数
loader.add(name, url, options, callback).load();
// 对象类型参数
loader.add({name: "xxx", url: "url/path", ...options, onComplete(){ ... }}).load();
// 多资源配置
loader.add([ "url/path1", "url/path2", "url/path2", "url/path3" ]).load();
loader.add([ { ... }, { ... }, ... ]).load();
Loader.shared
返回1个全局共享loader,特性包括:
-
任何地方任何时间新增加载资源(add)
-
PIXI.add(resources)
新增资源时会自动调用load
-
内置缓存,重复加载从缓存中加载
版本差异
资源加载
v4.x new Loader().add().load()
或者 pixi.loader.add().load()
v6.x 废弃pixi.loader
,保留 new Loader
类实现
v7.x Assets.load()
,内部基于 Assets.loader
对象,返回Promise实例
监听加载
v6.x 加载事件监听处理
-
loader.onStart.add(callback)
-
loader.onProgress.add(callback)
-
loader.onError.add(callback)
-
loader.onComplete.add(callback)