-
-
Save jefflembeck/5850332 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
First, I have to come clean. When [Scott tweeted about wanting a web app version of Grunticon](https://twitter.com/scottjehl/status/345618924219539458) a couple of weeks ago, I had never used Grunticon. Actually, still to this day I've never used Grunticon. Shocking...I know. | |
Of course, I had heard of it. I am a big proponent of using Grunt and use it everywhere to help automate my life. And I follow enough people on Twitter/read enough blogs/listen to enough podcast to have gotten ample exposure to Grunticon and it sounded really cool. So when I saw Scott's tweet I took to action. | |
Before reading on, if you're not familiar with [Grunticon](http://filamentgroup.com/lab/grunticon/) or [Grumpicon](http://www.grumpicon.com/), check those out first. | |
Here are kind of the steps on how Grumpicon works: | |
1. Handle drag and dropped SVG files | |
2. Read the SVGs | |
3. Convert SVGs to data URIs | |
4. Convert SVGs to PNG data URIs | |
5. Make CSS files and the preview.html file | |
6. Create a downloaded zip with all the files and PNG images | |
Sounds easy enough, right? | |
##Handle drag and dropped SVG files | |
The drag and drop API has a few nifty events for handling dropping files onto a web app. From Grumpicon we really just use two: `drop` and `dragover`. (Okay, so technically `dragleave` is also used just for removing the page highlighting.) With the dragover event we can add a highlight to the page and tell it to "copy" the file when dropped (instead of opening it). The drop event has a `dataTransfer` property that contains `files` which is a FileList array on all the files dropped. From there we can do what we want with the files. | |
Some pseudo code from Grumpicon (check out the [Grumpicon GitHub repo](https://github.com/filamentgroup/grumpicon) for the actual code): | |
Views.UploadView = Backbone.View.extend({ | |
events: { | |
"drop": "fileDrop", | |
"dragover": "fileDrag" | |
}, | |
fileDrag: function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
e.dataTransfer.dropEffect = "copy"; | |
// add a class to the body to highlight | |
}, | |
fileDrop: function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
// note, jQuery doesn't normalize the "drop" event | |
// so we have to access originalEvent to get to the dataTransfer property | |
this.addFiles(e.originalEvent.dataTransfer.files); | |
}, | |
addFiles: function(fileList) { | |
// do something with the files | |
} | |
}); | |
##Read the SVGs | |
To do anything with the dropped SVGs we need to access the content of the files. Grunticon generates data URIs with escaped SVG text as background images, we just need to read the text of the files. This is where the File API comes in to play and specifically the `FileReader` object. | |
Reading a file is a lot like create a new image. First you create a `new FileReader()`, then you set an `onload` event to do something with the content once it has been reader, and finally you tell the reader to read a file as text. | |
More pseudo code: | |
read: function() { | |
var model = this, | |
file = model.get("file"), | |
reader = new FileReader(); | |
reader.onload = function(e) { | |
var svgText = e.target.resutls; | |
//do stuff with the svgText | |
}; | |
reader.readAsText(file); | |
} | |
##Covert SVGs to data URIs | |
This step is actually super simple. Just escape the `svgText` read from the file, tack on `"data:image/svg+xml;charset=US-ASCII,"` to the beginning and blammo. | |
##Covert SVGs to PNG data URIs | |
Another amazing HTML5 API saves the day. The `canvas` has a nice little method called `toDataURL()` which takes the contents of a canvas and generates a PNG data URI...exactly what we want. So the only trick here is drawing the SVG onto a canvas. | |
Luckily, it was [canvg](https://code.google.com/p/canvg/) to the rescue. This small library makes drawing a SVG on a canvas super easy. Then `toDataURL()` can be called on the canvas. | |
canvg(canvas, svgText); | |
var pngDataUri = canvas.toDataURL(); | |
##Make CSS files and the preview.html file | |
The 3 CSS files and the HTML file are just made on the page using Underscore templates using the data from the dropped SVGs and the data URIs generated. | |
##Create a downloaded zip with all the files and PNG images | |
When I first started thinking about how to make Grunticon work in the browser, this was the step I was most unsure of. Creating zips? Node sure, but on the client... | |
[JSZip](http://stuk.github.io/jszip/) - another library to the rescue. It's a truly remarkable library. You can add all type of files to your zip, including images from a data URI (perfect for our needs), and even create folders. | |
var view = this, | |
zip = new JSZip(), | |
// create folder | |
img = zip.folder("png"); | |
view.collection.each(function(model) { | |
// add images to the folder | |
img.file(model.get("name") + ".png", | |
model.get("pngDataUri").replace("data:image/png;base64,","") + "\n", | |
{base64: true}); | |
}); | |
// add the other files | |
zip.file("icons.data.svg.css", $("#svg-css-results").text()); | |
zip.file("icons.data.png.css", $("#png-css-results").text()); | |
zip.file("icons.fallback.css", $("#fallback-css-results").text()); | |
zip.file("grunticon.loader.txt", ...); | |
zip.file("preview.html", ...); | |
// create the zip | |
var content = zip.generate(); | |
// make the browser download it | |
location.href = "data:application/zip;base64," + content; | |
So that's basically it. To see the actual source, check out the [Grumpicon repo](https://github.com/filamentgroup/grumpicon) on GitHub. | |
P.S. I had an amazing time working on this app with the team from Filament Group. It's the most fun I've had developing anything in a quite a while! | |
--- | |
###All of JS Libraries used (Thank you open source!) | |
* [RequireJS](http://requirejs.org/) | |
* [Backbone](http://backbonejs.org/), [Underscore](http://underscorejs.org/), and [jQuery](http://jquery.com/) | |
* [canvg](https://code.google.com/p/canvg/) | |
* [JSZip](http://stuk.github.io/jszip/) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment