WEBDOOD.COM

Archive for July, 2012

<CANVAS> Spinner Revisited

by webdood on Jul.11, 2012, under Canvas, Javascript, Software Development

I previously invented a Javascript-only, CANVAS-based spinner. I went to re-use this code today and found it to be less than easy to use.

Therefore, I rejiggered a few things to make it so you can create an number of spinners on page, with each spinner having its own start, stop, display and toggle method exposed.

      function Spinner( oHTMLElement, params ) {
        if (typeof(oHTMLElement)=="string")  { oHTMLElement = document.getElementById(oHTMLElement); }
        if (oHTMLElement) {
          if (params===undefined) { params={} };
          var oCanvas = document.createElement('canvas'),
              context = null,
              height = parseFloat( getComputedStyle( oHTMLElement, null)["height"] ),
              width  = parseFloat( getComputedStyle( oHTMLElement, null)["width"] ),
              radius = Math.min(height / 2, width / 2),
              innerRadius     = params["innerRadius"] || radius * 2 * .27833333,
              outerRadius     = params["outerRadius"] || radius * .99,
              finWidth        = params["finWidth"]    ||  .08 * radius,
              finColors       = params["finColors"]   || [ "e8e8e8","f1f1f1", "fafafa", "b2b2b2", "b6b6b6", "b9b9b9", "bebebe", "c3c3c3", "c9c9c9", "d0d0d0", "d7d7d7", "dfdfdf" ],
              strokeStyle     = params["strokeStyle"] || "rgba(128,128,128,1)",
              strokeWidth     = (params["strokeWidth"]!==undefined) ? params["strokeWidth"] : 2,    // Specifies whether an outline will appear around each fin
              finColorOffset  = 0,      // This is a counter that is mod-ed around 12 to draw the spinner with appropriate color as indexed into the finColors array
              spinnerInterval = null;   // Handle to setInterval operation generating spin animation
          // Set up the <canvas> object
          oCanvas.width  = width;
          oCanvas.height = height;
          context  = oCanvas.getContext('2d');
          context.translate( radius, radius );
          oHTMLElement.appendChild( oCanvas );
          draw();
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // Spinner.display(boolean bDisplay) - show/hides the parent oHTMLElement        //
        // =================================   (external method)                         //
        ///////////////////////////////////////////////////////////////////////////////////
        this.display = function( bDisplay ) {
          if ( bDisplay ) {
            oHTMLElement.style.display = 'block';
          } else {
            oHTMLElement.style.display = 'none';
          }
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // draw() - this clears the previous spinner drawn, then loops through the       //
        // ======   12 fins, rotating the canvas 30 degrees each time and drawing each   //
        // fin with the appropriate color from the finColors array (internal method)     //
        ///////////////////////////////////////////////////////////////////////////////////
        function draw() {
          context.clearRect(-width,-height,width*2,height*2);
          for (var i=0;i<12;i++) {
            drawFin( finColors[ (i + finColorOffset) % 12] );
            context.rotate(-360/12 * Math.PI/180);
          }
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // drawFin( string fillColor ) - this draws a single fin.  Each is made up of a  //
        // ==========================   half circle on either end of a rectangle.        //
        //  Trust me, this took a while to work out. But I overcame! (internal method)   //
        ///////////////////////////////////////////////////////////////////////////////////
        function drawFin( fillColor ) {
          var ctx = context;
          ctx.fillStyle = fillColor;
          ctx.strokeStyle = strokeStyle;
          ctx.lineWidth   = strokeWidth;
          ctx.beginPath();
          ctx.arc(outerRadius-finWidth,0,finWidth,-Math.PI/2,Math.PI/2, false);
          ctx.rect(innerRadius,-finWidth,outerRadius-innerRadius-finWidth,finWidth*2);
          ctx.arc(innerRadius,0,finWidth,Math.PI/2,-Math.PI/2,false);
          ctx.closePath();
          if (strokeWidth > 0) {
            ctx.stroke();
          }
          ctx.fill();
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // Spinner.start() - sets up an interval that continues to call spin().          //
        // ===============   (external method)                                           //
        ///////////////////////////////////////////////////////////////////////////////////
        this.start = function() {
          this.display( true );
          spinnerInterval = setInterval( function() { spin(); }, 100);
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // Spinner.stop() - cancels the interval used to continuously call spin().       //
        // ==============   (external method)                                            //
        ///////////////////////////////////////////////////////////////////////////////////
        this.stop = function() {
          clearInterval( spinnerInterval );
          spinnerInterval = null;
        }
        ///////////////////////////////////////////////////////////////////////////////////
        // spin() - this increments the offset into the fin color array. Really, the     //
        // ======   spinner is not "spinning", per se, but rather re-drawing the fins    //
        // in-place with a set of continuously changing colors.  (internal method)       //
        ///////////////////////////////////////////////////////////////////////////////////
        function spin() {
          finColorOffset += 1;
          draw();
        }
        this.toggle = function() {
          if (spinnerInterval === null) {
            this.start();
          } else {
            this.stop();
          }
        }
      }

Please give credit when you steal this!
by Shannon Norrell, 2010-2012

Working Example

Leave a Comment more...

Javascript extractedWebkitTranslate3DProperty WebKitCSSMatrix

by webdood on Jul.04, 2012, under CSS3, Safari-Specific, Software Development, WebKitCSSMatrix

For me, there are many times where I need to get at the x, y or z value of a -webkit-transform: translate3d({x}, {y}, {z}) property.

I had originally written the below method using .style.webkitTransform and then doing some fancy manipulations to extract out the x, y or z values from the style. However, you will find that elements will not have webkitTransform applied as a “style” to them when it is being set from a css class.

Therefore, I rewrote the method to use getComputedStyle and using its return value for ‘-webkit-transform’.

The interesting thing about this is that it returns a string representation of a WebKitCSSMatrix. Now I had to figure out how to get at the values of x, y and z from this structure.

With some trial and error, I found the following property correspondence:

.m41 – corresponds to the ‘x’ value of a WebKitCSSMatrix
.m42 – corresponds to the ‘y’ value of a WebKitCSSMatrix
.m43 – corresponds to the ‘z’ value of a WebKitCSSMatrix

Here is the main extractedWebkitTranslate3DProperty method, followed by its helper method getStyle:

extractedWebkitTranslate3DProperty

    ///////////////////////////////////////////////////////////////////////////////////////
    // extractedWebkitTranslate3DProperty([object|string] oHTMLElement, string property) //
    // ================================================================================= //
    // Extracts x,y,or z value from a webkitTranslate3D style. Returns a string value.    //
    ///////////////////////////////////////////////////////////////////////////////////////
    function extractedWebkitTranslate3DProperty( oHTMLElement, property ) {
      var retVal = 0;
      if (typeof(oHTMLElement)=="string")  { oHTMLElement = document.getElementById(oHTMLElement); }
      if (property !== undefined) {
        var webkitTransform = getStyle(oHTMLElement,'-webkit-transform');
        if (webkitTransform && webkitTransform!=='none' && webkitTransform.indexOf('matrix') > -1) {
          switch (property.toLowerCase()) {
            case "x" :
              retVal = new WebKitCSSMatrix(webkitTransform).m41;
              break;
            case "y" :
              retVal = new WebKitCSSMatrix(webkitTransform).m42;
              break;
            case "z" :
              retVal = new WebKitCSSMatrix(webkitTransform).m43;
              break;
          }
        }
      }
      return parseFloat(retVal);
    }

getStyle

    ////////////////////////////////////////////////////////////////////////////////
    // getStyle([object|string] oHTMLElement, string cssProperty)                 //
    // ==========================================================                 //
    // Returns value of a cssProperty                                             //
    ////////////////////////////////////////////////////////////////////////////////
    function getStyle(oHTMLElement, cssProperty) {
      if (typeof(oHTMLElement)=="string")  { oHTMLElement = document.getElementById(oHTMLElement); }
      var retVal = "";
      if(document.defaultView && document.defaultView.getComputedStyle){
        retVal = document.defaultView.getComputedStyle(oHTMLElement, null).getPropertyValue(cssProperty);
      } else {
        if (oHTMLElement.currentStyle){
          cssProperty = cssProperty.replace(/\-(\w)/g, function (strMatch, p1){
            return p1.toUpperCase();
          });
          retVal = oHTMLElement.currentStyle[cssProperty];
        }
      }
      return retVal;
    }
Leave a Comment :, , , , more...

Javascript animateObjectFromTo

by webdood on Jul.04, 2012, under Javascript, Objects, Software Development

This method will animate an object from a given left, top to another left, top over a specified number of milliseconds at a specified frame rate.

This is an example of how it might be called:

  var myDiv = document.getElementById('myDiv),
      currentPosition = window.getComputedStyle(myDiv,null),
      currentLeft     = parseFloat(currentPosition["left"]),
      currentTop      = parseFloat(currentPosition["top"]);
  animateObjectFromTo(
    myDiv,
    { top:currentTop, left:currentLeft },
    { top:0, left:currentLeft },
    250
  );

Notice how the method uses an inner function for the animation heavy lifting.

///////////////////////////////////////////////////////////////////////////////////////////
//  animateObjectFromTo([object|string] oHTMLElement, json From, json To, int totalTimeInMilliseconds, OPTIONAL int framesPerSecond)
//   Sets up an animation that interpolates frame animation from a beginning top, left coordinate
//   to an ending top, left coordinate over a number of milliseconds at a specified frame rate
//      totalTimeInMilliseconds - default is 1 second
//      framesPerSecond - default is 30
/////////////////////////////////////////////////////////////////////////////////////////
    animateObjectFromTo : function(oHTMLElement, from, to, totalTimeInMilliseconds, framesPerSecond) {
      if (typeof(oHTMLElement)=="string")  {
        oHTMLElement = document.getElementById(oHTMLElement);
      }
      totalTimeInMilliseconds = totalTimeInMilliseconds || 1000;
      framesPerSecond = framesPerSecond || 30;
      var currentFrame      = 0,
          numberOfFrames    = parseInt(totalTimeInMilliseconds / framesPerSecond),
          deltaTimePerFrame = totalTimeInMilliseconds/numberOfFrames,
          deltaXPerFrame    = (to.left - from.left)/numberOfFrames || 0,
          deltaYPerFrame    = (to.top - from.top)/numberOfFrames || 0;

      animate();

      function animate() {
        if (currentFrame<numberOfFrames) {
          var oCurrentStyle = document.defaultView.getComputedStyle(oHTMLElement, null);
          oHTMLElement.style.left = parseFloat(oCurrentStyle["left"]) + deltaXPerFrame + "px";
          oHTMLElement.style.top  = parseFloat(oCurrentStyle["top"]) + deltaYPerFrame + "px";
          currentFrame += 1;
          setTimeout( function() { animate(); }, deltaTimePerFrame );
        }
      }

shannon norrell

Leave a Comment more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Blogroll

A few highly recommended websites...

  • A List Apart
  • Dive into HTML5
  • Javascript: The Good Parts
  • QuirksMode.org