zwwcn

Just another WordPress.com site

Category Archives: coffescript

Javascript drawing on an image inside a canvas

I just finished a task: load an image inside a popup canvas then draw lines on the canvas and save the image data. I had a couple issues during the development, but luckily I managed to find answers on google.

  1. How to draw on canvas on mobile devices.
    I found a good article which give all the details Using Touch Events with the HTML5 Canvas.  You should be aware of the difference code for IOS,Android and canvas in browser. The touch devices use “touchmove”,”touchstart” while the browswer uses “mousemove”,”mousedown”..etc
  2. We need to stop page scrolling when user are ready for drawing.
    stopScroll = (ev) ->
      ev.preventDefault();
    
    $page.on("popupbeforeposition","#popupPanel", (event,ui) ->
     $('body').bind('touchmove',stopScroll);
    )
    $page.on("popupafterclose","#popupPanel",(event,ui) ->
       $('body').unbind('touchmove',stopScroll);
    )
    
  3. How to improve image quality.
    When loading the photo to canvas, you may get a blurry image. The following code improve the quality of image

    canvastx = popupCanvas.getContext("2d");
    canvastx.webkitImageSmoothingEnabled = false;
    canvastx.mozImageSmoothingEnabled = false;
    canvastx.imageSmoothingEnabled = false;
    
  4. Don’t resize canvas, you should always create new one.
    Make sure you create new canvas after reading the image info. This way you know the image demision, then you could create new canvas with the same width/height ratio.  If you draw on a resized canvas,  everything(x,y point) on that canvas will be scaled.  You will have to do some calculation to get the orginal point. That’s why I prefer creating a new canvas with the right size.

Multiple Canvas only draw the last one

I have five canvas on the page, The following code is supposed to read the image data from hidden input field and draw image to the correspoding canvas. but it only draw the last one.

$('.signatureField').each(function() {					   
	if($(this).children("input").val()!=""){
		canvas = $(this).children('canvas').get(0);
		ctx = canvas.getContext('2d');
		testImage = new Image();

		testImage.onload=function(){				
			ctx.drawImage(testImage,0,0,testImage.width,testImage.height,0,0,canvas.width,canvas.height);
		}
						
		testImage.src="data:image/png;base64,"+$(this).children("input").val();
	}						
})

Adding “var” to ctx solves the problem.What that does is to declare ctx as local variable in each loop, so they don’t share the same convas context.

$('.signatureField').each(function() {					   
	if($(this).children("input").val()!=""){
		var canvas = $(this).children('canvas').get(0);
		var ctx = canvas.getContext('2d');
		var testImage = new Image();

		testImage.onload=function(){				
			ctx.drawImage(testImage,0,0,testImage.width,testImage.height,0,0,canvas.width,canvas.height);
		}
						
		testImage.src="data:image/png;base64,"+$(this).children("input").val();
	}
})

Then I have the following code in CoffeeScript, unfortunately, it only draw the last canvas. Here I use apostrophe to declare canvas context as local variable. Also added an empty return at the end of the function to avoid CoffeeScript implicit return.

checklist.loadExistingSignature = (fields) ->
    for field in fields
        if field.type='signature' and field.value.length>0
            `var fieldCanvas = $('#' + field.id).get(0)`
            `var canvastx = fieldCanvas.getContext('2d')`
            `var testImage = new Image()`
            
            testImage.onload = ->
            	canvastx.drawImage(testImage,0,0,testImage.width,testImage.height,0,0,fieldCanvas.width,fieldCanvas.height);
            	return
            testImage.src = "data:image/png;base64," + field.value;
    return

To solve this problem, we need to add a javascript closure. In CoffeeScript , it’s the do function

checklist.loadExistingSignature = (fields) ->
    for field in fields
        if field.type='signature' and field.value.length>0
            `var fieldCanvas = $('#' + field.id).get(0)`
            `var canvastx = fieldCanvas.getContext('2d')`
            `var testImage = new Image()`
            do (canvastx, testImage) ->
            	testImage.onload = ->
            		canvastx.drawImage(testImage,0,0,testImage.width,testImage.height,0,0,fieldCanvas.width,fieldCanvas.height);
            		return
            	return
            testImage.src = "data:image/png;base64," + field.value;
    return

And the javascript version:

for (var i = 1; i < 5; i++) {
    var ctx = document.getElementById('canvas-' + i).getContext('2d'),
        image = new Image();
    (function(ctx, image) {
        image.onload = function() {
            ctx.drawImage(image, 0, 0);
        }
        image.src = images[(i - 1)];
    })(ctx, image);
}

ref for javascript closure in loop: http://www.mennovanslooten.nl/blog/post/62