Showing posts with label svg. Show all posts
Showing posts with label svg. Show all posts

Wednesday, March 23, 2016

Perspective Transformations in Inkscape

I still haven't gotten a chance to go back to work on one of my games. I'm on another trip for work this week. But I did learn something new related to today's comic.

While it doesn't have any special features, it does have a piece of paper on the ground with writing on it. The writing is skewed to show perspective. This is something that I hadn't been able to figure out before today - how to make a perspective transformation in Inkscape.

Here's how to do it. First, get whatever object you want to transform. It can be text or a picture of something, but you have to turn it into a path first. You can do that by using the Path - Object to Path menu option.

In this example, I'll transform one of my frog images. It is made up of lots of paths, all in a group. That's ok. To perform the skew, draw another path around the object in the shape that you want it to look like in the end. Start in the lower left corner and draw the line clockwise.


After you have the shape outlined by the new path, select the shape to be transformed first and the path second (Shift+click). Then use the menu option Extensions - Modify Path - Perspective. It takes a few seconds, but should look like this after it runs.
You can delete the outline path if you want.
That's how I made the text on the "landing page" look like it does. I'm always happy when I learn something new. I'm also happy when you read my frog comics.

Amphibian.com comic for 23 March 2016

Friday, July 3, 2015

Trying out Snap.svg

Snap.svg
On Wednesday I wrote about trying to use the jQuery SVG plugin to perform some simple manipulations to a SVG image directly embedded in an HTML document. While jQuery SVG is perhaps the most popular way to interact with SVG via JavaScript, I found it awkward to use. I decided to give another similar JavaScript package a try - Snap.svg.

Snap.svg is a JavaScript SVG library designed for modern web browsers and created by the author of the Raphaël library. Unlike Raphaël, Snap does not support browsers older than IE 9. This makes it smaller and more feature-rich - and let's face it, there are fewer and fewer reasons every day to support IE 8 and below.

To do a fair comparison between Snap and jQuery SVG, I performed the same task with both libraries. I have a really wide SVG scene with some frogs in it. The viewBox restricts the viewable part of the scene but changes when you click on one of the frogs.

Here is basically the same HTML/SVG I used on Wednesday, with most of the SVG cut out to save space.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Snap.svg Test</title>
</head>

<body>

    <div id="svgContainer">

        <svg id="svgScene" xmlns="http://www.w3.org/2000/svg"
                viewbox="0 0 500 282">
            ....
            <g id="ceo-frog">
                ....
            </g>
            <g id="science-frog">
                ....
            </g>
            ....
        </svg>

    </div>

</body>

<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="snap.svg.js"></script>

</html>

The difference from Wednesday's jQuery SVG page is obviously the script at the bottom. I am including the snap.svg.js file (there's a minified version available too) but unlike the jQuery plugin, Snap does not need any CSS. I therefore don't have any stylesheets included in this one. I am still using jQuery though, but just for the convenience of the function that can be called when the DOM is ready. Snap does not itself rely on jQuery.

Here is the JavaScript that sets up and performs the animation.

$(function() {

    var s = Snap("#svgScene");

    var vb = s.attr("viewBox");
    var bounds = s.getBBox();

    // note that the viewBox object is not the same
    // as the bounds object!

    var ceo = Snap("#ceo-frog");
    var sci = Snap("#science-frog");

    ceo.click(function() {
        s.animate({viewBox: "500 0 500 282"}, 1500, mina.easeinout);
    });

    sci.click(function() {
        s.animate({viewBox: "0 0 500 282"}, 1500, mina.easeinout);
    });

});

On line 3, I get a Snap object that represents the SVG root element. The Snap() function takes as a parameter a selector much like jQuery does for finding the SVG elements on the page.

Lines 5 and 6 (and the comments on 8 and 9) have no bearing on the rest of the code, but I wanted to include them just for informational purposes. The Snap.attr() function is a way to get attribute values for SVG elements. But when calling s.attr("viewBox"), Snap is smart enough to return a special object representing the viewBox, not just its String value. Nice. This is different from calling s.getBBox() however. The .getBBox() function returns an element's bounding box. In the case of my SVG scene that means an object representing the entire width, not just the currently visible portion. Just wanted to point that out. I was impressed.

Continuing on, lines 11 and 12 get the frog objects from the SVG. Notice how you can call Snap() directly to get these - you don't need to pass a reference to the root SVG node or anything. This is quite a departure from jQuery SVG. Once I have those objects, setting up click event handlers follows a pattern much like jQuery event handlers. Lines 14 and 18 are examples of that.

Animation of the viewBox change is also nicer than with jQuery SVG, in my opinion. Snap SVG Element objects (above, s, ceo, and sci are all Elements) have an animate function which is quite similar to jQuery's. To animate a property of the element, you just specify it in an object along with its target value. In my example I am animating the viewBox attribute by specifying the String value I want it to be when the animation finishes. This is done on lines 15 and 19. The second parameter to the animate function is the length of time in milliseconds that the animation should last. Again, this is just like jQuery. The third parameter is optional but specifies the animation's easing function. By default, a jQuery animation easing looks more like the Snap easeinout easing so I specified that to make them match more closely.

Here's what it looks like in action:



What's my conclusion after comparing these two SVG libraries? While the number of lines of code needed to perform the simple click-to-animate-the-viewbox action was basically the same in both versions, I think the Snap API makes more sense. Snap's documentation is also much better than jQuery SVG's. Even so, I wouldn't mind seeing a few more demos linked directly from the documentation (there are some cool demos, but no easy way to view the code behind them), and that viewBox object behavior doesn't even appear to be documented (at least I couldn't find it). After using both to perform the same task, I have to say that I prefer Snap and think I will use it going forward. I would also recommend it if you want to manipulate SVG on a web page. It has a ton more features that I've shown here - this example is extremely basic! Give it a try for yourself.

And enjoy the holiday weekend, if you live in the United States. Enjoy the regular weekend if you live anywhere else in the world.

Amphibian.com comic for 3 July 2015

Wednesday, July 1, 2015

The Basics of jQuery SVG

I know I've been writing more about SVG lately. This is because I am working on a new design for caseyleonard.com that is basically all SVG embedded in an HTML page. But static content is boring! I want a website that moves or something!

One way I can accomplish this is with jQuery SVG, a jQuery plugin that lets you interact with SVGs via JavaScript. I tried to do some basic things with it, just to see if I would like it.

First of all, I will report that the online documentation for jQuery SVG is not the greatest. I found the site navigation to be confusing and explanations of the code samples were hard to understand. There are some good demos, but it's not exactly clear why they work in all cases. I had to do a bit of experimentation in order to figure out the basics.

My goal was to have a very wide scene rendered via SVG. You can only see part of the scene at any given time because of the viewBox settings. Clicking on some elements in the SVG will cause the viewBox to scroll a new part of the scene into view.

Here is a stripped-down version of the HTML page. Most of the SVG has been removed so save space.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery SVG Test</title>
<link rel="stylesheet" type="text/css" href="css/jquery.svg.css">
<link rel="stylesheet" type="text/css" href="css/default.css">
</head>

<body>

    <div id="svgContainer">

        <svg id="svgScene" xmlns="http://www.w3.org/2000/svg"
                viewbox="0 0 500 282">
            ....
            <g id="ceo-frog">
                ....
            </g>
            <g id="science-frog">
                ....
            </g>
            ....
        </svg>

    </div>

</body>

<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="js/jquery.svg.js"></script>
<script type="text/javascript" src="js/jquery.svgdom.js"></script>
<script type="text/javascript" src="js/jquery.svganim.js"></script>

</html>

At the bottom, you can see the jQuery and jQuery SVG imports. jQuery SVG is very modularized - you need to include the base plus any of the different sub-modules you wish to use. I am using the DOM module to allow me to access the DOM of the SVG much like the DOM of the HTML. I am also using the Animations module to allow me to use jQuery animation with the SVG attributes. Also note that there is a small CSS file included as part of the jQuery SVG package that you'll want to include.

Now it gets weird. Look at the following JavaScript needed to make the viewBox move.

$(function() {

    $("#svgScene").svg();

    var svg = $("#svgScene").svg("get");

    $("#ceo-frog", svg.root()).click(function(e) {
        $("#svgScene").animate({svgViewBox: "500 0 500 282"}, 1500);
    });

    $("#science-frog", svg.root()).click(function(e) {
        $("#svgScene").animate({svgViewBox: "1000 0 500 282"}, 1500);
    });

});

Everything is wrapped in a function that gets called when the page is fully loaded. The first thing that must happen is that the SVG code must be initialized on the SVG element in the document. There on line 5, I used the standard jQuery selector to get the SVG element by id and then call .svg() on it. After that, I can get an SVG object by calling .svg("get") on that same element again.

You need the svg object in order to make calls to find elements inside the SVG. See lines 7 and 11 where I set up click listeners on two of the objects in the SVG scene (a picture of CEO Frog and a picture of Science Frog). The object returned from calling svg.root() must be passed as the second parameter to the normal jQuery selector function in order for it to work properly.

Finally, likes 8 and 12 reveal how to animate the viewBox of the SVG. Using the selector for the SVG root element and a call to the standard jQuery .animate() function, it is possible to create a smooth scroll transition between two viewBox configurations. The most important thing to realize is that you can't use the parameter "viewBox" as input to the animate function. You have to animate the "svgViewBox" attribute instead. I know, there is no actual attribute in the document named svgViewBox. It's just viewBox. But the SVG attributes have special names (mostly the same names with "svg" at the beginning but not always - see the documentation) that you must use for animation purposes. The viewBox attribute is made up of four numbers. The first two are the x and y coordinates for the top left corner of the viewBox. The next number is the width of the view and the last number is the height. In my animation, I change the first number to add 500 each time, which essentially scrolls the view sideways by its width.

I don't know about you, but I find the API for this module to be a bit odd. Yes, I got it to work, but I didn't really feel good about it.

Bottom line: I think I'm going to try Snap.svg next before I make a decision on what to use.

Amphibian.com comic for 1 July 2015

Wednesday, June 24, 2015

Responsive SVG

I mentioned the other day that I was working on an update for caseyleonard.com that included even more full-screen frog images. Much like the current site, I want to use SVG images and have the frogs scale right along with the browser window, for a smooth responsive effect.

And as usual, Microsoft Internet Explorer has to ruin the party.

Instead of using <img> tags for the frogs this time, I am going to embed SVG markup directly in the HTML of the page. This has been possible for quite a while (IE support began with version 9) but is not often seen on "normal" web pages. Any page with my name on it will be far from normal.

You can try this yourself if you have some SVG lying around. It's best to use minified SVG (see last Wednesday's post) so your pages don't get too terribly large. Here is an example with the actual SVG stuff blanked out to save space:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Responsive SVG</title>
</head>

<body>

  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276 281">
    ...
  </svg>

</body>

</html>

By including a viewBox attribute to set the aspect ratio of the image and not including width or height attributes, good browsers like Chrome and Firefox scale the image to the maximum width of the container. Bad browsers like Internet Explorer assume a fixed height of 150 pixels and scale the width to create an image with the appropriate aspect ratio. Huh?

Properly scaled frog, in Chrome

Improperly scaled frog, in Internet Explorer

It turns out this is a known issue with IE, and it easily correctable with some CSS. First, wrap the SVG element in a <div> of class container. Then add the following CSS to the page:

.container {
    width: 100%;
    height: 0;
    padding-top: 102%;
    position: relative;
}

svg {
    position: absolute;
    top: 0;
    left: 0;
}

The value of container width is 100% because I want the frog to be as big as the window. You can use other values if you want your image to be smaller. The value for padding-top is calculated based on a formula and will be different for every image. To get the percent for padding top, do

( ( svg height) / (svg width) ) x (container width)

So in my example, the height of the frog image with my desired aspect ratio (from the viewBox) is 281 and the width is 276. I divide height by width and then multiply by 100 to get a value of 102. I use that for the padding-top percentage. Another look at the page in IE shows the correct result, and the page looks unchanged in Chrome.

Correct this time, Internet Explorer

And that looks much better! It's still a shame that IE makes us do all this extra work. And speaking of extra work, in today's comic the frogs take server hardening a little too far.

Amphibian.com comic for 24 June 2014

Wednesday, June 17, 2015

Optimizing SVG Files with Node

The comics for today and Monday have been scaling jokes, but there's nothing funny about Scalable Vector Graphics. Well, there probably is but I can't think of anything at the moment.

Due to the fact that I have a new baby at home (daughter number 4!) I haven't had as much time to work on code projects, but I have been trying to make some updates to my personal website whenever I have a few minutes. Much like my comics, I want to make use of SVG images of frogs on it. Really big, in-your-face frogs. Frogs that scale perfectly even on retina displays.

The downside to using SVG images is that they can easily become bloated. Sure, if you want a really large image it's almost always better to use an SVG instead of a "normal" image format. And since they are just XML text they compress well. But Inkscape puts a ton of extra stuff in them that doesn't really need to be there - removing all the unnecessary information can make the files even better!

There are some web-based tools for cleaning-up SVG files, but the ones that I tried were not able to process my frogs without destroying them.

But then I found SVGO, the SVG Optimizer for Node.

It is super-configurable, modular, and can be run from the command line or included in an application. I installed and tested it from the command line:
$ npm install -g svgo
$ svgo frog.svg frog.min.svg
This image is the original file, frog.svg.

This one is 15k
...and this image is the optimized file, frog.min.svg.

This one is 5k
See any differences? I don't. And the optimized one is only 5k, whereas the original was 15k.

When I get a few more minutes, I intend to embed it in the workflow of adding or updating images for the comic. Once I do that, all SVGs that I create in Inkscape will be automatically optimized when I upload them before they are stored in the database. This should improve the download times for my comic as well as the render speed since there is less XML for the clients to process.

Not that the comic takes that long to load now...the server does gzip all the files before sending them as long as the client supports it. See for yourself by viewing the comic!

Amphibian.com comic for 17 June 2015

Wednesday, November 12, 2014

Amazing Anaglyphs

For today's comic about 3D printers, the final cell has an optional stereoscopic 3D effect. You are probably familiar with this type of 3D imagery, where you have to look at it with those cardboard glasses with the two different colored lenses. Pictures like this are called anaglyphs. The most common form are the red/cyan type. Those two colors are used because red and cyan are opposite each other on the color spectrum.

Red/Cyan Stereoscopic 3D Glasses
Making the 3D version of the comic was not as easy as I initially thought it would be. As you may know, I create my comics by combining multiple SVG images on a web page. My first idea was to apply an SVG filter to create the anaglyph effect.

The first hurdle was to actually create the filter. I mistakenly assumed that Inkscape's filter editor might be usable. Ugh. Poor assumption. I had to edit the SVG file in a text editor, but it was certainly easy enough to apply the filter in Inkscape.

The Anaglyph SVG Filter


Here is the filter I created. Apply it to any element in a SVG document to make it pop off the screen!

<filter
     color-interpolation-filters="sRGB"
     inkscape:label="3d"
     id="filter3895-1-1">
    <feOffset
      result="l1"
      in="SourceGraphic"
      id="feOffset3897-8-2"
      dy="0"
      dx="-5" />
    <feOffset
      result="r1"
      in="SourceGraphic"
      id="feOffset3899-9-3"
      dy="0"
      dx="5" />
    <feColorMatrix
      result="r2"
      values="1 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 "
      in="r1"
      id="feColorMatrix3901-2-3" />
    <feColorMatrix
      result="l2"
      values="1 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 "
      in="l1"
      id="feColorMatrix3903-7-4" />
    <feMerge
        id="feMerge3905-9-1">
       <feMergeNode
           id="feMergeNode3907-5-1"
           in="l2" />
       <feMergeNode
           id="feMergeNode3909-4-3"
           in="r2" />
    </feMerge>
    <feBlend
       mode="multiply"
       in2="l2"
       in="r2"
       id="feBlend3911-3-8" />
</filter>

Here's the before-and-after pictures.

Before the Anaglyph Filter
After the Anaglyph Filter
Let me explain a little about what it does. First, it splits the source graphic into two new graphics. Each one has an X offset. In this case, I use an offset of 5 in each direction. To make the image appear to come further out of the screen, just use a larger value here. The first offset graphic (see the tag starting on line 5) will be the left side because it has the dx value of -5. The second offset graphic (starting on line 11) will be the right side because it has the dx value of 5. The resulting new graphics are called l1 and r1, respectively. The next step is to apply a different color transformation to each of the new graphics. The feColorMatrix tags on lines 17 and 22 perform these modifications. One side gets all the red removed and the other all the cyan. Finally, both of the new graphics get merged back together and a multiply blend is applied. This multiplies the color values together wherever they overlap, which results in the original color being seen again. On the edges, where there is no overlap, you can see a little of the all-red and all-cyan images.

Looks good, right? Put on your 3D glasses and that frog is jumping out at you. There was just one slight problem...This only works on individual graphics. If you try to arrange multiple images on a web page with these filters applied, the colors won't all blend together to create a cohesive scene. It looks weird, trust me.

The Anaglyph CSS


First idea was a failure. Next idea: use the new color-blending features of CSS3 on the images to create the scene! Here's how that works.

First, I created two SVG images of the frog. On one image I applied a filter to strip out the cyan color channel, and on the other I applied a filter to strip out the red channel. That gave me these oddly colored graphics:

frog_cyan_channel.svg

frog_red_channel.svg
I could then use these graphics together as backgrounds for a DIV, blending them together to create the same type of effect seen in the SVG filter. Here is an example of just that:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Color Test</title>
  <style>

.ana {

  background:
    url(frog_red_channel.svg),
    url(frog_cyan_channel.svg);
  background-position:
    0px 0px,
    10px, 0px;
  background-repeat: no-repeat;
  background-blend-mode: multiply;

}

  </style>
</head>

<body>

  <div class="ana" style="top: 45px; width: 326px; height: 281px;"></div>

</body>

</html>

When you view this page in your browser, it looks like this:

Anaglyph via CSS blending
The CSS involved here is actually fairly simple. The DIV has two background images, the two color-filtered frog graphics. The position of the second background is given as "10px" which moves it 10 pixels to the right of the first background. This offset can be changed to make the frog appear closer to the viewer. The background-repeat is turned off so that we don't see another image along the edge, since the DIV has to be made 10 pixels wider than either image to ensure that there's room for the offset. Finally, background-blend-mode is set to multiply. Just like in the SVG filter, this causes the color values to be multiplied together anywhere the two images overlap - which brings back the original color in most of the image. Only along the edges do you see the all-red or all-cyan versions.

But alas, it's still not that easy. This works great for a single image. However, if you try to put one image done this way on top of another image, like I do in my comic, the colors from the two images won't blend with each other. They only blend their own backgrounds. Plus, this kind of blending still doesn't work on text even in the latest web browsers. That means even if I could figure out a slick way to overcome the first problem, I wouldn't be able to make the frog's words float in front of your face. Foiled again!

Even though this didn't work out either, I did learn quite a bit. You can read the following page to learn more about CSS blend modes: Basics of CSS Blend Modes.

The Classic Approach


In the end, I had to use the old-fashioned way to create the 3D version of the comic. I created two different versions of the cell, each with the individual components (frogs, text, and printer) slightly offset. I used my standard PhantomJS script to turn each one into a PNG image. Then I used GIMP to do the same thing to those two images that I'd been doing in the SVG filter and CSS - strip out the opposite colors from each image and then blend to put them together to form a single image.

After creating your 2 images, one for the right eye and one for the left eye, open them as Layers in GIMP.


You should end up with layers something like this. Make sure the right eye's image is on top. If it didn't end up there, just move it up.


Next, set your foreground color to red. Pure red. #FF0000.


Now create two new layers, with "Foreground color" selected as the fill type. Create one on top of the right side image and one between the right and left side images.


Your layers should now look like this:


Select the lower layer, the one directly above the left-side image, and from the Colors menu, select Invert. That should change the entire fill color to cyan.


And your layers should now look like this:


Next, click on each of the color layers and change their Mode to Screen. The mode selector is at the very top of the layer area above Opacity. Just click on the drop down on the right side and you'll see lots of options. Screen is the one you want.


Now from the Layer menu, select Merge Down on each of the pure color layers. This will combine them with the image layers directly below them.


And you have just two layers remaining and they should look something like the following picture. The one final step is to change the Mode on the top layer (should be the right eye image if you've done everything correctly) to Multiply. You can merge down again if you want to create a single layer, but your image should already look like a red/cyan anaglyph if you have both layers visible. Now you can export it!

Mode on the top layer is "Multiply"
It was quite a journey of discovery that I took to learn and experiment with all this, just to get a single cell in a single comic. I hope you enjoy looking at it as much as I enjoyed making it.

Amphibian.com comic for 12 November 2014

Friday, August 1, 2014

Web Comic Launch

Today I launch my web comic, which I have so creatively titled Amphibian.com. I've wanted to do a new comic for years, but I was waiting for the right inspiration. Unless you live in central Pennsylvania (inside this area) you probably don't know that 20 years ago I published a comic about a frog in a local newspaper. But newspapers are dead. Print comics are dead. My comic was barely alive in the first place. What's changed?

Web Comics Made with 100% Real Web


If you've ever read a comic on your computer or mobile device, you may have noticed that most of them are pretty much just print comics converted to a JPG or PNG and stuck on a web page. Most are probably made by talented artists, working with some artisty tools, maybe on a computer but maybe still on paper. Anyone who's ever seen my work knows that I am not a talented artist. But I am pretty good with the technologies that make web sites. And I have been drawing a frog so for so long that it looks like an almost legitimate frog.

So I decided to make a web comic that was made with real web technologies. I designed it to be optimized for mobile devices, and easy to share with your friends. Here's a breakdown of what it's made of.

  • HTML + CSS: The comics are just HTML markup and CSS styling. I didn't draw a box and then draw some stuff in it, I style a <div> to have a border and then position individual <img> objects in it to arrange the scene. Even the speech balloons are just <p> tags with appropriate styles applied.
  • SVG: I take responsive design to a whole other level. When you look at the comics on your phone, not only is the page header and navigation being restyled to fit the smaller screen, the comic images themselves are scaled down as well. Have a retina display? Make the page as big as you want, my frogs won't get all pixelated on you. And with Gzip compression, most of the images are actually smaller downloads than a large PNG would be.
  • JavaScript: In both the client and server, I'm using JavaScript to make things better. In your browser, jQuery lets me animate parts of the comic scenes easily. I can make things change when you click (or touch) the characters in the cells. These can be part of the jokes or just for silly fun. The back-end is made with Node and the Express framework. The part that you don't see is an editor that combines client-side goodness with REST web services to enable me to take the ideas from my head and put them in the comics with ease.
Comic Editor

Why Would Someone Do This?


Are there advantages to this approach to a web comic? I think there are many.

First of all, using web technologies to make web comics really uses the medium to its fullest. Web sites don't just look like newspaper pages (at least not anymore) so why should comics? We have all these great features in our web browsers and we are barely using them. I want to use them just a little bit more.

Second, comics made this way are mobile-friendly. They read top-to-bottom and don't require you to pinch or swipe or poke or jab or jump or anything. If you've ever tried to read a "normal" comic on a mobile phone in it's typical configuration - a.k.a. portrait mode - you know what I'm taking about. My comics scale to your device without giving up image quality and can be enjoyed with just your thumb on the phone.

Also, the HTML content can be read by the crawlers and even translators. Like I mentioned, the words my frogs speak are just normal HTML paragraph tags. The contents of my comics will be picked up by the search engine spiders and make it easier to find my stuff. And a tool like Google Translate can show you the comics in lots of other languages. I'm not sure if the jokes make sense in Swedish, but the words might!

Take My Code, Please


As always, my code is open source and on GitHub. My frogs and jokes aren't, but you probably don't want them anyway.


Amphibian.com comic for August 1, 2014