伪HTML5 Canvas 图像渐变转场效果(完全多线程)

这是一段无法运行的代码,至少我没能将它改造成能运行的代码.

浏览器会给你报这个错误:

Uncaught Error: INVALIDSTATEERR: DOM Exception 11

而且我如果没猜错的话,即使这段代码能够运行了,性能也大不如前.

Worker开启的多线程只适合处理大任务(或者在大任务中拆分成小任务),同时建立过多的线程肯定不行,而且在阻塞中将难以工作.

纪念我浪费掉的周日下午:

<!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>
    <script id='doit' type='javascript/worker'>
        this.addEventListener('message', function(e) {
            var drawData=new Uint8ClampedArray(e.data.drawData),
                tempData=new Float32Array(e.data.tempData),
                iData=new Float32Array(e.data.iData);
            function doit(i) {
                drawData[i] = tempData[i] += iData[i];
            }
            for(var i = drawData.length; i--;) {
                doit(i);
            }
            this.postMessage({
                'drawData':drawData.buffer,
                'iData': iData.buffer,
                'tempData': tempData.buffer
            }, [drawData.buffer,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 blobInit = new Blob([document.querySelector('#init').textContent], {
            'type': 'javascript/worker'
        }),
            blobDoit = new Blob([document.querySelector('#doit').textContent], {
                'type': 'javascript/worker'
            });

        var workerInit = new Worker(window.URL.createObjectURL(blobInit)),
            workerDoit = new Worker(window.URL.createObjectURL(blobDoit));

        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() {
                            if(times <= 0) {
                                callback();
                                return;
                            }
                            var f = arguments.callee;
                            var workerDoitHandler = workerDoit.addEventListener('message', function(e) {
                                drawData = e.data.drawData;
                                iData = e.data.iData;
                                tempData = e.data.tempData;
                                ctx.putImageData(draw, 0, -height);
                                times--;
                                setTimeout(f, 1000 / 30);
                                workerDoit.removeEventListener('message', workerDoitHandler, true);
                            }, true);
                            var drawDataBuffer = drawData.buffer,
                                iDataBuffer = iData.buffer,
                                tempDataBuffer = tempData.buffer;
                            var data = {
                                'drawData': drawDataBuffer,
                                'iData': iDataBuffer,
                                'tempData': tempDataBuffer
                            };
                            workerDoit.postMessage(data, [drawDataBuffer, iDataBuffer, tempDataBuffer]);
                        })();

                    }

                    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>