I am using a 3rd party Javascript to render TIFFs in all browsers (only safari supports them natively) This library works by printing the image to a HTML 5 canvas (no img tag involved)
Ajax is used and once the image is available there is a noticeable pause while the canvas renders. To cover this up I put in a placeholder image which is an animated gif. In addition to this animated gif I have used greensock.js and css3 transforms to make the loading gif zoom in from 0 scale and once the main image is ready zoom back out to 0 scale before the main image appears.
The problem is that there is always a moment when the animation pauses (both the greensock/css powered animation and the animated gif).
I thought I could solve this by moving whatever was causing it to a webworker thread. However after much experimenting (by removing parts of the code in isolation or where that is not possible by stopping all animation before a specific piece of code runs) I have discovered that the line of code which renders the canvas is what causes it.
var canvas = tiff.toCanvas();
Unfortunately I cannot move this to webworker because it is a DOM manipulation. I am also unable to render the image/canvas content (without making a canvas element itself) on the webworker and then paste it into the canvas element from the main thread because the library I am using does not provide such functionality.
The best I could do is put the code which runs AJAX and receives the image in the webworker and then return the raw image data to the main thread to do the rest.
I have tried a JS library which provides a virtual DOM but this just resulted in a dependency hell which I never got to the bottom of.
In the end I settled with having the zoom out animation occur as soon as the web worker returns the data containing the image instead of after rendering completes. This prevents any visible pausing of the animation but it means after the loading image disappears there is a pause (while the canvas is rendering) before the image appears.
My code is below. Any ideas how I can solve this?
Main thread JS
var worker = new Worker('/javascript/task.js');
worker.addEventListener('message', function(e) {
TweenMax.to(placeHolderImage,0.5,{scale:0, transformOrigin:"centre",onComplete:function(){
document.getElementById("pic").removeChild(placeHolderImage);
var tiff = new Tiff({buffer: e.data});
var canvas = tiff.toCanvas();
document.getElementById("pic").appendChild(canvas);
canvas.className = "canvasReady";
TweenMax.from(canvas,0.2,{rotationY:90, transformOrigin:"left"})
}}
);
}, false);
//Call the worker and send the cover image URL to it.
var src = /*[[${CoverImageURL}]]*/;
worker.postMessage(src);
Worker thread JS
self.addEventListener('message', function(e) {
importScripts("tiff.js-master/tiff.min.js");
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', e.data);
xhr.onload = function (e) {
self.postMessage(xhr.response);
}
xhr.send();
}, false);
You are correct that you cannot manipulate the document from the web worker. You must find a way to do the majority of the conversion in the web worker, and then only pass the converted data back to the main thread.
This demo from the tiff.js library appears to be doing exactly what you want. The worker it uses explicitly avoids using tiff.toCanvas()
. The message they pass to the worker includes the total memory to use as well as the URL of the image. They then use tiff.readRGBAImage()
to get the data, and post info back to the main thread for the actual rendering.
Note also the call made in the worker to pass the data back to the main thread:
var image = tiff.readRGBAImage();
self.postMessage({ image: image, width: tiff.width(), height: tiff.height() }, [image]);
It passes image
in an array as a second parameter to take advantage of the array's implementation of the Transferrable interface. Long story short, this means the data is not copied between the worker thread and the main thread. For potentially large data sets (like images), transferring the data instead of copying the data is much more efficient.