topical media & game development

talk show tell print

lib-flow-example-canvas.js / js



  // canvas.js
  //
  // 0.4
  //
  // Stephen Band
  // webdev.stephband.info/canvas
  //
  // Methods for working with canvases.
  
  debug = true;
  
  //
  
  var canvas = {
        framerate:40,
        init:     function() {
                    // Makes sure canvases have an id and stores their contexts
                    jQuery('canvas').each(function(n){
                      var id = jQuery(this).attr("id");
                      if (!id) {
                        id = "canvas_"+n;
                        jQuery(this).attr({id: id});
                      }
                      if (this.getContext) canvas.elements[id] = {ctx: this.getContext("2d")};
                    });
                    // Set animation clock to trigger frame events
                    setInterval(function(){jQuery("canvas").trigger("frame");}, canvas.framerate);
                  },
        resize:   function() {
                    // Stores dimensions of canvases and maintains aspect ratio of 1:1 by setting their grid dimensions to same
                    jQuery('canvas').each(function(){
                      var id = jQuery(this).attr("id");
                      var dimensions = {
                          width:    jQuery(this).width(),
                          height:   jQuery(this).height()
                      };
                      jQuery.extend(canvas.elements[id], dimensions);
                      jQuery(this).attr(dimensions);
                    });
                  },
        clear:    function (id) {
                    // How do we get dimensions from ctx only, if we wanted to pass ctx not id?
                    ctx = canvas.elements[id].ctx;
                    ctx.clearRect(0, 0, canvas.elements[id].width, canvas.elements[id].height);
                  },
        draw:     function draw(ctx, shape, options, style){
  
                    // ctx              - object drawing context OR string #id of canvas element
                    // shape            - reference to a shape OR string name of shape in canvas.shapes
                    // options          - object - all properties optional
                    //   pos: [x, y]    - Position on canvas
                    //   ori: [x, y]    - Origin of path (relative to unscaled size)
                    //   sca: [x, y]    - Scale of path
                    //   smin: [x, y]   - Min scale of path (positive number, can't be 0)
                    //   smax: [x, y]   - Max scale of path
                    //   rot: 0-1       - Rotation of path
                    //   plot: boolean  - Gradients and patterns transformed with shape
                    //   fill: boolean  -
                    //   stroke: boolean-
                    // style            - object
                    //   fillStyle:     - "rgba(255,165,0,1)", "#ffa300", "orange", gradient, pattern
                    //   strokeStyle:   - "rgba(255,165,0,1)", "#ffa300", "orange", gradient
                    //   lineWidth: >0
                    //   lineCap:       - "butt", "round", "square"
                    //   lineJoin:      - "round", "bevel", "miter"
                    //   miterLimit:    - value
                    
                    if (typeof ctx == "string") ctx = canvas.elements[ctx].ctx;
                    
                    var pos  = options.pos;
                    var ori  = options.ori;
                    var sca  = options.sca;
                    var smin = (options.smin) ? options.smin : [0.001, 0.001] ;
                    var smax = options.smax;
                    var rot  = options.rot * 2* Math.PI;
                    
                    // Limit scale.  Don't allow to scale to 0.
                    for (i in sca) {
                      if (smin[i] > sca[i] && -smin[i] < sca[i]) sca[i] = smin[i];
                      if (smax) {
                        if (smax[i] < sca[i]) sca[i] = smax[i];
                        if (-smax[i] > sca[i]) sca[i] = -smax[i];
                      }
                    }
  
                    ctx.save();
                    
                    // Adjust drawing plane
                    if (pos) ctx.translate(pos[0], pos[1]);
                    if (rot) ctx.rotate(rot);
                    if (sca) ctx.scale(sca[0],sca[1]);
                    if (ori) ctx.translate(-ori[0], -ori[1]);
                    
                    // Make path
                    if (typeof shape == "string") shape = canvas.shapes[shape];
                    shape(ctx, options);
                    
                    // Reset orientation before plot
                    if (!options.plot && ori) ctx.translate(ori[0], ori[1]);     // undo translate to reset gradient position
                    if (sca)                  ctx.scale(1/sca[0], 1/sca[1]);     // undo scale
                    if (!options.plot && rot) ctx.rotate(-1*rot);                // undo rotate (reset gradient orientation)
                    if (!options.plot && pos) ctx.translate(-pos[0], -pos[1]);   // undo translate (stop gradient travel with origin)
                    
                    // Plot path
                    canvas.plot(ctx, options.fill, options.stroke, style);
                    
                    ctx.restore();
  
                  },
        gradient: function (ctx, coords, colors) {
                    // ctx    - either the drawing context OR the id of the canvas element
                    // coords - array - 4 numbers for a linear gradient (x1,y1,x2,y2), 6 for a radial (x1,y1,r1,x2,y2,r2)
                    // colors - object - {colorPosition: value} pairs, eg. {0: white, 1:"rgba(34, 34, 78, 0.8)"}
                    // returns gradient object
                    if (typeof ctx == "string") ctx = canvas.elements[ctx].ctx;
                    var gradient;
                    if      (coords.length == 4) {gradient = ctx.createLinearGradient(coords[0], coords[1], coords[2], coords[3]);}
                    else if (coords.length == 6) {gradient = ctx.createRadialGradient(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);}
                    else if (debug)              {alert("Gradient not created: There's a problem with the number of values you're passing to canvas.gradient in coords.");}
                    for (i in colors) {gradient.addColorStop(i, colors[i]);}
                    return gradient;
                  },
        plot:     function (ctx, fill, stroke, style) {
                    if (typeof ctx == "string") ctx = canvas.elements[ctx].ctx;
                    ctx.save();
                    if (typeof style == "object" && (fill || stroke)) jQuery.extend(ctx, style);
                    if (fill) ctx.fill();
                    if (stroke) ctx.stroke();
                    ctx.restore();
                  },
        elements: {},
        shapes:{
            roundedRect:  function(ctx, options){
                            
                            // options
                            //   dim: [x, y]   - dimensions
                            //   radius: >0    - radius of corners
                            //   stretch: 0-1  - how 'stretched out' corners are
                            //   random:       - adds that unexpected touch
                            
                            var width   = (options.dim) ?  options.dim[0]   :   300 ;
                            var height  = (options.dim) ?  options.dim[1]   :   150 ;
                            var radius  = (options.radius) ? options.radius :   20 ;
                            var random  = (options.random) ? options.random : false;
                            var stretch = (options.stretch || options.stretch === 0) ? options.stretch : 12 ;
                            var xstretch = 0;
                            var dig     = 10;
                            var start   = [0, 0];
                            
                            // Shall we draw the straight edges?
                            var edge = {
                                x:      (radius >= width /2) ? false: true,
                                y:      (radius >= height/2) ? false: true
                            };
                            
                            function ran() {
                              if (random)  { return (parseInt(dig*(Math.random() * 2 * random - random)))/dig; }
                              else      { return 0; }
                            };
                            
                            // Swap direction of rounded corner if they go in the way
                            if (stretch < 0) {
                              xstretch = stretch;
                              stretch = 0;
                            } 
                            
                            start = [ran(), ran()+radius]
                            
                            ctx.beginPath();
                            ctx.moveTo(start[0], start[1]);
                            if (edge.y) ctx.lineTo(ran(),ran()+height-radius);
                            ctx.bezierCurveTo( ran()-xstretch,              ran()+height-radius+stretch,  ran()+radius-stretch,       ran()+height+xstretch,        radius+ran(), ran()+height);
                            if (edge.x) ctx.lineTo(ran()+width-radius,ran()+height);
                            ctx.bezierCurveTo( ran()+width-radius+stretch,  ran()+height+xstretch,        ran()+width+xstretch,       ran()+height-radius+stretch,  ran()+width, ran()+height-radius);
                            if (edge.y) ctx.lineTo(ran()+width,ran()+radius);
                            ctx.bezierCurveTo( ran()+width+xstretch,        ran()+radius-stretch,         ran()+width-radius+stretch, ran()-xstretch,               ran()+width-radius, ran());
                            if (edge.x) ctx.lineTo(ran()+radius,ran());
                            ctx.bezierCurveTo( ran()+radius-stretch,        ran()-xstretch,               ran()-xstretch,             ran()+radius-stretch,         start[0], start[1]);
                          },
            ellipse:  function(ctx, options){
                            
                            // radius: pixels
                            // xscale: 0-1
                            // yscale: 0-1
                            // pinch: 0-1     - squares or flattens the corners, ie. no longer an ellipse
                            
                            var radius = (options.radius) ?  options.radius  :   100 ;
                            var xscale = (options.xscale) ?  options.xscale  :   1 ;
                            var yscale = (options.yscale) ?  options.yscale  :   1 ;
                            var pinch  = (options.pinch || options.pinch === 0) ? options.pinch : 0.552285 ;
                            var random = (options.random) ? options.random : false;
                            var bez = pinch*radius;
                            var x = [0, xscale*(radius-bez) ,xscale*radius, xscale*(radius+bez), xscale*2*radius];
                            var y = [0, yscale*(radius-bez) ,yscale*radius, yscale*(radius+bez), yscale*2*radius];
                            
                            function ran() {
                              if (random)  { return Math.random()*2*random - random; }
                              else         { return 0; }
                            };
                            
                            var start = [ran()+x[2],ran()+y[0]];
                            
                            ctx.beginPath();
                            ctx.moveTo(start[0], start[1]);
                            ctx.bezierCurveTo(ran()+x[3], ran()+y[0],  ran()+x[4], ran()+y[1],  ran()+x[4], ran()+y[2]);
                            ctx.bezierCurveTo(ran()+x[4], ran()+y[3],  ran()+x[3], ran()+y[4],  ran()+x[2], ran()+y[4]);
                            ctx.bezierCurveTo(ran()+x[1], ran()+y[4],  ran()+x[0], ran()+y[3],  ran()+x[0], ran()+y[2]);
                            ctx.bezierCurveTo(ran()+x[0], ran()+y[1],  ran()+x[1], ran()+y[0],  start[0],   start[1]);
                                                    
                          }
        },
        gradients: {
          //blackFade:      canvas.gradient(ctx, coords, colors),
        }
      };


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.