pixi疑难杂症汇总
这段时间在忙一个七夕活动游戏的项目,技术栈基于gsap + pixi.js + ts + vue
因为算是第一次用pixi和gsap来做项目,踩了不少坑。特此记录整理一下。
gsap挂载pixi.js插件
import gsap, { PixiPlugin } from 'gsap/all';
gsap.registerPlugin(PixiPlugin);
PixiPlugin.registerPIXI(PIXI);
loader加载音频、gif等特殊资源
加载音频
pixijs 在苹果手机上面的微信浏览器里面用loader无法加载音频文件
加载图片很容易,但是有时候加载音频文件会卡住不动。
网上大部分能搜到的都是在pixi的 v4.0.0+ 版本中,通过修改loadtype来解决
PIXI.loaders.Resource.setExtensionLoadType( 'mp3', PIXI.loaders.Resource.LOAD_TYPE.XHR );
PIXI.loaders.Resource.setExtensionXhrType( 'mp3', PIXI.loaders.Resource.XHR_RESPONSE_TYPE.BLOB );
在 v5.0.0+ 版本中,原理是一样的,只是修改方法不一样,可以直接在load.add方法中来添加参数
// npm i @pixi/gif
import { Loader, LoaderResource } from 'pixi.js';
const loader = Loader.shared;
loader.add(
{
loadType: LoaderResource.LOAD_TYPE.XHR,
url: 'assets/audio.mp3'
})
.load(setup)
function setup() {
...
}
加载gif
gif图片也可以用来当作精灵来使用
import { Loader, LoaderResource, Sprite } from 'pixi.js';
import { AnimatedGIF, AnimatedGIFLoader } from '@pixi/gif';
Loader.registerPlugin(AnimatedGIFLoader);
const loader = Loader.shared;
const gifSprites: Record<string, AnimatedGIF> = {} as any;
loader.add(
{
key: 'gif',
url: 'assets/gg.gif'
})
.load((_, res) => {
gifSprites['gif'] = res['gif'].animation;
})
mask遮罩导致点击事件失效
当元素的遮罩mask设置透明度为0时,元素的绑定事件会失效。将透明度置为1即可
// Create a graphics object to define our mask
let mask = new PIXI.Graphics();
// Add the rectangular area to show
mask.beginFill(0xffffff, 1); // 第二个参数为透明度,若设置为0,则maskContainer的点击事件不会生效
mask.drawRect(0,0,200,200);
mask.endFill();
// Add container that will hold our masked content
let maskContainer = new PIXI.Container();
// Set the mask to use our graphics object from above
maskContainer.mask = mask;
// Add the mask as a child, so that the mask is positioned relative to its parent
maskContainer.addChild(mask);
// Offset by the window's frame width
maskContainer.position.set(4,4);
// And add the container to the window!
this.app.stage.addChild(maskContainer);
maskContainer.interactive = true
maskContainer.on('pointerdown', () => {
console.log('pointerdown');
})
Container的背景色设置和阴影设置
坦白来说,Container并不能设置背景色和阴影,因为它本来只是作为一系列元素的集合,并不是真正div元素
等价方法就是创建一个Graphics元素,宽高设置为Container的宽高,来模仿背景色。
阴影设置也是手动绘制图形,放在Container下方来模拟。
绘制自定义图形
目前官方的绘制函数,有圆形、矩形、多边形、椭圆
可以自定义想要的绘制函数
const words = new Graphics()
this.words.beginFill(0x000000);
// 传入Graphics元素,进行手动绘制
this.roundRect(words, 0, 0, 100, 50, 9, 'right');
this.words.endFill();
function roundRect(ctx, x, y, w, h, r, direction) {
// 画圆角 ctx、x起点、y起点、w宽度、y高度、r圆角半径, direction圆角朝向left/right
// 绘制左上角圆弧 Math.PI = 180度
// 圆心x起点、圆心y起点、半径、以3点钟方向顺时针旋转后确认的起始弧度、以3点钟方向顺时针旋转后确认的终止弧度
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
// 绘制border-top
// 移动起点位置 x终点、y终点
ctx.moveTo(x + r, y);
// 画一条线 x终点、y终点
ctx.lineTo(x + w - r, y);
// 绘制右上角圆弧
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
if (direction == 'right') {
// 三角上的间距
ctx.lineTo(x + w, y + h / 2 - 10);
// 绘制三角
ctx.lineTo(x + w + 10, y + h / 2);
ctx.lineTo(x + w, y + h / 2 + 10);
}
// 绘制border-right
ctx.lineTo(x + w, y + h - r);
// 绘制右下角圆弧
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
// 绘制border-bottom
ctx.lineTo(x + r, y + h);
// ctx.lineTo(x, y + h - r)
// 绘制左下角圆弧
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// 绘制border-left
if (direction == 'left') {
// 三角下的间距
ctx.lineTo(x, y + h / 2 + 10);
// 绘制三角
ctx.lineTo(x - 10, y + h / 2);
ctx.lineTo(x, y + h / 2 - 10);
}
ctx.lineTo(x, y + r);
}
直接采用相对路径加载图片失败
当你采用下面的方法来直接load图片资源的话,会报错「Error: Failed to load element using: IMG」
loader
.add("a", "logo.png")
.add("b", "bbb.png")
.load(handleLoadComplete);
function handleLoadComplete() {
let texture = loader.resources.a.texture;
img = new PIXI.Sprite(texture);
img.anchor.x = 0.5;
img.anchor.y = 0.5;
app.stage.addChild(img);
app.ticker.add(animate);
};
function animate() {
img.x = app.renderer.screen.width / 2;
img.y = app.renderer.screen.height / 2;
img.rotation += 0.1;
}
原因是图像的路径是相对于项目根目录指定的,而不是相对于编译版本。由于项目是动态构建的,为了找出图像的相对路径(以及哈希等等),不需要手动指定路径,只需导入图像:
可以使用import来引入正确的路径,或者使用require函数来加载(推荐)
import logoImagePath from './logo.png';
import bbbImagePath from './bbb.png';
loader
.add("a", logoImagePath)
.add("b", bbbImagePath)
// 或者
loader
.add("a", require("logo.png"))
.add("b", require("bbb.png"))
.load(handleLoadComplete);
pixi实现3D类旋转
因为pixi是一个2D的动画引擎,实现在平面上的动效是比较方便的,例如平移、旋转等。
不过实现3D的旋转,只需要将scale值设置为负数即可
// 这种办法需要和gsap结合使用。
gsap.to(
this.container,
{
duration: 0.9,
pixi: { scaleX: -0.5 }
}
)
pixi精灵图加载方法
网上能够搜到的精灵图加载方法,基本上都是直接loader加载json文件,就可以拿到纹理了。
const app = new PIXI.Application();
document.body.appendChild(app.view);
app.loader
.add('examples/assets/spritesheet/fighter.json')
.load(onAssetsLoaded);
function onAssetsLoaded() {
...
}
但是我写demo来实现动画精灵的时候,这种方法无法正确添加纹理缓存。(pixi版本v6+)
因为webpack会编译我们的json文件,这样loader加载的时候就不是常量类型了。建议可以放在public文件夹下面,或者你利用脚手架设置的webpack不编译的地方。
如果无法改变文件夹位置,也可以使用下面这张方法,此时可以采用绕过loader的方法来加载,直接创建Spritesheet实例
import { Loader } from 'pixi.js';
import * as PIXI from "pixi.js";
const app = new PIXI.Application();
document.body.appendChild(app.view);
Loader.shared.add('spritesheet', require('./assets/dnf.png')).load(setup)
function setup(loader, resources) {
// console.log(PIXI.utils.TextureCache, resources['spritesheet']);
let id
const texture = loader.resources["spritesheet"].texture.baseTexture;
const sheet = new PIXI.Spritesheet(texture, jsonPath);
sheet.parse((textures) => {
id = textures;
});
//
// let id = resources['spritesheet'].textures;
//创建纹理数组
let frames = [
id["dnf0.png"],
id["dnf1.png"],
id["dnf2.png"],
id["dnf3.png"]
];
//创建动画精灵
let pixie = new PIXI.AnimatedSprite(frames);
//设置动画精灵的速度
pixie.animationSpeed = 0.1;
//把动画精灵添加到舞台
app.stage.addChild(pixie);
//播放动画精灵
pixie.play();
}
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。