canvas – Parerga und Paralipomena http://www.michelepasin.org/blog At the core of all well-founded belief lies belief that is unfounded - Wittgenstein Wed, 14 Nov 2012 23:38:57 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.11 13825966 HTML5 Canvas Cookbook http://www.michelepasin.org/blog/2012/02/08/html5-canvas-cookbook/ Wed, 08 Feb 2012 17:34:12 +0000 http://www.michelepasin.org/blog/?p=1815 HTML5 Canvas Cookbook is a new publication from Packt publishing that discusses in details the new drawing functionalities the html5 canvas element makes available; in the last weeks I’ve been looking at this book in more details and since it’s been a quite useful learning experience I wanted to mention it here too.

The book (which is available online here )is simple and well organized; it spends quite a bit of time on both introductory topics and more advanced ones, so it’ll probably fit both the beginner and the more experienced programmer.

Here’s a list of the chapters available:

CHAPTER 1: GETTING STARTED WITH PATHS AND TEXT
Introduction
Drawing a line
Drawing an arc
Drawing a Quadratic curve
Drawing a Bezier curve
Drawing a zigzag
Drawing a spiral
Working with text
Drawing 3D text with shadows
Unlocking the power of fractals: Drawing a haunted tree

CHAPTER 2: SHAPE DRAWING AND COMPOSITES
Introduction
Drawing a rectangle
Drawing a circle
Working with custom shapes and fill styles
Fun with Bezier curves: drawing a cloud
Drawing transparent shapes
Working with the context state stack to save and restore styles
Working with composite operations
Creating patterns with loops: drawing a gear
Randomizing shape properties: drawing a field of flowers
Creating custom shape functions: playing card suits
Putting it all together: drawing a jet

CHAPTER 3: WORKING WITH IMAGES AND VIDEOS
Introduction
Drawing an image
Cropping an image
Copying and pasting sections of the canvas
Working with video
Getting image data
Introduction to pixel manipulation: inverting image colors
Inverting video colors
Converting image colors to grayscale
Converting a canvas drawing into a data URL
Saving a canvas drawing as an image
Loading the canvas with a data URL
Creating a pixelated image focus

CHAPTER 4: MASTERING TRANSFORMATIONS
Introduction
Translating the canvas context
Rotating the canvas context
Scaling the canvas context
Creating a mirror transform
Creating a custom transform
Shearing the canvas context
Handling multiple transforms with the state stack
Transforming a circle into an oval
Rotating an image
Drawing a simple logo and randomizing its position, rotation, and scale

CHAPTER 5: BRINGING THE CANVAS TO LIFE WITH ANIMATION
Introduction
Creating an Animation class
Creating a linear motion
Creating acceleration
Creating oscillation
Oscillating a bubble
Swinging a pendulum
Animating mechanical gears
Animating a clock
Simulating particle physics
Creating microscopic life forms
Stressing the canvas and displaying the FPS

CHAPTER 6: INTERACTING WITH THE CANVAS: ATTACHING EVENT LISTENERS TO SHAPES AND REGIONS 
Introduction
Creating an Events Class
Working With Canvas Mouse Coordinates
Attaching Mouse Event Listeners to Regions
Attaching Touch Event Listeners to Regions on a Mobile Device
Attaching Event Listeners to Images
Dragging-And-Dropping Shapes
Dragging-And-Dropping Images
Creating an Image Magnifier
Creating a Drawing Application

CHAPTER 7: CREATING GRAPHS AND CHARTS
Introduction
Creating a pie chart
Creating a bar chart
Graphing equations
Plotting data points with a line chart

CHAPTER 8: SAVING THE WORLD WITH GAME DEVELOPMENT
Introduction
Creating sprite sheets for the heroes and enemies
Creating level images and boundary maps
Creating an Actor class for the hero and enemies
Creating a Level class
Creating a Health Bar class
Creating a Controller class
Creating a Model class
Creating a View class
Setting up the HTML document and starting the game

CHAPTER 9: INTRODUCING WEBGL
Introduction
Creating a WebGL wrapper to simplify the WebGL API
Creating a triangular plane
Rotating a triangular plane in 3D space
Creating a rotating cube
Adding textures and lighting
Creating a 3D world that you can explore

The last chapters about animations and game developments are probably the most interesting ones, especially they all include detailed walk-through of the techniques discussed. Here’s for example an example from Chapter 5, ‘Oscillating a bubble’:


[inline]

[script type=”text/javascript”]

var Animation = function(canvasId){
this.canvas = document.getElementById(canvasId);
this.context = this.canvas.getContext(“2d”);
this.t = 0;
this.timeInterval = 0;
this.startTime = 0;
this.lastTime = 0;
this.frame = 0;
this.animating = false;

// provided by Paul Irish
window.requestAnimFrame = (function(callback){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
};

Animation.prototype.getContext = function(){
return this.context;
};

Animation.prototype.getCanvas = function(){
return this.canvas;
};

Animation.prototype.clear = function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
};

Animation.prototype.setDrawStage = function(func){
this.drawStage = func;
};

Animation.prototype.isAnimating = function(){
return this.animating;
};

Animation.prototype.getFrame = function(){
return this.frame;
};

Animation.prototype.start = function(){
this.animating = true;
var date = new Date();
this.startTime = date.getTime();
this.lastTime = this.startTime;

if (this.drawStage !== undefined) {
this.drawStage();
}

this.animationLoop();
};

Animation.prototype.stop = function(){
this.animating = false;
};
Animation.prototype.getTimeInterval = function(){
return this.timeInterval;
};

Animation.prototype.getTime = function(){
return this.t;
};

Animation.prototype.getFps = function(){
return this.timeInterval > 0 ? 1000 / this.timeInterval : 0;
};

Animation.prototype.animationLoop = function(){
var that = this;

this.frame++;
var date = new Date();
var thisTime = date.getTime();
this.timeInterval = thisTime – this.lastTime;
this.t += this.timeInterval;
this.lastTime = thisTime;

if (this.drawStage !== undefined) {
this.drawStage();
}

if (this.animating) {
requestAnimFrame(function(){
that.animationLoop();
});
}
};

window.onload = function(){
// instantiate new animation object
var anim = new Animation(“myCanvas”);
var context = anim.getContext();
var canvas = anim.getCanvas();

anim.setDrawStage(function(){
// update
var widthScale = Math.sin(this.getTime() / 200) * 0.1 + 0.9;
var heightScale = -1 * Math.sin(this.getTime() / 200) * 0.1 + 0.9;

// clear
this.clear();

//draw
context.beginPath();
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(widthScale, heightScale);
context.arc(0, 0, 65, 0, 2 * Math.PI, false);
context.restore();
context.fillStyle = “#8ED6FF”;
context.fill();
context.lineWidth = 2;
context.strokeStyle = “#555”;
context.stroke();

context.beginPath();
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(widthScale, heightScale);
context.arc(-30, -30, 15, 0, 2 * Math.PI, false);
context.restore();
context.fillStyle = “white”;
context.fill();
});

anim.start();
};

[/script]

[/inline]

 

In a nutshell, this is what is going on (note that the ‘animation’ library is discussed in a previous chapter of the book):

 

<script src="animation.js"></script>
<script>
    window.onload = function(){
        // instantiate new animation object
        var anim = new Animation("myCanvas");
        var context = anim.getContext();
        var canvas = anim.getCanvas();
        
        anim.setDrawStage(function(){
            // update
            var widthScale = Math.sin(this.getTime() / 200) * 0.1 + 0.9;
            var heightScale = -1 * Math.sin(this.getTime() / 200) * 0.1 + 0.9;

            // clear
            this.clear();
            
            //draw
            context.beginPath();
            context.save();
            context.translate(canvas.width / 2, canvas.height / 2);
            context.scale(widthScale, heightScale);
            context.arc(0, 0, 65, 0, 2 * Math.PI, false);
            context.restore();
            context.fillStyle = "#8ED6FF";
            context.fill();
            context.lineWidth = 2;
            context.strokeStyle = "#555";
            context.stroke();
            
            context.beginPath();
            context.save();
            context.translate(canvas.width / 2, canvas.height / 2);
            context.scale(widthScale, heightScale);
            context.arc(-30, -30, 15, 0, 2 * Math.PI, false);
            context.restore();
            context.fillStyle = "white";
            context.fill();
        });
        
        anim.start();
    };
</script>

 

All in all, a book definitely worth reading!

 

]]>
1815
Using Impromptu to visualize RSS feeds http://www.michelepasin.org/blog/2011/12/21/using-impromptu-to-visualize-rss-feeds/ Wed, 21 Dec 2011 13:22:30 +0000 http://www.michelepasin.org/blog/?p=1073 Some time ago I’ve been experimenting with the processing and display of RSS feeds within Impromptu, and as a result I built a small app that retrieves the news feed from The Guardian online and displays on a canvas. I’ve had a bit of free time these days, so last night I thought it was time to polish it a little and make it available on this blog (who knows maybe someone else will use it as starting point for another project).

Visualizing rss feeds with Impromptu

There’re a thousand improvements that could be done to it still, but the core of the application is there: I packaged it as a standalone app that you can download here. (use the ‘show package contents’ Finder command to see the source code).

The application relies on a bunch of XML processing functions that I found within Impromptu ‘examples’ folder (specifically, it’s the example named 35_objc_xml_lib). I pruned that a bit so to fit my purposes and renamed it xml_lib.scm.

By using that, I created a function that extracts title and url info from the guardian feed:

(load "xml_lib.scm")
(define feedurl "http://feeds.guardian.co.uk/theguardian/world/rss")

;;
;; loads the feed and extracts title and url
;;

(define get-articles-online
     (lambda ()
        (let* ((out '())
               (feed (xml:load-url feedurl))
               (titles (objc:nsarray->list (xml:xpath (xml:get-root-node feed)
                                                "channel/item/title/text()")))
               (urls (objc:nsarray->list (xml:xpath (xml:get-root-node feed)
                                                "channel/item/link/text()"))))                                                 
           (for-each (lambda (x y)
                        (let ((xx (objc:nsstring->string x))
                              (yy (objc:nsstring->string y)))
                           (set! out (append out (list (list xx yy))))))
                titles urls)
           out)))

Some feed titles are a bit longish, so I added a utility function formattext that wraps the titles’ text if they exceed a predefined length.

(define formattext 
   (lambda (maxlength txt posx posy)
      (let ((l (string-length txt)))      
         (if (> l maxlength)
             (let loop ((i 0)
                        (j maxlength) ;; comparison value: it decreases at each recursion (except the first one) 
                        (topvalue maxlength)) ;; komodo value : must be equal to j at the beginning
                (if (equal? (- topvalue i) j) ;; the first time
                    (loop (+ i 1) j topvalue)
                    (begin   ;(print (substring txt (- topvalue i) j))
                             (if (string=? (substring txt (- topvalue i) j) " ")
                                 (string-append (substring txt 0 (- topvalue i)) 
                                                "n" 
                                                (substring txt (- topvalue i) (string-length txt)))
                                 (if (< i topvalue) ;;avoid negative indexes in substring
                                     (loop (+ i 1) (- j 1) topvalue))))))
             txt))))

And here’s the main loop: it goes through all the feed items at a predefined speed, and displays it on the canvas using a cosine oscillator to vary the colours a bit. At the end of it I’m also updating 3 global variables that are used for the mouse-click-capturing routine.

(define displayloop
   (lambda (beat feeds) 
      (let* ((dur 5)
             (posx  (random 0 (- *canvas_max_x* 350)))
             (posy  (random 10 (- *canvas_max_y* 150)))
             (txt (formattext 40 (car (car feeds)) posx posy))
             (dim ;(+ (length feeds) 10))                  
                  (if (= (length feeds) 29)
                      60  ;; if it's the first element of the feed list make it bigger
                      (random 25 50)))
             (fill (if (= (length feeds) 29)
                         '(1 0 (random) 1)  ;; if it's the first element of the feed list make it reddish
                         (list (random) 1 (random) 1)))
             (style (gfx:make-text-style "Arial" dim fill)))
         (gfx:clear-canvas (*metro* beat) *canvas* (list (cosr .5 .6 .001) 0 (cosr .5 .6 .001) .5 ))
         (gfx:draw-text (*metro* beat) *canvas* txt style (list posx posy))
         (set! *pos_x* posx)
         (set! *pos_y* posy)
         (set! *current_url* (cadr (car feeds)))
     (callback (*metro* (+ beat (* 1/2 dur))) 'displayloop (+ beat dur)
               (if-cdr-notnull feeds 
                               (get-articles-online))))))

In order to capture the clicks on the feed titles I simply create a rectangle path based on the x,y coordinates randomly assigned when displaying the title on the canvas. These coordinates are stored in global variables so that they can be updated constantly.

(io:register-mouse-events *canvas*)
(define io:mouse-down
   (lambda (x y)
      (print x y)
      (when (gfx:point-in-path? (gfx:make-rectangle *pos_x* *pos_y* 200 200) x y )
            (util:open-url *current_url*))))

Finally, the util:open-url opens up a url string in your browser (I’ve already talked about it here).

You can see all of this code in action by downloading the app and taking a look its contents (all the files are under Contents/Resources/app).

Visualizing rss feeds with Impromptu

If I had the time…

Some other things it’d be nice to do:

  • Creating a routine that makes the transitions among feed items less abrupt, maybe by using canvas layers.
  • Refining the clicking events creation: now you can click only on the most recent title; moreover the clicking event handler is updated too quickly, thus unless you click on the titles as soon as it appears you won’t be able to trigger the open-url action.
  • Refining the xml-tree parsing function, which now is very very minimal. We could extract news entries description and other stuff that can make the app more informative.
  • Adding some background music to it.
  • Any other ideas?

     

    ]]>
    1073
    First steps with Canvas and HTML5 http://www.michelepasin.org/blog/2011/08/25/first-steps-with-canvas-and-html5/ Thu, 25 Aug 2011 14:47:35 +0000 http://www.michelepasin.org/blog/?p=1503 I’ve been postponing experimenting with HTML5 for quite a while, so today I finally set aside a few hours to play with it. This is a very simple example of the graphic effects you can create using javascript and the HTML5 canvas element..

    Note that the examples below will work only on HTML5-compatible browsers, such as the latest versions of Chrome or Firefox..

    1. Setting up a canvas and drawing some lines on it.

    This is easily done using the moveTo and lineTo functions. So let’s create a simple symmetrical geometrical shape..


    Your browser does not support the canvas element.

    [inline]

    [script type=”text/javascript”]

    var c=document.getElementById(“myCanvas1”);
    var cxt=c.getContext(“2d”);
    var i=0;

    for (i=1;i<=5;i++) { cxt.moveTo(0, 400); cxt.lineTo(100, i * 40); cxt.lineTo(300, i * 60); cxt.lineTo(400, i * 30); cxt.lineTo(500, i * 60); cxt.lineTo(700, i * 40); cxt.lineTo(800, 400); } cxt.stroke(); [/script] [/inline]

    Here’s the source code:

    <!DOCTYPE HTML>
    <html>
    <body>
    
    <canvas id="myCanvas1" width="800" height="400" style="border:1px dashed #c3c3c3;">
    Your browser does not support the canvas element.
    </canvas>
    
    <script type="text/javascript">
    
    var c=document.getElementById("myCanvas1");
    var cxt=c.getContext("2d");
    var i=0;
    
    for (i=1;i<=5;i++)
    {
    cxt.moveTo(0, 400);
    cxt.lineTo(100, i * 40);
    cxt.lineTo(300, i * 60);
    cxt.lineTo(400, i * 30);
    cxt.lineTo(500, i * 60);
    cxt.lineTo(700, i * 40);
    cxt.lineTo(800, 400);
    }
    
    cxt.stroke();
    
    </script>
    
    </body>
    </html>
    

     

    2. Creating a mirroring effect

    Now we can increase the size of the canvas to 800 and replicate these lines at the bottom of the canvas so to achieve a ‘mirroring’ effect.. since we know the max Y value of the canvas (800), let’s just add another loop that draws the same lines but inverts the Y position. This is easily achieved by subtracting the constant-dependent parameter from the max value of Y.


    Your browser does not support the canvas element.

    [inline]

    [script type=”text/javascript”]

    var c=document.getElementById(“myCanvas2”);
    var cxt=c.getContext(“2d”);
    var i=0;

    for (i=1;i<=5;i++) { cxt.moveTo(0, 400); cxt.lineTo(100, i * 40); cxt.lineTo(300, i * 60); cxt.lineTo(400, i * 30); cxt.lineTo(500, i * 60); cxt.lineTo(700, i * 40); cxt.lineTo(800, 400); } for (i=5;i>=1;i–)
    {
    cxt.moveTo(0, 400);
    cxt.lineTo(100, 800 – (i * 40));
    cxt.lineTo(300, 800 – (i * 60));
    cxt.lineTo(400, 800 – (i * 30));
    cxt.lineTo(500, 800 – (i * 60));
    cxt.lineTo(700, 800 – (i * 40));
    cxt.lineTo(800, 400);
    }

    cxt.stroke();

    [/script]

    [/inline]

    This is how the new source code looks like:

    <canvas id="myCanvas2" width="800" height="800" style="border:1px dashed #c3c3c3;">
    Your browser does not support the canvas element.
    </canvas>
    
    <script type="text/javascript">
    
    var c=document.getElementById("myCanvas2");
    var cxt=c.getContext("2d");
    var i=0;
    
    for (i=1;i<=5;i++)
    {
    cxt.moveTo(0, 400);
    cxt.lineTo(100, i * 40);
    cxt.lineTo(300, i * 60);
    cxt.lineTo(400, i * 30);
    cxt.lineTo(500, i * 60);
    cxt.lineTo(700, i * 40);
    cxt.lineTo(800, 400);
    }
    
    
    for (i=5;i>=1;i--)
    {
    cxt.moveTo(0, 400);
    cxt.lineTo(100, 800 - (i * 40));
    cxt.lineTo(300, 800 - (i * 60));
    cxt.lineTo(400, 800 - (i * 30));
    cxt.lineTo(500, 800 - (i * 60));
    cxt.lineTo(700, 800 - (i * 40));
    cxt.lineTo(800, 400);
    }
    
    cxt.stroke();
    
    </script>
    

     

    3. Adding more graphical interest

    Finally, let’s parametrize a bit more the construction of this geometrical pattern by including everything into another loop, and using this new counter to increment the points ordinate position. We’ll add a new variable y and use it to run the external loop 20 times. Here’s the result:


    Your browser does not support the canvas element.

    [inline]

    [script type=”text/javascript”]

    var c=document.getElementById(“myCanvas3”);
    var cxt=c.getContext(“2d”);
    var i=0;
    var y=0;

    for (y=1;y<=20;y++) { for (i=1;i<=5;i++) { cxt.moveTo(0, 400+y); cxt.lineTo(100, i * (40+y)); cxt.lineTo(300, i * (60+y)); cxt.lineTo(400, i * (30+y)); cxt.lineTo(500, i * (60+y)); cxt.lineTo(700, i * (40+y)); cxt.lineTo(800, 400+y); } for (i=5;i>=1;i–)
    {
    cxt.moveTo(0, 400);
    cxt.lineTo(100, 800 – (i * (40+y)));
    cxt.lineTo(300, 800 – (i * (60+y)));
    cxt.lineTo(400, 800 – (i * (30+y)));
    cxt.lineTo(500, 800 – (i * (60+y)));
    cxt.lineTo(700, 800 – (i * (40+y)));
    cxt.lineTo(800, 400+y);
    }

    }

    cxt.stroke();

    [/script]

    [/inline]

    Not too bad uh? This last modification to the code looks like this:

    <canvas id="myCanvas3" width="800" height="800" style="border:1px dashed #c3c3c3;">
    Your browser does not support the canvas element.
    </canvas>
    
    <script type="text/javascript">
    
    var c=document.getElementById("myCanvas3");
    var cxt=c.getContext("2d");
    var i=0;
    var y=0;
    
    for (y=1;y<=20;y++)
    {
        
        for (i=1;i<=5;i++)
        {
        cxt.moveTo(0, 400+y);
        cxt.lineTo(100, i * (40+y));
        cxt.lineTo(300, i * (60+y));
        cxt.lineTo(400, i * (30+y));
        cxt.lineTo(500, i * (60+y));
        cxt.lineTo(700, i * (40+y));
        cxt.lineTo(800, 400+y);
        }
    
    
        for (i=5;i>=1;i--)
        {
        cxt.moveTo(0, 400);
        cxt.lineTo(100, 800 - (i * (40+y)));
        cxt.lineTo(300, 800 - (i * (60+y)));
        cxt.lineTo(400, 800 - (i * (30+y)));
        cxt.lineTo(500, 800 - (i * (60+y)));
        cxt.lineTo(700, 800 - (i * (40+y)));
        cxt.lineTo(800, 400+y);
        }
    
    }
    
    cxt.stroke();
    
    </script>
    

     

    Cool! I’d like to know more..

    Yes, I agree. This opens up a new world for web-based graphics.. and I just scratched the surface of it! In particular I’d like to see how this type of graphical components can be used to compose visualizations in the digital humanities – provided more interactivity is added to them.

    Here are a couple if learning resources I found useful:

  • Learning the basics of HTML5 canvas on .Net magazine online
  • Canvas tutorial on Mozilla.org (more advanced)
  • Javascript Graphics and Effects Frameworks: useful document providing a list of the most common libraries for doing javascript-based graphics
  • Canvas and interactivity tutorial: a step by step discussion on how to create a ‘breakout’ game clone that you can play in your browser, using javascript and the canvas element
  • Creating an HTML 5 canvas painting application: nice tutorial that shows how to put together several canvas drawing techniques so to build a basic ‘painting’ application
  • 21 Ridiculously Impressive HTML5 Canvas Experiments: a collection of some state-of-the-art HTML5 canvas-based experiments that will make you say, “Wow!”
  •  

     

    ]]>
    1503