廢話不說,先上圖
圖1:轉場前
圖2:轉場中(動畫效果此處請看末尾鏈接)
圖3:轉場後
看完效果,我們來講講原理。
首先我們需要準備一張轉場用的圖。理論上所有圖片都可以作爲過渡圖使用。
我使用的是一張帶有天使翅膀圖案的圖片作爲過渡圖。
首先,將過渡圖黑白化。
var pixs = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
var data = pixs.data;
var len = data.length;
while(len -= 4){
var r = (data[len] + data[len + 1] + data[len + 2]) / 3;
data[len] = data[len + 1] = data[len + 2] = r;
}
context.putImageData(pixs, 0, 0);
(以上代碼純手寫未測試,請注意)
在進行過渡之前,我們必須往屏幕上繪製一些內容,隨便畫什麼都可以。
然後我們開始準備過渡了。
在過渡之前,我們需要定義一些變量。
// 用於保存舊的場景最後一幀
var oldSceneCanvas = document.createElement("canvas");
// 用於指示當前最多可用的不透明度值
var currentOffset = 255;
// 將過渡圖繪製在後臺canvas上,由於之前我們將它黑白化過,因此這裏假定已經在canvas上
var transitionCanvas = ...;
完成上面的操作後,我們將當前可見畫布中的內容繪製在 oldSceneCanvas 上。
然後假裝正常的把新內容繪製在可見畫布上。
之後我們將 oldSceneCanvas 的每個像素和 currentOffset進行比較,如果alpha通道大於currentOffset,則讓其等於 currentOffset的值。
將oldSceneCanvas 最後繪製在可見畫布上。
開啓一個定時器,定時將currentOffset的值減小。動畫效果就這麼出現了……
最後放上我的實現代碼,其中使用了幾個其他的東東,你可以根據運行報錯進行修改……
(function (window, document, Jyo, undefined) {
Jyo.Transition = function (renderer) {
/// <summary>轉場動畫</summary>
/// <param name="renderer" type="Jyo.Renderer">渲染器對象</param>
Jyo.Object.call(this);
this.renderer = renderer;
/*
設置過渡用畫布
*/
var osCvs = document.createElement("canvas");
var tsCvs = document.createElement("canvas");
// 當前偏移量
this.currentOffset = 255;
// 是否正在執行
this.isRun = false;
// 是否允許更新
this.canUpdate = false;
/*
設置舊場景圖畫布
*/
this.oldScreenCanvas = new Jyo.Xnb();
this.oldScreenCanvas.object = osCvs;
this.oldScreenContext = osCvs.getContext("2d");
this.oldPixelData = null;
/*
設置過渡圖畫布
*/
this.transitionCanvas = tsCvs;
this.transitionContext = tsCvs.getContext("2d");
// 過渡Alpha數組
this.transitionAlphaArray = null;
};
Jyo.Transition.prototype = new Jyo.Object({
begin: function (img, time, delay) {
/// <summary>開始轉場動畫</summary>
/// <param name="img" type="Jyo.Xnb">圖像Xnb對象</param>
/// <param name="time" type="Number">總時長(毫秒)</param>
/// <param name="delay" type="Number" optional="true">延遲時長(毫秒)</param>
var _this = this;
var renderer = this.renderer;
var osCvs = this.oldScreenCanvas.object;
var tsCvx = this.transitionCanvas;
this.oldPixelData = null;
this.transitionAlphaArray = null;
this.canUpdate = false;
this.currentOffset = 255;
this.isRun = true;
this.time = time;
// 設置畫布大小
osCvs.width = tsCvx.width = renderer.width;
osCvs.height = tsCvx.height = renderer.height;
// 繪製預設圖
this.oldScreenContext.drawImage(renderer.canvas, 0, 0);
this.transitionContext.drawImage(img, 0, 0, renderer.width, renderer.height);
/* 預先對過渡圖進行灰度計算 */
var pix = this.transitionContext.getImageData(0, 0, renderer.width, renderer.height);
var iD = pix.data;
var i = iD.length;
while ((i -= 4) > 0) {
iD[i + 3] = iD[i] * 0.3 + iD[i + 1] * 0.59 + iD[i + 2] * 0.11;
}
_this.transitionAlphaArray = iD;
if (typeof delay != "undefined") {
setTimeout(function () {
_this.canUpdate = true;
}, delay);
} else {
_this.canUpdate = true;
}
setTimeout(function () {
_this.fireEvent("begin");
}, 0);
},
draw: function () {
/// <summary>繪製過渡動畫</summary>
if (!this.isRun) return;
var renderer = this.renderer;
if (this.transitionAlphaArray) {
var osCtx = this.oldScreenContext;
var tArr = this.transitionAlphaArray;
var pix = this.oldPixelData;
if (!pix) {
this.oldPixelData = pix = osCtx.getImageData(0, 0, renderer.width, renderer.height);
}
var iD = pix.data;
var i = iD.length;
while ((i -= 4) > 0) {
if (tArr[i + 3] > this.currentOffset) {
iD[i + 3] = this.currentOffset;
}
}
osCtx.putImageData(pix, 0, 0);
}
renderer.drawImage(this.oldScreenCanvas, 0, 0);
},
update: function (currentTime) {
/// <summary>更新過渡動畫</summary>
/// <param name="currentTime" type="Number">當前時間</param>
if (!this.isRun || !this.canUpdate) return;
if (!this.beginTime) {
this.beginTime = currentTime;
return;
}
var timeSpan = currentTime - this.beginTime;
if (timeSpan < this.time) {
this.currentOffset = 255 - (timeSpan / this.time * 255) | 0;
} else {
delete this.beginTime;
this.canUpdate = false;
this.isRun = false;
this.fireEvent("end");
}
}
});
})(window, document, Jyo);