PixiJS 101
// This examples is hard
// To understand it, you have to carefully read all readme`s and other examples of respective plugins
// Be ready to study the plugins code. Please use latest version of those libs
// Used plugins: pixi-projection, pixi-display
// 新建一个PIXI程序
var app = new PIXI.Application(800, 600, {
resolution: 1,
autoStart: false,
antialias: true
});
document.body.appendChild(app.view);
app.stage = new PIXI.display.Stage();
var loader = app.loader;
loader.baseUrl = 'required/assets/proj';
// 3D摄像机部分
var camera = new PIXI.projection.Camera3d();
camera.position.set(app.screen.width / 2, app.screen.height / 2);
camera.setPlanes(350, 30, 10000); //设定平面,视距。
// In this case, 350 is focus distance.
// If width of the screen is 700, that means 90 degrees horizontal FOV.
// Everything that's behind z < -320 will be cut by near plane, everything that's too far away z > 9650 will be cut too.
camera.euler.x = Math.PI / 5.5;
app.stage.addChild(camera);
var cards = new PIXI.projection.Container3d(); // 一个3D容器
cards.position3d.y = -50;
// MAKE CARDS LARGER:
cards.scale3d.set(1.5);
camera.addChild(cards);
var shadowGroup = new PIXI.display.Group(1); // 投影组
var cardsGroup = new PIXI.display.Group(2, function(item) {
item.zOrder = item.getDepth(); // 直接把深度赋给z轴
item.parent.checkFace();
});
// Layers are 2d elements but we use them only to show stuff, not to transform items, so its fine :)
// 这里直接把阴影组和卡组当做2D对象处理了。当然这样处理结果是他们没有厚度。
camera.addChild(new PIXI.display.Layer(shadowGroup));
camera.addChild(new PIXI.display.Layer(cardsGroup));
//we could also add layers in the stage, but then we'll need extra layer for the text
// load assets
loader.add('cards', 'casino/cards.json');
loader.add('table', 'casino/table.png');
loader.load(onAssetsLoaded);
// blur for shadow. Do not use it in production, bake shadow into the texture!
// 这里是模糊效果。模糊效果应该在生产环境中直接渲染到贴图上,这样比较节省性能。
var blurFilter = new PIXI.filters.BlurFilter();
blurFilter.blur = 0.2;
// PixiJS特有的“精灵”函数
function CardSprite() {
PIXI.projection.Container3d.call(this);
let tex = loader.resources['cards'].textures;
//shadow will be under card
this.shadow = new PIXI.projection.Sprite3d(tex['black.png']);
this.shadow.anchor.set(0.5);
this.shadow.scale3d.set(0.98);
this.shadow.alpha = 0.7;
//TRY IT WITH FILTER:
this.shadow.filters = [blurFilter];
//all shadows are UNDER all cards
this.shadow.parentGroup = shadowGroup;
this.inner = new PIXI.projection.Container3d();
//cards are above the shadows
//either they have back, either face
this.inner.parentGroup = cardsGroup;
this.addChild(this.shadow);
this.addChild(this.inner);
//construct "inner" from back and face
this.back = new PIXI.projection.Sprite3d(tex['cover1.png']);
this.back.anchor.set(0.5);
this.face = new PIXI.projection.Container3d();
this.inner.addChild(this.back);
this.inner.addChild(this.face);
this.code = 0;
this.showCode = -1;
this.inner.euler.y = Math.PI;
this.scale3d.set(0.2);
//construct "face" from four sprites
this.createFace();
}
// 用Object.create作为继承元素。
CardSprite.prototype = Object.create(PIXI.projection.Container3d.prototype);
CardSprite.prototype.createFace = function() {
var face = this.face;
face.removeChildren();
var tex = loader.resources['cards'].textures;
var sprite = new PIXI.projection.Sprite3d(tex['white1.png']);
var sprite2 = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY);
var sprite3 = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY);
var sprite4 = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY);
sprite2.y = -120;
sprite2.x = -80;
sprite3.y = 70;
sprite3.x = 40;
sprite4.y = -70;
sprite4.x = -100;
sprite.anchor.set(0.5);
sprite2.anchor.set(0.5);
sprite3.anchor.set(0.5);
face.addChild(sprite);
face.addChild(sprite2);
face.addChild(sprite3);
face.addChild(sprite4);
this.updateFace();
};
CardSprite.prototype.updateFace = function() {
var tex = loader.resources['cards'].textures;
var code = this.showCode == -1 ? 0 : this.showCode;
var num = code & 0xf;
var suit = code >> 4;
var face = this.face;
face.children[1].texture =
num > 0 ? tex[(suit % 2) + '_' + num + '.png'] : PIXI.Texture.EMPTY;
if (!face.children[1].texture) {
console.log('FAIL 1 ', (suit % 2) + '_' + num + '.png');
}
face.children[2].texture =
suit !== 0 ? tex[suit + '_big.png'] : PIXI.Texture.EMPTY;
if (!face.children[2].texture) {
console.log('FAIL 2', suit + '_big.png');
}
face.children[3].texture =
suit !== 0 ? tex[suit + '_small.png'] : PIXI.Texture.EMPTY;
if (!face.children[3].texture) {
console.log('FAIL 3', suit + '_small.png');
}
};
CardSprite.prototype.update = function(dt) {
var inner = this.inner;
if (this.code > 0 && inner.euler.y > 0) {
inner.euler.y = Math.max(0, inner.euler.y - dt * 5);
}
if (this.code == 0 && inner.euler.y < Math.PI) {
inner.euler.y = Math.min(Math.PI, inner.euler.y + dt * 5);
}
inner.position3d.z = -Math.sin(inner.euler.y) * this.back.width;
//assignment is overriden, so its actually calling euler.copy(this.euler)
this.shadow.euler = inner.euler;
};
CardSprite.prototype.checkFace = function() {
var inner = this.inner;
var cc;
if (!inner.isFrontFace()) {
//user sees the back
cc = 0;
} else {
//user sees the face
cc = this.showCode || this.code;
}
if (cc == 0) {
this.back.renderable = true;
this.face.renderable = false;
} else {
this.back.renderable = false;
this.face.renderable = true;
}
if (cc !== this.showCode) {
this.showCode = cc;
this.updateFace();
}
};
function dealHand() {
cards.removeChildren();
for (var i = 0; i < 5; i++) {
var card = new CardSprite();
card.position3d.x = 56 * (i - 2);
if (((Math.random() * 3) | 0) == 0) {
onClick({ target: card });
}
card.update(0);
card.interactive = true;
card.on('mouseup', onClick);
card.on('touchend', onClick);
cards.addChild(card);
}
}
function onClick(event) {
var target = event.target;
if (target.code == 0) {
var num = ((Math.random() * 13) | 0) + 2;
var suit = ((Math.random() * 4) | 0) + 1;
target.code = suit * 16 + num;
} else {
target.code = 0;
}
}
function addText(txt) {
var style = {
font: 'normal 80px Arial',
fill: '#f5ffe3',
dropShadow: true,
dropShadowColor: 'rgba(1, 1, 1, 0.4)',
dropShadowDistance: 6,
wordWrap: false
};
var basicText = new PIXI.projection.Text3d(txt, style);
basicText.position3d.x = -240;
basicText.position3d.y = 20;
camera.addChild(basicText);
}
function onAssetsLoaded() {
//background must be UNDER camera, it doesnt have z-index or any other bullshit for camera
app.stage.addChildAt(new PIXI.Sprite(loader.resources.table.texture), 0);
dealHand();
addText('Tap on cards');
// start animating
app.start();
}
app.ticker.add(function(deltaTime) {
for (var i = 0; i < cards.children.length; i++) {
cards.children[i].update(deltaTime / 60.0);
}
// We are gonna sort and show correct side of card,
// so we need updateTransform BEFORE the sorting will be called.
// otherwise this part will be tardy by one frame
camera.updateTransform();
});