Tag Archives: HTML5

Diffing Two Canvases

03 Sep 2016

Below is the script I wrote that will take two canvases and find the differences between them – a canvas diff. What it returns is the bounding area – upper left coordinates and lower-right coordinates – with which you can do as you like.

The challenge here is iterating through the pixel data. The data itself is an array of each pixel’s RGBA values in sequence. For example, if we are looking at four pixels then the pixel array representing them would have a length of 16 (RGBA = 4 array elements x the number of pixels = 16). This imaginary array could look like:

  • 0,0,122,5,100,12,123,6,16,100,43,123,55,55,100,50

With a little formatting to help make it make more sense we can easily see the groupings:

  • 0,0,122,5,    100,12,123,6,    16,100,43,123,    55,55,100,50

Looking at the first group of 4 numbers we can see that the pixel they represent has these RGBA values:

  • R: 0
  • G: 0
  • B: 122
  • A: 5

Imagine a 1024×768 image represented by a single data array of RGBA values. Such an image would have a data array length of 3,145,728 (1024 x 768 x 4). In order to manipulate the pixel data you’d have to discover a way of looping through the array keeping in mind that every group of 4 array elements is itself a single pixel. You would also need to realize that any given group of array elements represents a specific pixel somewhere within your image/canvas.

Image Comparison Example

In the example shown here I’m comparing the pixel data between two images, keeping track of the differences, and returning a bounding area that encompasses them.

This example is in an iframe – view source to see the script that loads the images into image objects, then loads those images into two canvas elements, extracts the image data from the canvases, compares them, and then finally draws the red bounding boxes to illustrate where the images diverge from each other.

Diff Function – Comparing Two Canvases

The function I wrote below compares all the pixels and notes the coordinates of the ones that don’t match. I then use the sort array method to sort the resulting “diff” array by either the X or Y of each diff’d coordinate so that I can find the extremes of each one.

While I’m looking for the differences I am also keeping track of the X and Y coordinate representing each RGBA quadruplet. Note the use of the modulus operator as that is what makes the coordinate-tracking work.

To use this function all you have to do is create two image data objects using the canvas getImageData() method then pass them to the canvasDiff function where the first data object is the original image and the second is the one that has changed. Refer to the iframed example above – view source to see how the diff function seen below was used within the example to produce the bounding differential boxes.

When using images as the source data they need to be of identical size and ideally in PNG format. PNG is optimal because JPG is a lossy compression algorithm that will make it hard to do a legit diff – provided you are using images at all – you could easily just have programmatically-generated canvas art – point is that canvasDiff needs two image data objects where the images are the same physical dimensions.

function canvasDiff(imageData1,imageData2){
    // www.rickluna.com - please leave the attribution!
    var w = imageData1.width;
    var h = imageData1.height;
    var diffs = [];
    var start = {x:null,y:null};
    var end   = {x:null,y:null};
    var pA1_r,pA1_g,pA1_b,pA1_a,
	pA2_r,pA2_g,pA2_b,pA2_a;
    var y = 0;
    var x = 0;
var len = imageData1.data.length;
    for (var i=0;i b.x){
	    return 1;
	} else {
	    return 0;
	}
    });
    start.x = diffs[0].x || 0;
    end.x = diffs[diffs.length-1].x || w;
    diffs.sort(function(a,b){
	if (a.y < b.y){
	    return -1;
	} else if (a.y > b.y){
	    return 1;
	} else {
	    return 0;
	}
    });
    start.y = diffs[0].y || 0;
    end.y = diffs[diffs.length-1].y || h;

    // DONE
    // "start" and "end" have the bounding coordinates
    console.log(start,end);
    return [start,end];
}

HTML5 Canvas and Particle Systems

13 Mar 2015

I’ve been doing a lot of canvas stuff lately which reminded me of some things that I’ve always wanted to try. In particular I’ve always meant to find time to try writing a particle system using HTML5 Canvas.

Its pretty easy to do – the idea is that we render a shape or a number of shapes to a canvas using window.requestAnimationFrame to recalculate the positions of the particles it each iteration. Before each render to the canvas we wipe the canvas clean then render the shapes to their new locations. That’s all there is to it.

I wrote two experiments at creating a particle system – in one I had the particles take care of themselves separate from any thing else – they essentially moved themselves around the canvas. The second attempt has a system that updates all of the new particle coordinates before rendering them all to the canvas. There are some subtle differences and effects that can be achieved. In almost all cases the second “style” of updating the canvas is preferred.

Before we go any further I’m assuming that you are using a modern web browser – I’ve not bothered with supporting lesser browsers.

Experiment One

The methodology in this one is that each particle takes care of itself. That is, it calculates its own position within the canvas and writes itself to the canvas regardless of whatever else might be happening. The caveat here is that we always need to remove the previously rendered shape before plotting the new one else we end up drawing lines on the screen.

You might think that this would be easy to do as we always know the coordinate of the previous shape and can simply erase it. Shapes, however, are anti-aliased. The outer-most anti-aliased edge of the shape (a “ghost”) is always left behind when we attempt to erase only the portion of the canvas where the previously plotted shape was. You can enlarge the bounding area of the shape to be sure to remove all of it but then you see “empty borders” around shapes as they cross each other.

The point is that even though this looks cool its impractical for most purposes.

The first example doesn’t bother to erase the previously plotted shape. As a result we have a series lines – but lines that have opacity and compositing so that we end up with something cool.

The example on the right does attempt to erase the previously plotted shape but as I mentioned above you can still see the “ghost” of that previous shape which leaves a sort of trail behind it as it moves about the screen.

Experiment Two

This one approaches Canvas animation the way its usually done. First calculate the new position of all shapes, wipe the entire canvas clean, and then write all the shapes to the canvas, repeat.




I wont go through any exhaustive description of how to do things – the described workflow above and the source code below should be all that you need to give it a try for yourself.

;(function(ns){
	
	var _parts = [];
	var _cvs = null;
	var _ctx = null;
	var _bgColor = null;
	
	ns.setupParts = function(cvsID,bgColor){
		_cvs = document.getElementById(cvsID);
		_ctx = _cvs.getContext('2d');
		_bgColor = bgColor;
	}
	
	ns.addParts = function(o){
		_parts.push(o);
	}
	
	ns.updateCanvasWithParts = function(){
		_ctx.clearRect(0,0,_cvs.width,_cvs.height);
		if (_bgColor){
			_ctx.fillStyle = _bgColor;
			_ctx.fillRect(0,0,_cvs.width,_cvs.height);
		}
		for (var i=0;i<_parts.length;i++){
			_ctx.fillStyle = _parts[i].color;
			_ctx.globalCompositeOperation = _parts[i].comp;
			_ctx.globalAlpha = _parts[i].alpha;
			_ctx.fillRect(_parts[i].x, _parts[i].y,_parts[i].height,_parts[i].width);
			_parts[i].update();
		}
		requestAnimationFrame(ns.updateCanvasWithParts);
	}
	
	ns.particle = function(config){
		var that = this;
		this.vx = config.omni ? (Math.random() < 0.5 ? config.vx * -1: config.vx) : config.vx;
		this.vy = config.omni ? (Math.random() < 0.5 ? config.vy * -1: config.vy) : config.vy;
		this.x = config.x;
		this.y = config.y;
		this.originX = config.x;
		this.originY = config.y;
		this.starfield = config.starfield;
		this.color = config.color;
		this.bgColor = config.bgColor;
		this.alpha = config.alpha;
		this.comp = config.comp;
		this.size = config.size;
		this.height = config.uniform ? config.size : Math.round(Math.random() * config.size);
		this.width = config.uniform ? config.size : Math.round(Math.random() * config.size);
		this.update = function(){
			if (!that.starfield){
				if (that.x > _cvs.height - that.height){
					that.vx = that.vx * -1;
				} else if (that.x < 0){
					that.vx = Math.abs(that.vx);
				}
				if (that.y > _cvs.width - that.width){
					that.vy = that.vy * -1;
				} else if (that.y < 0){
					that.vy = Math.abs(that.vy);
				}
			} else {
				if (that.x > _cvs.height + that.size || that.y > _cvs.width + that.size ||
					that.x < -that.size || that.y < -that.size){
					that.x = that.originX;
					that.y = that.originY;
				}
			}				
			that.x = that.x + that.vx;
			that.y = that.y + that.vy;
		}
	}

})(this.particles2 = this.particles2 || {});

document.addEventListener('DOMContentLoaded', function(e){	
	particles2.setupParts('cvs1','#000');
	for (var i=0;i<500;i++){
		var color = Math.floor(Math.random()*16777215).toString(16);
		var p = new particles2.particle({
			color: '#' + color,
			comp: null,
			alpha:1,
			x:(Math.random() * 400),
			y:(Math.random() * 400),
			vx:(Math.random() * 2),
			vy:(Math.random() * 2),
			size:(Math.random() * 6),
			uniform: true,
			omni:false,
			starfield:false
		});
		particles2.addParts(p);
	}
	particles2.updateCanvasWithParts();
});

Using the SliderFill Sencha Touch Plugin in Sencha Architect

08 Aug 2013

Sliders are pretty cool in Sencha Touch – what makes them cooler is the SliderFill plugin by Thomas Alexander. The plugin can be downloaded here: https://market.sencha.com/extensions/sliderfill.

Before SliderFill the sliders are minimalist – functional yet a tad bland:

sliderfill_before

After SliderFill – one small addition makes them much more appealing:

sliderfill_after

Adding the plugin to your Sencha Architect project is simple – there was one caveat that I encountered – an error within the plugin but it was easy to sort out. Lets walk through the various steps needed to add and use the plugin within Sencha Touch via Sencha Architect.

Create a Sample Architect Project

First thing is first – open Sencha Architect, create a new project and add a slider to your default view. I won’t walk you through that part but if you are lazy here’s a link to a sample project before the plugin was added. What we want is something simple so my example looks like this:

sliderfill_01

Download and Install the SliderFill plugin

Next, download the plugin (link is in first para above) and extract the contents. The archive has the following structure:

  • sliderfill
    • img/
    • src/
    • index.html
    • README

Curiously the README is empty – no worries – within the “src” folder are two files, what you want is the plugin itself which is called “SliderFill.js”. Copy that file and place it within the root of your project, or, if you like, create a plugins folder in your project root and place the file there. For this demonstration I’m just placing it in the project root.

Next you need to add the plugin to your project. Within architect look for the Project Inspector panel – in the screen caps provided here it is on the right. Scroll down to the bottom and look for the “Library” node. Once the plugin is added to the project it will appear as a child of that node.

sliderfill_02

To add the plugin, click the “plus” (+) icon as shown below and choose Resource > JS Resource:

sliderfill_03

You now have a new JS resource added to your project’s library. You can see a red exclamation indicator nexct to it indicating that it needs to be configured.

sliderfill_04

Select the JS library resource and its config will appear. All you need to do is to enter the path to the SliderFill.js file. As I just placed it in the project root all I need to do here is to add the file name.

sliderfill_05

Add SliderFill to a Slider

Now that the plugin is installed lets add the plugin to a slider. Select your slider from the Project Inspector. Its config options will appear.

sliderfill_06

The plugin property doesn’t exist in the config but we can add and configure it by typing the word “plugins” into the config search field and then clicking the “Add” button to create it.

sliderfill_07

We now have a new custom property within the slider’s config.

sliderfill_08

Lets set the property’s type by clicking the “type” button (to the left of the property identified by a circle icon with three dots) and choosing “Array”:

sliderfill_09

We now need to configure the plugin. The plugin will accept a configuration object which is comprised of two things: 1)The xclass that specifies the plugin, and 2) an array of class names to apply to each SliderFill background. I have a single slider handle so I’m only going to have a single class.

To add the config object click the “Edit” button that appeared to the right of the plugins property when you selected “Array” as the property type.

sliderfill_10

The editor appears:

sliderfill_11

I’ll add this object wrapped in an array literal:

[
  {
    xclass : 'Ext.plugin.SliderFill',
    fillCls : ['my_custom_slider_bg']
  }
]

sliderfill_12

Done!

…but one last thing – SliderFill Ver 1 currently generates an error:

sliderfill_13

Ok then, lets go fix it – look in your Project Inspector under the Library node where you added SliderFill.js. Double-click it to open the file as we’ve got a single line to add to fix the error:

sliderfill_14

As the error indicated lets go down to line 46. We want to go back up a couple of lines and add the following line *after* line 44 – but first we need to unlock the file – click the “Unlock” button:

sliderfill_15

Again, create new line after line 44, we will add the following code:

slider = Ext.getCmp('slider.id');

The code should look like this:

sliderfill_16

Save your project.

Style the SliderFill background

Almost done – we just need to setup some defaults and we need to specify a color for the SliderFill background. An easy way to do this is to attach a new style sheet to the project via the Library. So create a new css file, save it in the root fo your project as we did with SliderFill.js (or create a new folder for it if you like) and populate it with the following:

.x-slider-fill{
    margin:0.925em;
    position:absolute;
    height:0.8em;
    -webkit-border-radius:0.4em;
    border-radius:0.4em;
    margin-top:0.75em !important;
    background-image:-webkit-gradient(linear, left top ,left bottom, from(#0A3A86), color-stop(.5, #4C8DE7), color-stop(.95, #6BABF5), to(#0A3A86));
    z-index:1 ;
}

.x-draggable {
    z-index:2;
}

.my_custom_slider_bg{
    background-image:-webkit-gradient(linear, left top ,left bottom, from(#8b1a05), color-stop(.5, #e35e4f), color-stop(.95, #e18080), to(#6f2c22));
}

Note that last style – my_custom_slider_bg thats the class name we specified in SliderFill’s configuration object.

All that is left is to attach the style sheet. The process is the same as what we did to add SliderFill.js, except this time we will add a Style Sheet instead (note that this is a quick and dirty way of doing things – perfectly functional though if you’re familiar with SASS then you’d likely prefer to do it that way).

sliderfill_17

Select the CSS node underneath the Library and enter the path to your CSS – in my case my CSS file is called SliderFill.css.

Save your project and preview – you should see this:

sliderfill_after

Has your design view in Architect gone blank?

Sencha Architect V2.2.2 has a bug where if you add the plugin attribute to the view config the WYSIWG Design View will become completely blank as you can see in one of the above screen captures.

There are a couple of ways around this – the one I think I prefer is to create an onSliderfieldInitialize event in the controller which will apply the plugin to every slider that is initialized within your app.

To do this follow these steps:

  1. Click on your Controller node within the Project Inspector
  2. Next, Locate “Actions” and click the “Plus” button to the right
  3. Select “Controller Action” from the pop-up
  4. Next choose “Ext.field.Slider” as the target type
  5. Then select “initialize” as the event name

Architect will create the new event and display the editor for it – paste in the following code:

component.setPlugins({
    xclass : 'Ext.plugin.SliderFill',
    fillCls : ['my_custom_slider_bg']
});

That’s it, now **every** slider that you add will have SliderFill applied to it and Architect’s 2’s Design View won’t go completely blank on you.

jQuery Mobile Fixes

29 Jul 2013

Hmmm… jQuery Mobile… if only you delivered on mobile the way you promised…. the world’s most popular JS framework sadly misses the mobile mark and by a wide margin. If you do have to use jQM then you’re probably looking for tips on how to get some level of performance out it.

Scroll Performance

This is a widely discussed topic – list view performance in jQM is pretty poor and is most noticibly lacking in Android especially in older devices. Throw in form elements like sliders, buttons – and things are downright unacceptable. Per the aforementioned online discussions and some of my own trial and error remove the following to get a modicum of performance gains:

  • Remove all radiused corners
  • Remove all gradients – I replace them with tiled backgrounds to achieve the same effect
  • Remove all shadows
  • “Disable” all hover states – a big performance hog on mobile
  • Don’t inset the list view – doing so adds more overhead for the mobile browser / webview

An example that has worked for me – you’ll want to tailor this for your specific needs. This works on the premise that if all of the up, down and hover states’ CSS is the same the browser doesn’t have to redraw anything. The result is that nothing happens and you negate the performance hit that you would have otherwise had to endure.

...
*.ui-listview li.ui-btn-up-c,
*.ui-listview li.ui-btn-down-c,
*.ui-listview li.ui-btn-hover-c,
*.ui-listview li div.ui-btn-hover-c
  {
	background: #fff !important;
	font-weight: bold !important;
	color: #222 !important;
	
	background-color: none !important;
	background-image: none !important;
	background-image: -webkit-gradient(linear, left top, left bottom, from( #fff ), to( #f1f1f1 )) !important;
	background-image: -webkit-linear-gradient( #fff , #f1f1f1 ) !important;
	background-image: -moz-linear-gradient( #fff , #f1f1f1 ) !important;
	background-image: -ms-linear-gradient( #fff , #f1f1f1 ) !important;
	background-image: -o-linear-gradient( #fff , #f1f1f1 ) !important;
	background-image: linear-gradient( #fff , #f1f1f1 ) !important;

	border-top-style: solid !important;
	border-top-width: 0px !important;
	border-top-color: rgb(204, 204, 204) !important;
	
	border-bottom-style: solid;
	border-bottom-width: 1px;
	border-bottom-color: rgb(204, 204, 204) !important;
	box-shadow: none !important;
	
  }
...

In addition, if you’re using iScroll or iScrollView you’ll want to do the following:

  • Set vScrollbar to false – the presence of the scroll bar adds some overhead so remove it from view

And here’s an example of setting some defaults in iScrollView – notice that I reference getDeviceInfo() – see my previous article titled Mobile Device Detection using the UserAgent as the code there is assumed to be in use here. Of course you can use your own platform detection scripts. Anyway, you can see that I only use this setting in Android.

...
var _d = myapp.util.getDeviceInfo(); // my own device-detection library
if (_d.android){
    $.mobile.iscrollview.prototype.options.vScrollbar = false;
}
...

Shifty Fixed Headers

Symptoms:

  • When clicking the page the headers disappear
  • When clicking the page elements within the header or footer shift up or down.

Add the following to your application:

...
$("[data-role=header]").fixedtoolbar({ tapToggle: false });
...

I put the above in the “deviceready” event.

Alternatively you could add the following attribute to your header divs:

...
data-tap-toggle="false"
...

Flashing or Slow Page transitions

iOS does a good job with jQM’s page transition animations. Android is another matter. You will want to turn off page transitions entirely for Android.

...
$.extend( $.mobile,{
    var _d = myapp.util.getDeviceInfo(); // my own device-detection library
    defaultDialogTransition: (_d.android ? 'none' : 'pop'),
    defaultPageTransition:   (_d.android ? 'none' : 'fade'),
});
...

Manage DOM Bloat

If you have a multi-page app jQM will add each page into the app’s DOM as you navigate the app. The purpose is to “speed up” the app as jQM will no longer have to do any DOM insertion when future page requests happen. That’s all fine and good for desktop computers but it kind of stinks on mobile. The more that is added to the DOM the slower your app gets. Memory use for most apps is a major concern so you definitely want to disabling this, like so:

...
$.extend( $.mobile,{
    domCache: false
});
...

Another reason to disable is that events like pageload will only fire the very first time the page is inserted into the DOM. Thereafter, it doesn’t fire because the page has already been loaded (cached).

jQuery onChange Event and Text Fields

18 Apr 2013

Seems like jQuery’s change event when bound to an input or textarea element doesn’t work like it should. What happens is that when those elements are blurred *then* onChange fires – completely not what we want to happen. According to the documentation this is actually by design. The only input elements that have an onChange event that behaves in a manner that makes sense are radios, checkboxs and select boxes. For anything else the onChange event will fire when the element loses focus!

...
$('input').change(function() {
  console.log('change, but only after the text input is blurred');
});


...

There are a couple of events that you can use as an alternative, one is the “onInput” event. Curiously while this works in jQuery it is not part of jQuery’s API documentation (v1.9.1) – even though it works. The onInput event is part of the HTML 5 spec so my assumption is that jQuery’s support of it is here to stay, its just not part of the documentation for unknown reasons.

...
$('input').input(function() {
  console.log('behaves like a change event: as the text input element's value is changed the oninput event is fired');
});


...

The caveat here – yes, you knew there would be one – is that since oninput is a member of the HTML5 spec that not every browser will support it. Thankfully IE9 and up supports it as does the crop of modern browsers.

For something more universal you might consider key bindings such as onKeydown or onKeyup, and then not worry about browser support.

...
$('input').keyup(function() {
  console.log('The text input element's value has changed');
});


...