pixi.js 圖像資源(svg)轉紋理

當Pixi使用WebGL去調用GPU渲染圖像時,須要先將圖像轉化爲GPU能夠處理的版本。而可以被GPU處理的圖像就叫作紋理,在pixi中使用紋理緩存來存儲和引用全部紋理。經過將紋理分配給精靈,再將精靈添加到舞臺上,從而顯示圖像。html

圖像轉化爲紋理的方式

1. app的loader對象

Pixi強大的loader對象能夠加載任何種類的圖像資源,並保存在紋理緩存中。後續若是須要繼續獲取紋理,就不用再重複加載圖像,直接從緩存中獲取便可,減輕GPU內存佔用。git

app.loader
  .add("imgs/1.jpg")
  .load(setup);

function setup() {
  //This code will run when the loader has finished loading the image
  let sprite = new PIXI.Sprite(app.loader.resources["imgs/1.jpg"].texture);
  app.stage.add(spirte)
}
2. Pixi的Texture類型

Pixi的Texture類型,實現了加載圖像的靜態方法。
static from(source: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | PIXI.BaseTexture, options?: any, strict?: boolean): PIXI.Texture;github

從容許的參數類型能夠看出,咱們能夠經過直接傳入圖像地址、img標籤、canvas標籤、video標籤,將圖像轉化爲紋理。canvas

  • 經過圖像連接加載
var texture = PIXI.Texture.from("imgs/1.jpg");
var sprite = new PIXI.Sprite(PIXI.utils.TextureCache["imgs/1.jpg"]);
app.stage.addChild(sprite);

根據連接加載圖像,更推薦這種方式,能夠捕獲到圖像加載失敗緩存

ps:其實fromURL(),內部調用的仍是from()app

PIXI.Texture.fromURL(data)
.then((texture) => {
    const spirte = new PIXI.Sprite(texture);
    app.stage.addChild(spirte);
})
.catch((e) => {
    console.log("load error", e);
});
  • 經過標籤加載
var img = document.getElementById("img");
var texture = PIXI.Texture.from(img);
var sprite = new PIXI.Sprite(texture)
app.stage.addChild(sprite)

將SVG Dom節點轉爲紋理

若是隻是單純的把svg做爲一個單獨的外部文件,其實只要按照上面兩種轉換紋理的方式,傳入svg圖像連接就能夠實現了。可是若是這個svg是在同一個html頁上的dom節點呢?這時候還能將其轉爲紋理嗎?答案是能夠的。dom

注意觀察Texture.from()的參數,能夠傳入圖像的連接。那麼base64編碼後的圖像地址,按理來講也能夠。因此只要將頁面上的svg節點,轉化爲base64編碼便可。ide

function getSvgBase64(id) {
    var svg = document.getElementById(id)
    return "data:image/svg+xml;base64," +  window.btoa(svg.outerHTML);
}

關鍵代碼:window.btoa() 建立一個base64編碼的字符串,解碼方法 window.atob()svg

源碼解析

首先,從Texture.from()開始入手,咱們具體看看pixi是如何加載圖像紋理的。函數

在from方法中有這麼一句話texture = new Texture(new BaseTexture(source, options));。全部的Texture對應的還有一個BaseTexture,他們之間的關係能夠這麼解釋

BaseTexture : The base texture source to create the texture from

接下來看一下 BaseTexture類的構造函數,其中調用了autoDetectResource()方法,在這個方法中真正的對資源進行了檢測分類,並根據不一樣類型的資源調用不一樣的資源插件(ResourcePlugin)。

function autoDetectResource(source: unknown, options?: IAutoDetectOptions): Resource
{
    if (!source)
    {
        return null;
    }

    let extension = '';
    if (typeof source === 'string')
    {
        // search for file extension: period, 3-4 chars, then ?, # or EOL
        const result = (/\.(\w{3,4})(?:$|\?|#)/i).exec(source);
        if (result)
        {
            extension = result[1].toLowerCase();
        }
    }

    for (let i = INSTALLED.length - 1; i >= 0; --i)
    {
        const ResourcePlugin = INSTALLED[i];
        if (ResourcePlugin.test && ResourcePlugin.test(source, extension))
        {
            return new ResourcePlugin(source, options);
        }
    }
    throw new Error('Unrecognized source type to auto-detect Resource');
}

INSTALLED在index.ts中已經初始化注入全部的ResourcePlugin

INSTALLED.push(
    ImageResource,
    ImageBitmapResource,
    CanvasResource,
    VideoResource,
    SVGResource,
    BufferResource,
    CubeResource,
    ArrayResource
);

在這裏能夠看到,pixi中有一個SVGResource,咱們就以這個爲例繼續深刻看下內部的處理機制。

簡化版SVGResource類:

export class SVGResource extends BaseImageResource
{
    constructor(sourceBase64: string, options?: ISVGResourceOptions)
    {
        //...
        super(document.createElement('canvas'));
        if (options.autoLoad !== false)
        {
            this.load();
        }
    }

    load(): Promise<SVGResource>
    {
        // Convert SVG inline string to data-uri
        if ((/^\<svg/).test(this.svg.trim()))
        {
            if (!btoa)
            {
                throw new Error('Your browser doesn\'t support base64 conversions.');
            }
            (this as any).svg = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(this.svg)))}`;
        }

        this._loadSvg();
        return this._load;
    }

    /**
     * Loads an SVG image from `imageUrl` or `data URL`.
     *
     * @private
     */
    private _loadSvg(): void
    {
        const tempImage = new Image();

        BaseImageResource.crossOrigin(tempImage, this.svg, this._crossorigin);
        tempImage.src = this.svg;//將base64編碼的Svg字符串,建立爲Image對象

        tempImage.onload = (): void =>
        {
            // Draw the Svg to the canvas
            canvas
                .getContext('2d')
                .drawImage(tempImage, 0, 0, svgWidth, svgHeight, 0, 0, width, height);

        };
    }

    static test(source: unknown, extension?: string): boolean
    {
        // url file extension is SVG
        return extension === 'svg'
            // source is SVG data-uri
            || (typeof source === 'string' && (/^data:image\/svg\+xml(;(charset=utf8|utf8))?;base64/).test(source))
            // source is SVG inline
            || (typeof source === 'string' && source.indexOf('<svg') === 0);
    }

}

看完這裏就差很少明白了,對於傳入的Source來講,當在autoDetectResource()中經過test()方法檢測到資源爲SVG格式後,將其轉換爲Base64字符串(也就是說直接傳入拼接好的svg字符串也是能夠被解析的~),而後再load爲Image對象,加載到臨時canvas中。最後再經過BaseImageResourceupload()調用GPU輸出圖像資源。

總結

一步步瞭解一個東西過程仍是頗有意思的,每走一步都會有新的發現。事實上仍是有不少東西沒有搞懂,就留着之後去發現了。

相關文章
相關標籤/搜索