Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How I Built an HTML5 Comic Book Reader

DZone's Guide to

How I Built an HTML5 Comic Book Reader

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Following up on my Sunday blog post on comics, I thought it would be fun to share a little experiment I built this weekend. Comic books are available in a compressed format typically called CBRs or CBZs. These aren't a special format, just simple compressed archives. CBRs are RAR files and CBZs are zips. While there doesn't appear to be good support for RAR files (I've only found a Java library to list the contents), the Zip format is much more widely used and easy to work with. In fact, you can find an excellent JavaScript implementation: zip.js I thought it might be fun to try using that to build my own web-based CBZ reader. Here's how I did it.

First, I added drag/drop support to my application so that users could simply drag in their local CBZ files. Instead of a div, I made the entire page a target for drop events:

$(document).on("drop", dropHandler);

The dropHandler needs to do a few things. First - it needs to figure out the type of drop. Don't forget that people can drag/drop blocks of text from other applications. What I want is to listen for files. Even better - I need to ensure that one file is dropped, not multiple. Here's the snippet for that logic.

function dropHandler(e) {
e.stopPropagation();
e.preventDefault();

if(!e.originalEvent.dataTransfer.files) return;
var files = e.originalEvent.dataTransfer.files;
var count = files.length;
 
  if(!count) return;

  //Only one file allowed
  if(count > 1) {
  doError("You may only drop one file.");
  return;
  }

  handleFile(files[0]);
 }

 Ok, now for the fun part. My application needs to try to decompress the zip file to the file system. In order to do that I am making use of the HTML5 File API. Earlier on I did a quick request for some temporary file storage - which is pretty simple:

window.webkitStorageInfo.requestQuota(window.TEMPORARY, 20*1024*1024, function(grantedBytes) {
window.webkitRequestFileSystem(window.TEMPORARY, grantedBytes, onInitFs, errorHandler);
}, errorHandler);

Having the file system means I can extract the images out of the zip into the directory and refer to them later. We have access to the file from the drop event, so it is a simple matter of:

  • Pass the file data to zip.js
  • Extract the files
  • Save the files (again, this is a temporary file system)
  • Store a reference to them so I can display them to the user

Here is the function that handles all of that.

function handleFile(file) {
	zip.workerScriptsPath = "js/";

	zip.createReader(new zip.BlobReader(file), function(reader) {
		console.log("did create reader");
	    reader.getEntries(function(entries) {
	    	console.log("got entries");
	    	
			$("#introText").hide();

	    	//Start a modal for our status
	    	var modalString = 'Parsed the CBZ - Saving Images. This takes a <b>long</b> time!';
	    	$("#statusModalText").html(modalString);
			$("#statusModal").modal({keyboard:false});

	        entries.forEach(function(entry) {
	        	if(!entry.directory && entry.filename.indexOf(".jpg") != -1) {
	        		//rewrite w/o a path
	        		var cleanName = entry.filename;
	        		if(cleanName.indexOf("/") >= 0) cleanName = cleanName.split("/").pop();
					dir.getFile(cleanName, {create:true}, function(file) {
						console.log("Yes, I opened "+file.fullPath);
		        		images.push({path:file.toURL(), loaded:false})
						entry.getData(new zip.FileWriter(file), function(e) {

							done++;
							//$("#statusModalText").html("Did "+done+" images out of "+images.length);
							var perc = Math.floor(done/images.length*100);
							var pString = 'Processing images.';
							pString += '<div class="progress progress-striped active">';
							pString += '<div class="bar" style="width: '+perc+'%;"></div>';
							pString += '</div>';
							$("#statusModalText").html(pString);

							for(var i=0; i<images.length; i++) {
								if(images[i].path == file.toURL()) {
									images[i].loaded = true; 
									break;
								}								
							}

							if(done == images.length) {
								$("#statusModal").modal("hide");
								//enable buttons
								$("#buttonArea").show();
								$("#prevBtn").on("click",prevPanel);
								$("#nextBtn").on("click",nextPanel);
								drawPanel(0);
							}
						});

					},errorHandler);

		        }
	        });
	    });
	}, function(err) {
		doError("Sorry, but unable to read this as a CBR file.");
	    console.dir(err);
	});

}

Once done - that leaves us with the simple job of providing basic interaction with the images. This is done via buttons that allow for navigation.

function drawPanel(num) {
	curPanel = num;
	$("#comicImg").attr("src",images[num].path);
	$("#panelCount").html("Panel "+(curPanel+1)+" out of "+images.length);
}

function prevPanel() {
	if(curPanel > 0) drawPanel(curPanel-1);
}

function nextPanel() {
	if(curPanel+1 < images.length) drawPanel(curPanel+1);
}

And that's it! Before I link to the demo, I'll warn you that this is not very tolerant of browsers that don't support everything required. Here are a few screen shots though to give you an idea of how it works.

First up - the application as it looks on loading.

Next - I drag a CBZ file over it...

And then it gets to work. Now - this part can be a bit slow. To be fair, I dragged a 35 megabyte file into the browser and it took about 40 seconds to parse. I think that's fairly decent for JavaScript.

I also provide UI feedback as the images are saved.

And then finally - the comic is readable. (Whether or not the story is any good is another question.)

Want to try it out? Hit the demo link below. Note that you may want to try with the latest Chrome and with a small comic. I've created a simple "comic" out of a zip of pictures that can be downloaded here.

 

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}