圖像的加載和處理
本章主要圍繞Bitmap
類展開
Bitmap
本質上是PIXI.BaseTexture
的一層 wrapper
它提供了對圖片資源的異步加載, 回調等方法
同時暴露出Canvas.context
對象來為外部使用來繪制圖形
主線 1: 加載一張 Image
加載圖片是從靜態(tài)方法Bitmap.load
入手的
Bitmap.load = function (url) {
const bitmap = Object.create(Bitmap.prototype);
bitmap.initialize();
bitmap._url = url; // ※ 設置圖片路徑
bitmap._startLoading(); // ※ 觸發(fā)加載
return bitmap;
};
Bitmap.prototype._startLoading = function () {
// 實質上相當于創(chuàng)建了一個Image標簽
this._image = new Image();
// 綁定回調事件
this._image.onload = this._onLoad.bind(this);
this._image.onerror = this._onError.bind(this);
this._destroyCanvas();
this._loadingState = 'loading';
// 和加密相關的內容
if (Utils.hasEncryptedImages()) {
this._startDecrypting();
} else {
// 通過設置url來瀏覽器就會發(fā)送異步請求
this._image.src = this._url;
}
};
// 回調事件
Bitmap.prototype._onLoad = function () {
// 和加密相關
if (Utils.hasEncryptedImages()) {
URL.revokeObjectURL(this._image.src);
}
this._loadingState = 'loaded';
// 將Image作為source創(chuàng)建BaseTexture
this._createBaseTexture(this._image);
// 激活所有監(jiān)聽中的回調函數(shù), 見下文
this._callLoadListeners();
};
Bitmap.prototype._createBaseTexture = function (source) {
// ※ 通過bitmap.baseTexture暴露給外部
this._baseTexture = new PIXI.BaseTexture(source);
this._baseTexture.mipmap = false;
this._baseTexture.width = source.width;
this._baseTexture.height = source.height;
// 更新縮放模式, 關于PIXI的具體配置就不多在本系列里闡述了
this._updateScaleMode();
};
支線 onLoad 的監(jiān)聽事件
Bitmap
提供了注冊監(jiān)聽事件的方法, 讓外部及時知道圖片已經(jīng)加載完畢
// 順便吐槽一下他們listener居然都給拼錯了
// 看起來是沒用SpellingChecker啊
Bitmap.prototype.addLoadListener = function (listner) {
// 如果尚未就緒則加入監(jiān)聽者隊列, 否則就直接觸發(fā)
if (!this.isReady()) {
this._loadListeners.push(listner);
} else {
listner(this);
}
};
主線 2: 加載 Canvas 對象
Canvas
對象一樣可以作為PIXI.BaseTexture
的來源
// 想要繪制Canvas對象, 首先要創(chuàng)建Bitmap實例
const bitmap = new Bitmap(width, height);
// bitmap的canvas會在第一次引用的時候自動創(chuàng)建
Object.defineProperty(Bitmap.prototype, 'canvas', {
get: function () {
this._ensureCanvas();
return this._canvas;
},
configurable: true,
});
Bitmap.prototype._ensureCanvas = function () {
if (!this._canvas) {
// 這里的_image是從Image.load方法加載后得到的
// 當對圖片有處理的時候, 比如對windowskin.png取色的時候
// 需要把圖片渲染到canvas上來操作
if (this._image) {
this._createCanvas(this._image.width, this._image.height);
this._context.drawImage(this._image, 0, 0);
// 否則就直接創(chuàng)建一個空窗口
} else {
this._createCanvas(0, 0);
}
}
};
// 創(chuàng)建Canvas和BaseTexture
Bitmap.prototype._createCanvas = function (width, height) {
this._canvas = document.createElement('canvas');
this._context = this._canvas.getContext('2d');
this._canvas.width = width;
this._canvas.height = height;
// 這里會覆蓋Image.load創(chuàng)建的BaseTexture
this._createBaseTexture(this._canvas);
};
主線: 操作 Canvas
Bitmap
提供了一系列canvas
繪制方法,
這里簡單得列舉一些
// 漸變填涂矩形
Bitmap.prototype.gradientFillRect = function(
x, y, width, height, color1, color2, vertical
) {
const context = this.context;
const x1 = vertical ? x : x + width;
const y1 = vertical ? y + height : y;
const grad = context.createLinearGradient(x, y, x1, y1);
grad.addColorStop(0, color1);
grad.addColorStop(1, color2);
context.save(); // 保存當前畫筆狀態(tài)
context.fillStyle = grad;
context.fillRect(x, y, width, height);
context.restore(); // 恢復畫筆狀態(tài)
this._baseTexture.update(); // ※ 繪制完成后要更新BaseTexture
};
// 圓
Bitmap.prototype.drawCircle = function (x, y, radius, color) {
const context = this.context;
context.save();
context.fillStyle = color;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2, false);
context.fill();
context.restore();
this._baseTexture.update();
};
// 文字
Bitmap.prototype.drawText = function (text, x, y, maxWidth, lineHeight, align) {
// [Note] Different browser makes different rendering with
// textBaseline == 'top'. So we use 'alphabetic' here.
const context = this.context;
const alpha = context.globalAlpha;
maxWidth = maxWidth || 0xffffffff;
let tx = x;
let ty = Math.round(y + lineHeight / 2 + this.fontSize * 0.35);
if (align === 'center') {
tx += maxWidth / 2;
}
if (align === 'right') {
tx += maxWidth;
}
context.save();
context.font = this._makeFontNameText();
context.textAlign = align;
context.textBaseline = 'alphabetic';
context.globalAlpha = 1;
this._drawTextOutline(text, tx, ty, maxWidth);
context.globalAlpha = alpha;
this._drawTextBody(text, tx, ty, maxWidth);
context.restore();
this._baseTexture.update();
};
如果熟悉canvas
的接口, 我們在這里可以比較自由得繪制任何內容.
支線: 圖片緩存
Bitmap
對象的管理是由ImageManager
來處理的
// 盡管有很多諸如:
// ImageManager.loadTitle1
// ImageManager.loadSystem
// ImageManager.loadCharacter
// 等等加載方法, 實際上本質都是調用Bitmap.load
ImageManager.loadBitmapFromUrl = function(url) {
// 注意, 這里通過判斷是否有system路徑來給圖片分了2個不同的緩存池
const cache = url.includes("/system/") ? this._system : this._cache;
if (!cache[url]) {
cache[url] = Bitmap.load(url);
}
return cache[url];
};
// 而它的清空方法僅清空._cache而不會清空._system
ImageManager.clear = function() {
const cache = this._cache;
for (const url in cache) {
cache[url].destroy();
}
this._cache = {};
};
// 而清空緩存的場合僅有一處
// 也就是在切換地圖的時候
// 說明這個緩存管理主要是用來處理地圖圖塊緩存的問題的
Scene_Map.prototype.onTransfer = function() {
ImageManager.clear();
EffectManager.clear();
};
總結
image.png