Grow your CSS skills. Land your dream job.

Drawing Table

Published by Chris Coyier

I made a thing, in which you can use your mouse cursor to draw on a grid with different colors. You can then copy-and-paste the HTML from the design you made.


View Demo   Download Files

Features

  • Clearing the current design
  • Changing the grid size to different preset options
  • Color swatches to change the currently active color
  • Color picker to change the swatch colors
  • White color = erasing
  • Holding the [option] key will enter erase mode, lifting up that key goes back to the color you were on.
  • Ability place image inside the grid to trace from (dims the grid above it)
  • Toggle tracing mode on and off
  • And the point of all this... ability to copy and paste the HTML of the finished design.

What's the point?

I was at a presentation by @blueys where she was talking about HTML email and showed some particularly nice examples. One that I thought was extra cool was this one that she found from the Campaign Monitor gallery.

"Inky", the blue ghost from PacMan shown above, was created with no images, just using table cells with background colors applied. This is particularly cool for HTML emails because in many (most?) email clients these days, image ares not displayed until a user explicitly clicks a link to choose to display them. By using color table cells, you can display simple graphics without using actual images.

Building this Mini App

This is essentially a one-page JavaScript (jQuery) powered mini application. Let's cover how some of the different parts of it work.

Building the grid

The drawing table itself is quite literally an HTML table. We could put table markup in the HTML itself, but it's far more flexible to have JavaScript build the markup. That way we can change the tables structure programatically rather than through literally altering HTML. This also allows us to build new size tables on the fly. One of the features is a dropdown menu for different grid sizes, so let's actually put our grid building JavaScript in a function we can call when that changes.

function buildGrid(cols, rows) {

	var tableMarkup = "";

	for (x = 0; x < cols; x++) {
		tableMarkup += "<tr>";
		for (y = 0; y < rows; y++) {
			tableMarkup += "<td>&nbsp;</td>";
		}
		tableMarkup += "</tr>";	
	}

	$("#drawing-table").html(tableMarkup)

};

This creates an empty string, then has an outer loop which runs as many times as the passed rows parameter and an inner loops which runs as many times as the passed cols parameter. For each row, wrapping <tr> tags are appened to the string, and for each column, <td>'s are inserted.

When done, the grid's html is replaced with the new markup we just created. I was a bit curious about the speed of string concatenation for this kind of thing, but it seems like it's not too bad a way to go. The alternatives being creating and appending elements on the fly, and using arrays. See this Forrst thread for more info.

Now we can set some variables for the initial rows and columns, and call the function.

 var cols = 20, rows = 20;

// Inital Build of Table 
buildGrid(cols, rows);

The HTML for our grid size dropdown menu will be like this:

<select id="gridSize">
	<option value="10,10">10 x 10</option>
	<option value="20,20" selected>20 x 20</option>
	<option value="30,30">30 x 30</option>
</select>

And then we'll watch for that dropdown to change value, and re-call the buildGrid() function when it does.

// Dropdown for changing Grid Size
$("#gridSize").change(function() {
	$el = $(this);
	rows = $el.val().split(",")[0];
	cols = $el.val().split(",")[1];
	buildGrid(rows, cols);
});

Similarly, clearing the design just checks the current setting of the dropdown and rebuilds the grid with that size.

The actual drawing

We need to get a touch clever with the mouse and how we accomplish the click-and-drag drawing feature. We clearly can't just attach click events to the cells, as that would make drawing tedious. We'll need to use the mouseenter event, but also know if the mouse button is current down or not. Let's think it out.

  • On mousedown of any table cell, toggle the drawing state to on
  •   - If the erase state is on, remove styling from cell
  •   - If the erase state is off, apply coloring to cell
  • On mouseenter of any table cell, check if drawing state is on
  •   - if on, color cell
  • On mouseout anywhere, toggle the drawing state off
// Drawing functionality
$("#drawing-table").delegate("td", "mousedown", function() {
	mouseDownState = true;
	$el = $(this);
    if (eraseState) {
    	$el.removeAttr("style");
    } else {
    	$el.css("background", curColor);
    }
}).delegate("td", "mouseenter", function() {
	if (mouseDownState) {
		$el = $(this);
	    if (eraseState) {
	    	$el.removeAttr("style");
	    } else {
	    
	    	// DRAWING ACTION
	    	$el.css("background", curColor);
	    }
	}
});
$("html").bind("mouseup", function() {
	mouseDownState = false;
});

Erasing mode

Our drawing mode is all ready to deal with erasing as well as coloring, so all we need to do is ensure that the eraseState variable is properly set to true or false accordingly. The first way to enable it is to click the white circle. Note in the HTML below, the data-color attribute is used to hold the color value for the three color swatches, but for the fourth/white/eraser circle, the value is "eraser".

<fieldset id="color-selector">
	<legend>Color Picker</legend>
	<div class="color red selected" data-color="red"><input type="text"></div>
	<div class="color green" data-color="green"><input type="text"></div>
	<div class="color blue" data-color="blue"><input type="text"></div>
	<div class="color eraser" data-color="eraser"></div>
	<p>Hold [Option] key for temporary erase mode</p>
</fieldset>

When one of the circles is clicked, if it is a color, the current color will be set to that swatch color and erase mode turned off. If it is the eraser that was clicked, erase mode is toggled on. A selected class is also applied to give visual feedback of the change.

// Color selection swatches
$("#color-selector").delegate(".color", "click", function() {
	
	$el = $(this);
	var pulledVal = $el.attr("data-color");
	
	if (pulledVal == 'eraser') {
		eraseState = true;
	} else {
		eraseState = false;
		curColor = pulledVal;
	}
	
	$(".color").removeClass("selected");
	$(this).addClass("selected");
});

We also wrote in the markup that you can hold the [option] key to toggle erase mode. This makes drawing much easier, being able to switch between modes without having to move the mouse over and select the eraser manually. To do this, we'll watch for keydown and keyup events on the document. If the key happens to be 18 (the option key), we'll turn erase mode on and off accordingly, as well as apply that selected class for more visual feedback.

// Erasing functionality through OPTION key
$(document).keydown(function(event) {
	if (event.keyCode == 18) {
		eraseState = true;
		$(".selected").addClass("previous");
		$(".color").removeClass("selected");
		$(".eraser").addClass("selected");
		
	}
}).keyup(function(event) {
	if (event.keyCode == 18) {
		eraseState = false;
		$(".color").removeClass("selected");
		$(".previous").addClass("selected").removeClass("previous");
		$("." + curColor).addClass("selected");
	}
});

Color picker

I used this jQuery Color Picker. Notice in the HTML for the color swatches above each of them had an <input type="text" /> inside of the <div>. Those inputs are used for the colorpicker, to store the value.

To each of those text inputs, we'll bind the colorpicker. When a choice is made, we'll update the visual color of the swatch as well as its data-color attribute for when it's switched away from and back to.

$('.color input').ColorPicker({
	onSubmit: function(hsb, hex, rgb, el) {
	
		var $swatch = $(el).parent();
		var newColor = "#" + hex;
		
		$(".color").removeClass("selected");
		$("." + $swatch.attr("data-color")).css("background", newColor).addClass("selected");
		$swatch.attr("data-color", newColor);
		curColor = newColor;
		    		    		
	},
	onBeforeShow: function () {
		$(this).ColorPickerSetColor(this.value);
	}
});

Tracing mode

When the URL to an image is entered into the text input provided for that, and it submitted, we want to create a new <div> underneath the table and show that image. It will be sized exactly the same as the table, and set as a background image so if the image is larger it will just get cut off. We'll also have a tracing mode, where we dim the opacity of the table while the image is viewable. To help check the final progress, we'll have a button toggle for turning off tracing mode. Turning it back on is still just a click of a button though.

// Tracing Functionality

$("#tracing-image-form").submit(function() {
	
		var url = $("#fileLocation").val();
					
		$("<div />", {
		
			css: {
				backgroundImage: "url(" + url + ")",
				width: 500,
				height: 500,
				opacity: 1,
				position: "absolute",
				top: 0,
				left: 0
			},
			id: "tracing-image"
		
		}).appendTo("#table-wrap");
		
		$("#drawing-table").css("opacity", 0.5);
		$("#toggle-tracing-mode").show(); 
		$("#tracing-image-form").remove();
		tracingMode = true;	
					
		return false;

	});

$("#toggle-tracing-mode").click(function() {

	if (tracingMode) {
		$("#tracing-image").css("visibility", "hidden");
		$(this).html("Toggle Tracing Mode On");
		$("#drawing-table").css("opacity", 1);
		tracingMode = false;
	} else {
		$("#tracing-image").css("visibility", "visible");
		$(this).html("Toggle Tracing Mode Off");
		$("#drawing-table").css("opacity", 0.5);
		tracingMode = true;
	}

});

That can probably be handled more elegantly than that, but it does the trick.

Supplying the final HTML

The purpose of this whole mini app is to deliver the HTML of the designed table. Fortunately this is incredibly easy. We have a textarea and a button. When the button is clicked, the HTML is gathered from the table and put into the textarea.

$("#get-html-button").click(function() {
	$("#the-html").val("<table style='width: 100%; border-collapse: collapse;'>" + $("#drawing-table").html() + "</table>");
});

Wrap Up

I think I might do a screencast of all this and talk through it all, so watch for that. Not every single detail and line of code is present in the written stuff above, I just broke apart the modules that are the most interesting. To view the complete code, download the example and play. If you do anything fun with it, share!

View Demo   Download Files

Comments

  1. Noah
    Permalink to comment#

    ….How do you find time to be this awesome?

  2. Will
    Permalink to comment#

    Nice, this kind of reminds me of something similar I made:

    http://frabjous.ath.cx/misc/conway/

    It’s a Conway’s Game of Life simulator that toggles the background-color property of a grid of block elements.

    • Permalink to comment#

      NICE. I totally wrote that same thing one time. Only I did mine in jQuery that used a bunch of jQuery DOM traversal stuff to do the calculations, and it was WAY slow. I had help and ultimately got it tweaked and running better, but not quite as good as this. Something so magical about the Conway Game of Life. It was one of the first programming challenges I ever took up as a kid, so I’m sentimentally attached to it that way.

  3. Permalink to comment#

    Hey nice article, I made something very similar this week!

    http://www.reedeesign.com/drawing/

    Yours is way better though :P

  4. Permalink to comment#

    The code is beyond old js-ignorant me, but the demo is fun! Thanks for helping me waste a little work time :)

    I sent myself a drawing, and in Mac OS X Mail at least, the table cells were not square; they were wider than they were high. I wonder if some extra CSS needs to be added to guarantee their squareness. Not that I want to delve deeply into the vagaries of tables in HTML email, or that I expect you to, Chris!

  5. Donna
    Permalink to comment#

    Wow, Chris you never cease to amaze me. You are a genuis!

  6. Permalink to comment#

    Chris, this is awesome, you are a wizard

  7. Permalink to comment#

    I think,first you shared in LinkedIn Chris and yes there is no bug:)

  8. Thanks. Its awesome!

  9. Permalink to comment#

    Just awesome! thanks for sharing!

  10. Permalink to comment#

    Hey guys,

    dunno if you know this, but it -is- possible to let images show up in a html mailing letter, as long as it is not coming from an external source.

    Its called embedding images inline.

    I just did a newsletter for an organization I work for, and it seemed to work!
    I don’t really know what the science is behind it, but the mailing script I use handles it for me:

    http://swiftmailer.org/docs/inline-message-media

    but the drawing app is really cool, and a nice way to share pixel art (whether it be in a html newsletter or online like this) :D

  11. Atanas Minev
    Permalink to comment#

    Cool app, I like the idea!

    You can considerably optimize the output HTML size, though:

    1. style=”background-color:#XXXXXX” is more compact than the current style output

    2. you can utilize colspan and rowspan attributes (to do something like RLE compression)

  12. Nice one Chris, especially dig the clever use of delegate

  13. Chris, some of my favorite posts of yours start with, “I made a thing.”

    Maybe that could even be the name of your blog! :)

  14. Very cool, thanks

  15. Permalink to comment#

    Love it Chris. I just might come back with a Favre portrait. Or are you a Cutler man now?

  16. Bert de Vries
    Permalink to comment#

    Indeed very cool.

  17. Permalink to comment#

    Very cool Chris, I will definitely be wasting some time on this today :)

  18. Omg, this is the coolest tool you ever created! Trying out my navicon: Cody says Hello to everyone ^_^                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

  19. thanks for this article, greatings Sebastian

  20. Gonna give this a shot here and try posting my little droid buddy.

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

  21. Jack Nycz
    Permalink to comment#

    http://www.favicon.cc/

    I know they’re serving different purposes but the design just reminded me of it.

  22. Permalink to comment#

    nice tool pixel based paradigm

  23. Excellent idea, using colour table cells to display simple graphics without using actual images for email, very useful, and thank you for sharing the HTML/jQuery code for the table. LT

  24. Very cool concept, especially for email like you had noted. I wonder what kind of cool effects one could potentially create using & characters or symbol font-families inside of those cells. Though 8-bit is defiantly making a comeback in design, I’d be worried about trying to present this kind of method to a client given it’s limitations. Could have some very slick jQ animations using a table-cell painting method though; I’d be interested to see how slim(file size) someone could make a demo animating this concept.

    Am pretty sure the compiled HTML needs to be a lot more detailed(inline properties) to work inside an email, but, great sandbox anyway.

  25. Permalink to comment#

    your jQuery skills are amazing. I’m jealous.

  26. Permalink to comment#

    Thanks for this :)

    Can you release it under a open source license or grant a permission to use it?

  27. Akshay Aurora
    Permalink to comment#

    I just think if you could enable custom canvas size, it could be an incredibly useful tool for creating background images for 2 column float layout.

    I hope you understood my point….

  28. Permalink to comment#

    Nice for favicon drawing)

  29. Fantastic post and application; as a few have suggested too, looks a rather useful interface in direction for creating or touching up favicons :) Thanks for sharing!

  30. Permalink to comment#

    i got excited to see this RGB support in HTML email…im gonna develop a template for myself and will use this tool to send cool pixel art email..cool stuff !
    Is it possible that it could take an image as source and pixelate it ?might be possible with even more complex calculation !

  31. This is fantastic. I am going to use this on a webblog every day to create some funny images to go along with the stories. I will give you credit at the bottom of the page. So awesome. I was thinking about manually doing this for a while, but this is fantastic!

  32. Permalink to comment#

    great work!
    can it be saved as Content-type:image/jpeg ?

  33. Hi guys, I manage to create a PHP code that builds a html table with the images pixels… I will try to make this something better, soon I will post it here.

  34. Datta3
    Permalink to comment#

    great work

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".