iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 21
0
Modern Web

再談 PixiJS,那些先前不一定有提到的部分與地雷系列 第 21

[Re:PixiJS - Day21] 畫漸層填色、材質重複填滿會遇到的問題

填入漸層感覺不難,
Canvas 有方法可以用: HTML Canvas Gradients

CSS 也有方法可以用: CSS Gradients

但用 PixiJS 實作卻 有點麻煩


PixiJS範例 裡與填入漸層的範例有兩個:


實作方式差不多,都是透過 canvas2d gradient API 來繪製:

function createGradTexture() {
    const quality = 256;
    const canvas = document.createElement('canvas');
    canvas.width = quality;
    canvas.height = 1;

    const ctx = canvas.getContext('2d');
    
    // use canvas2d API to create gradient
    const grd = ctx.createLinearGradient(0, 0, quality, 0);
    grd.addColorStop(0, 'rgba(255, 255, 255, 0.0)');
    grd.addColorStop(1, 'green');

    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, quality, 1);

    return PIXI.Texture.from(canvas);
};

原理是把漸層畫在 Canvas 上,再把 Canvas 當成 TextureSprite實體 使用

會有的問題:

前篇 提到的 PIXI.Text 特性相同,每生成一次就會產生快取
也就是說:每一次畫漸層產生 Texture 時,就會產生 TextureCacheBaseTextureCache
大量使用時且沒刪除時,可能會因為記憶體不足而讓 頁面當掉


前半部心得:

與前天相同,在專案較大、考慮 效能記憶體優化 時需注意:

  • 是否產生一次性的素材、文字等,並且在不使用後,移除對應的 Texture / BaseTexture

今日後半段較深,且有部分為實驗用說明,可參考就好

不小心沒指定寬高時:

先前在實作時,某次在使用 漸層 CanvasTexture 時,少寫了 Canvas寬高

const gradTexture = createGradTexture();
const sprite = new PIXI.Sprite(gradTexture);

function createGradTexture() {
    const canvas = document.createElement('canvas');
    // canvas.width = 100; // 忘了指定
    // canvas.height = 100; // 忘了指定

    const ctx = canvas.getContext('2d');

    const grd = ctx.createLinearGradient(0, 0, 0, 100);
    grd.addColorStop(0, 'red');
    grd.addColorStop(1, '#ffffff');

    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, 100, 100);

    return PIXI.Texture.from(canvas);
};

const gradTexture = createGradTexture();

Sprite實體 裡放入沒指定寬高的 Texture - 沒有問題,[ Demo ]:


Graphics類別 有一個叫做 beginTextureFill() 的方法,可填入 Texture
實驗: 將剛剛忘了指定寬高的 Texture 拿來填
預期: Graphics實體 裡會填滿方塊,但是不知道是下列哪種結果?

  • 選項1: 放大
  • 選項2: 重複
  • 選項3: 不放大且不重複,只顯示一個

提示: 用來當作材值的 Canvas 沒有設定寬高
好吃驚時間,答案會是誰?

答案:

  • PC: 選項2 - 重複
  • 手機: 選項3 - 不放大且不重複,只顯示一個

[ Demo:使用目前最新的 5.3.3 版 PixiJS ]
PC:

手機:

const testGraphic = new PIXI.Graphics();

testGraphic.beginTextureFill({texture: createGradTexture()});
testGraphic.drawRect(0, 0, 600, 600);
testGraphic.endFill();

誒? 沒指定寬高時,不重複是合理的。那 PC 為什麼會重複?


指定寬高後,又更奇怪了:[ Demo ]

PC - WebGL 2

手機 - WebGL 1

這邊我列多列出了一件事: WebGL2WebGL1
實際這個問題確實就是 WebGL2WebGL1 的差異

eXponenta:
U tried repeat nPOT textures, that supports only in WebGL 2, IE doesn't support WebGL 2.
Same problem on mobile, because pixi force webgl 1 on it.

參考討論:PIXI.Graphics.beginTextureFill does not work in IE11 #6422
這篇為 2020/02 的討論,目前也非所有裝置皆支援 WebGL2
建議先不要使用 Texture 重複,可能會在不同裝置上有不同問題


後半結論:
Canvas 上繪圖當成 Texture 來使用時,
1: 先指定好要使用的畫布大小,
2: 一次畫完最後需要的大小與結果,不在 PixiJS 裡使用 Texture 重複
可避免這個問題


補充資料:POT & NPOT


上一篇
[Re:PixiJS - Day20] 用 tint() 方法填色時,原物件需為白色或灰階
下一篇
[Re:PixiJS - Day22] 畫漸層線段、材質問題
系列文
再談 PixiJS,那些先前不一定有提到的部分與地雷45

尚未有邦友留言

立即登入留言