前言
Pixi.js使用WebGL,是一個超快的HTML5 2D渲染引擎。作為一個Javascript的2D渲染器,Pixi.js的目標是提供一個快速的、輕量級而且是兼任所有設備的2D庫。提供無縫 Canvas 回退,支持主流瀏覽器,包括桌面和移動。 Pixi渲染器可以開發者享受到硬件加速,但並不需要了解WebGL。 網上暫時沒有找到關於
pixi.js的中文說明文檔,我這里僅是記錄項目中使用的經驗,即記錄利用pixi在制作精靈動畫時的一些方法。
=========================================================================
概述
需要說明的是,有些示例需要使用http://方式才能有效果,也就是說file://的方式有些示例是無法顯示的。
下面我們來看一下,用pixi創建一個精靈要經歷哪些步驟:
創建一個PIXI的實例,並展示出來,通常需要以下幾步:
- 創建一個舞台 (stage)
- 創建一個畫布 (render)
- 把畫布插入DOM中
- 創建一個精靈 (sprite)
- 把精靈加入畫布
- 把畫布加入舞台
- 刷新舞台
概念解釋:
舞台(stage):
// A Stage represents the root of the display tree. Everything connected to the stage is rendered
所有要渲染的對象都必須連接到舞台中才能被顯示出來,舞台處於整個樹形展示結構的最底層,可以理解為背景
demo :
var stage = new PIXI.Stage(0xFFFFFF); //接收一個16進制的值,用於背景的顏色
畫布(renderer):
選用canvas或webGL進行渲染的一個區域
demo:
var renderer = PIXI.autoDetectRenderer(400, 300);
它的后面還有兩個可選參數,分別 用來指定現有的canvas元素作為容器,canvas背景是否透明,
材質(texture):
可以理解成一種承載圖片的結構,它本身不能直接用於顯示,需要通過精靈(sprite)才能顯示,有點類似於dom中的臨時碎片(DocumentFragment)
demo:
var texture = PIXI.Texture.fromImage("bunny.png");
精靈(sprite):
可以直接用於舞台顯示的對象,可以理解為DOM中的element.
精靈可以直接用圖片創建,也可以先創建材質,再用材質創建精靈
demo :
var sprite = new PIXI.Sprite.fromImage('assets/image.png');//直接由圖片創建
//...
var texture = PIXI.Texture.fromImage("bunny.png"); var sprite = new PIXI.Sprite(texture);//由材質創建
// sprite.position.x = 30; //沒有單位
// sprite.position.y = 20; // 這就和給element設置樣式是一樣的道理。
事件(event):
PIXI庫在精靈和舞台上提供了事件,用於交互.
demo:
stage.click = function(data){ var event = data.originalEvent } sprite.click = function(data){ var event = data.originalEvent var target = event.target //阻止默認行為,sprite.buttonMode = false;
event.preventDefault(); }
此外,還有.mousedown,.mouseover,.mouseout,.mousemove,.mouseup ,.mouseupoutside , .touchstart,.touchend,.tap
注意事項:
- 對於有事件的精靈,通常需要設置sprite.interactive = true , 對於作按鈕用的精靈,需要設置sprite.buttonMode = true;
- 與DOM的事件不同的是,精靈中的事件,是進行了二次封裝的,
- 通常需要用var event = data.originalEvent來與普通dom中的event對象保持一至
不過又不完全相同,如data.originalEvent.stopPropagation()就不起作用.
關於stopPropagation的問題,我問過pixijs的開發者,他答復說stopPropagation是dom中的概念,在canvas/webGL中是沒有的,所以不能說是pixi的問題。
下面使用PIXI庫顯示一張圖片,簡單的演示下pixi的用法:
var stage = new PIXI.Stage(); var renderer = PIXI.autoDetectRenderer(400, 300); document.body.appendChild(renderer.view); var sprite = new PIXI.Sprite.fromImage('bunny.png'); stage.addChild(sprite);
直接貼出上面的代碼,在網頁上只能看到漆黑的背景, 這是因為精靈的創建是異步的,必須等加載完圖片之后才能把精靈加入舞台中
我在這里給出兩種解決方式:
1:不斷的去刷新舞台:
requestAnimFrame(animate); function animate(){ renderer.render(stage); requestAnimFrame(animate); }
2. 在圖片加載完成的回調中執行。這僅限於不需要刷新的精靈、
sprite.texture.baseTexture.on('loaded',function(){ renderer.render(stage); })
在項目中實踐的時候發現用基於baseTexture的loaded事件有兩個問題
1:如果圖片加載失敗怎么辦?
2:同時加載多張圖片的時候,有些沒有觸發圖片的loaded回調
經過實踐,找到一個更加穩妥的辦法:
var img = new Image(); img.src = 'bunny.png'; img.onload = function(){ var baseTexture = new PIXI.BaseTexture(this); var texture = new PIXI.Texture(baseTexture); var sprite = new PIXI.Sprite(texture); stage.addChild(sprite); renderer.render(stage); }
此方案對於批量加載圖片也是適用的。最后再重復一遍,請用localhost://xxxxxx的方式進瀏覽
細心的比較也許你會發現,我和官方的示例不相同,官方使用了requestAnimFrame( animate ),而然我這里沒有用,為什么?因為我只是要顯示一張圖片而已,沒有必要不斷的去刷新舞台。requestAnimFrame( animate );這個是在精靈有變化的時候才需要用到。當然,如果不考慮性能,對一張靜態的圖不斷的刷新也沒有什么問題。
完整的示例:
var stage = new PIXI.Stage(0xFFFFFF); var renderer = PIXI.autoDetectRenderer(400, 300); document.body.appendChild(renderer.view); var sprite = new PIXI.Sprite.fromImage('bunny.png'); stage.addChild(sprite); sprite.texture.baseTexture.on('loaded',function(){ renderer.render(stage); }) sprite.interactive = true stage.click = stage.tap = function(data){ console.log(data.originalEvent.target); //alert('you hit the stage') } sprite.mousedown = sprite.touchstart =function(data){ // stop the default event... data.originalEvent.preventDefault(); this.data = data; this.alpha = 0.9; this.dragging = true; console.log('mousedown'); } sprite.mouseup = sprite.mouseupoutside = sprite.touchend = function(data){ this.alpha = 1 this.dragging = false; // set the interaction data to null this.data = null; console.log('mouseup'); }; sprite.mousemove = sprite.touchmove = function(data){ if(this.dragging){ var newPosition = this.data.getLocalPosition(this.parent); this.position.x = newPosition.x; this.position.y = newPosition.y; console.log('mousemove') renderer.render(stage); } } //requestAnimFrame(animate); function animate(){ renderer.render(stage); requestAnimFrame(animate); }
精靈放大2倍
var sprite = new PIXI.Sprite.fromImage('bunny.png'); var sx = sprite.scale.x; var px = sprite.position.x; sprite.scale.x += 1; sprite.position.x -= px; //Y方向同理 ... //刷新一下 requestAnimFrame(animate);
精靈的層級問題
在canvas中,由於不能用css的層級樣式表來控制層級關系,所以只能另想辦法進行模擬。pixi.js中沒有提供現成的方法,不過我們研究發現,只要控制精靈加入畫布的順序就可以實現類似的層級效果。它的規則是這樣的,先加入畫布的,出現在最下面,后加入的,出現在最上面。於是自然的就想到了先用一個數組收集這些精靈,排好序之后再循環加入畫布。可是問題就來了,有時候,我們事先往往不能確定層級,或者有時候是用戶觸發某一個動作之后才改變層級的,這怎么實現呢?這個我想到了給精靈人為的加一個zindex屬性。然后遍歷這個屬性,進行排序,最后再刷新一下舞台。
/** * 對象按指定的屬性排序 * @param {array} arr 對象集合 * @return null */ function pixiSort(arr){ var ln = arr.length; var tmp; for(var i = 0;i<ln-1;i++){ for(var j=i+1;j<ln;j++){ tmp = arr[i]; if(arr[i].sortIndex > arr[j].sortIndex){ arr[i] = arr[j]; arr[j] = tmp; } } } }
受html dom操作的影響,有時候我們需要精靈按照指定的層級進行排列顯示,比如給精靈指定index屬性。要做到對zindex的控制,可以用到如下方法
function updateZindex(ob,stage){ Object.defineProperty(ob,'zIndex',{ set : function(value){ if(this.sortIndex !== value){ this.sortIndex = value; pixiSort(stage.children); } }, get : function(){ return this.sortIndex; } }); }
說白了,就是控制精靈的先后順序來達到顯示層級的效果。
最后是如何銷毀:
//移除全部精靈
stage.removeChildren() //移除整個舞台的引用
stage.removeStageReference()
以上創建的是建靜止的精靈,及在精靈上邦定事件。這個在官方沒有找到示例,特補充一下。(或許pixi就是為動畫而生的,所以沒有考慮這種簡單的需求了)
如果需要動畫效果的精靈,官方提供了多種方法。這里就不再細述了。我目前正在研究這一塊,有興趣的可以和我(@frog)一起交流!
在線資源
Pixi.js的GitHub主頁上,列舉了很多在線Demo。
GitHub:https://github.com/GoodBoyDigital/pixi.js/
官網: http://www.pixijs.com/
API :http://pixijs.download/release/docs/index.html
官方教程:https://github.com/kittykatattack/learningPixi
https://github.com/GoodBoyDigital/pixi.js
本篇為原創博文,轉載請注明出處. 278500368@qq.com