HTML5 Canvas 图像渐变转场效果(多线程初始化)

这是HTML5 Canvas 图像渐变转场效果的另一个版本,使用HTML5 Worker进行初始化.

初始化内容包括插值和图像原始数据,理论上比原版占用更多的内存.

初始化后的效果并不理想,原版初始化为200ms左右,使用Worker后增加到了800ms,耗费时间增长了4倍.

动画时耗费时间下降1~2ms左右,几乎没有改善性能.

在线DEMO(位于Google Drive,可能无法访问,示例载入的图片较大,请耐心等待).

代码如下:

<!DOCTYPE html>  
<html>  
<head>  
    <title>Image transition transition effects with Worker</title>
    <script id='init' type='javascript/worker'>
        this.addEventListener('message', function(e) {
            var img1Data = new Uint8ClampedArray(e.data.img1Data),
                img2Data = new Uint8ClampedArray(e.data.img2Data),
                times = e.data.times;

            var length = img1Data.length > img2Data.length ? img1Data.length : img2Data.length;

            var iData = new Float32Array(length),
                tempData = new Float32Array(length);
            for(var i = length; i--;) {
                iData[i] = (img2Data[i] - img1Data[i]) / times;
                tempData[i] = img1Data[i];
            }
            this.postMessage({
                'iData': iData.buffer,
                'tempData': tempData.buffer,
                'i': e.data.i
            }, [iData.buffer, tempData.buffer]);
        }, true);
    </script>
</head>  
<body>  
    <canvas width='850' height='240'></canvas>
    <script>
        var imgList = ['nav_event.png',
                       'nav_twitter.png',
                       'nav_banner.png',
                       'nav_drama06.png',
                       'nav_sound.png',
                       'nav_movie.png',
                       'nav_leaflet.png',
                       'nav_trial.png',
                       'nav_count12.png'];

        var blob = new Blob([document.querySelector('#init').textContent], {
            'type': 'javascript/worker'
        });

        var workerInit = new Worker(window.URL.createObjectURL(blob));

        function loaded() {
            var canvas = document.querySelector('canvas'),
                ctx = canvas.getContext('2d');
            var width = canvas.width,
                height = canvas.height;

            var tempCanvas = document.createElement('canvas'),
                tempCtx = tempCanvas.getContext('2d');
            tempCanvas.width = width;
            tempCanvas.height = height * 2;
            tempWidth = tempCanvas.width, tempHeight = tempCanvas.height;

            var times = 30;
            var iDataList = [],
                tempDataList = [];
            var compeleted = 0;

            for(var i = 0; i < imgList.length; i++) {
                tempCtx.drawImage(imgList[i], 0, 0);
                var img1Data = tempCtx.getImageData(0, 0, tempWidth, tempHeight).data;
                var img1Buffer = img1Data.buffer;

                tempCtx.drawImage(imgList[i + 1] || imgList[0], 0, 0);
                var img2Data = tempCtx.getImageData(0, 0, tempWidth, tempHeight).data;

                var length = img1Data.length > img2Data.length ? img1Data.length : img2Data.length;

                var img1Buffer = new ArrayBuffer(length),
                    img2Buffer = new ArrayBuffer(length);

                var img1BufferView = new Uint8ClampedArray(img1Buffer),
                    img2BufferView = new Uint8ClampedArray(img2Buffer);

                for(var j = length; j--;) {
                    img1BufferView[j] = img1Data[j];
                    img2BufferView[j] = img2Data[j];
                }

                function run() {
                    function animation(index, callback) {
                        var times = 30;
                        tempCtx.drawImage(imgList[index], 0, 0);
                        var draw = tempCtx.getImageData(0, 0, tempWidth, tempHeight);
                        var drawData = draw.data;

                        tempCtx.clearRect(0, 0, width, height);

                        var iData = iDataList[index],
                            tempData = new Float32Array(drawData.length);

                        for(var i = drawData.length; i--;) {
                            tempData[i] = tempDataList[index][i];
                        }

                        function doit(i) {
                            drawData[i] = tempData[i] += iData[i];
                        }

                        (function() {
                            if(times <= 0) {
                                callback();
                                return;
                            }
                            for(var i = drawData.length; i--;) {
                                doit(i);
                            }
                            ctx.putImageData(draw, 0, -height);
                            times--;
                            setTimeout(arguments.callee, 1000 / 30);
                        })();
                    }

                    var i = 0;
                    (function() {
                        if(i < imgList.length) {
                            animation(i++, arguments.callee);
                        } else {
                            animation(i = 0, arguments.callee);
                            i++;
                        }
                    })();
                }

                var workerInitHandler = workerInit.addEventListener('message', function(e) {
                    var index = e.data.i;
                    iDataList[index] = new Float32Array(e.data.iData);
                    tempDataList[index] = new Float32Array(e.data.tempData);
                    compeleted++;
                    if(compeleted == imgList.length) {
                        run();
                    }
                    workerInit.removeEventListener('message', workerInitHandler, true);
                }, true);
                var data = {
                    'img1Data': img1BufferView.buffer,
                    'img2Data': img2BufferView.buffer,
                    'times': times,
                    'i': i
                };
                workerInit.postMessage(data, [img1BufferView.buffer, img2BufferView.buffer]);
            }

        }

        var imgLoaded = 0;
        var onload = function() {
                imgLoaded++;
                if(imgLoaded == imgList.length) {
                    loaded();
                };
            }
        for(var i = imgList.length; i--;) {
            var img = new Image();
            img.addEventListener('load', onload)
            img.src = imgList[i];
            imgList[i] = img;
        }
    </script>
</body>  
</html>