canvas - 基础知识 - 仪表盘的绘制

js逻辑代码:

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),

    CENTROID_RADIUS = 10, // 中心半径
    CENTROID_STROKE_STYLE = 'rgba(0, 0, 0, 0.5)',
    CENTROID_FILL_STYLE = 'rgba(80, 190, 240, 0.6)',

    RING_INNER_RADIUS = 35,
    RING_OUTER_RADIUS = 55,

    ANNOTATIONS_FILL_STYLE = 'rgba(0,0,230,0.9)',
    ANNOTATIONS_TEXT_SIZE = 12

    TICK_WIDTH = 10,
    TICK_LONG_STROKE_STYLE = 'rgba(100,140,230,0.9)',
    TICK_SHORT_STROKE_STYLE = 'rgba(100,140,230,0.7)',

    TRACKING_DIAL_STROKING_STYLE = 'rgba(100,140,230,0.5)',

    GUIDEWIRE_STROKE_STYLE = 'goldenrod',
    GUIDEWIRE_FILL_STYLE = 'rgba(250,250,0,0.6)',

    circle = {
      x: canvas.width/2,
      y: canvas.height/2,
      radius: 150
    }

    // function ...........................
    function drawGrid(color, stepx, stepy) {
      context.save();
      context.shadowColor = undefined;
      context.shadowOffsetX = 0;
      context.shadowOffsetY = 0;
      context.strokeStyle = color;
      context.fillStyle = '#fff';
      context.lineWidth = 0.5;
      context.fillRect(0,0, context.canvas.width, context.canvas.height);

      for(var i = stepx + 0.5; i< context.canvas.width; i+= stepx) {
        context.beginPath();
        context.moveTo(i , 0);
        context.lineTo(i, context.canvas.height);
        context.stroke();
      }

      for(var i= stepy + 0.5; i<context.canvas.height; i += stepy) {
        context.beginPath();
        context.moveTo(0, i);
        context.lineTo(context.canvas.width, i);
        context.stroke();
      }
    }

    function drawDial() {
      var loc = {x: circle.x, y : circle.y};

      drawCentroid();
      drawCentroidGuidewire(loc);
      drawRing();
      drawTickInnerCircle();
      drawTicks();
      drawAnnotations();
    }

    function drawCentroid() {
      context.beginPath();
      context.save();
      context.strokeStyle = CENTROID_STROKE_STYLE;
      context.fillStyle = CENTROID_FILL_STYLE;
      context.arc(circle.x, circle.y,
          CENTROID_RADIUS, 0, Math.PI*2, false);
          context.stroke();
          context.fill();
          context.restore();
    }

    function drawCentroidGuidewire(loc) {
      var angle = -Math.PI/4,
          radius, endpt;

        radius = circle.radius + RING_OUTER_RADIUS;

        if(loc.x >= circle.x) {
          endpt = {x: circle.x + radius*Math.cos(angle),
                   y: circle.y + radius*Math.sin(angle)};
        } else {
          endpt = {
            x: circle.x -radius*Math.cos(angle),
            y: circle.y - radius*Math.sin(angle)
          };
        }

        context.save();

        context.strokeStyle = GUIDEWIRE_STROKE_STYLE;
        context.fillStyle = GUIDEWIRE_FILL_STYLE;

        context.beginPath();
        context.moveTo(circle.x, circle.y);
        context.lineTo(endpt.x, endpt.y);
        context.stroke();

        context.beginPath();
        context.strokeStyle = TICK_LONG_STROKE_STYLE;
        context.arc(endpt.x, endpt.y, 5, 0, Math.PI*2, false);
        context.fill();
        context.stroke();

        context.restore();

    }

    function drawRing() {
      drawRingOuterCircle();
      context.strokeStyle = 'rgba(0,0,0,0.1)';
      context.arc(circle.x, circle.y, circle.radius + RING_INNER_RADIUS, 0, Math.PI*2, false);
      context.fillStyle = 'rgba(100,140, 230, 0.1)';
      context.fill();
      context.stroke();
    }

    function drawRingOuterCircle() {
      context.shadowColor = 'rgba(0,0,0,0.7)';
      context.shadowOffsetX = 3;
      context.shadowOffsetY = 3;
      context.shadowBlur = 6;
      context.strokeStyle = TRACKING_DIAL_STROKING_STYLE;

      context.beginPath();
      context.arc(circle.x, circle.y, circle.radius + RING_OUTER_RADIUS, 0, Math.PI*2, true);
      context.stroke();
    }

    function drawTickInnerCircle() {
      context.save();
      context.beginPath();
      context.strokeStyle = 'rgba(0,0,0,0.1)';
      context.arc(circle.x, circle.y, circle.radius+RING_INNER_RADIUS - TICK_WIDTH, 0, Math.PI*2, false);
      context.stroke();
      context.restore();
    }

    function drawTick(angle, radius, cnt) {
      var tickWidth = cnt % 4 === 0? TICK_WIDTH : TICK_WIDTH/2;

      context.beginPath();
      context.moveTo(circle.x + Math.cos(angle) * (radius - tickWidth),
                     circle.y + Math.sin(angle) * (radius - tickWidth));
      context.lineTo(circle.x + Math.cos(angle) * radius, circle.y + Math.sin(angle) * radius);
      context.strokeStyle = TICK_SHORT_STROKE_STYLE;
      context.stroke();
    }

    function drawTicks() {
      var radius = circle.radius + RING_INNER_RADIUS,
          ANGLE_MAX = 2*Math.PI,
          ANGLE_DELTA = Math.PI/64,
          tickWidth;
      context.save();

      for(var angle=0, cnt=0; angle < ANGLE_MAX; angle += ANGLE_DELTA, cnt++) {
        drawTick(angle, radius, cnt++);
      }
      context.restore();
    }

    function drawAnnotations() {
      var radius = circle.radius + RING_INNER_RADIUS;

      context.save();
      context.fillStyle = ANNOTATIONS_FILL_STYLE;
      context.font = ANNOTATIONS_TEXT_SIZE + 'px Helvetica';

      for(var angle=0; angle < 2*Math.PI; angle += Math.PI/8) {
        context.beginPath();
        context.fillText((angle * 180 / Math.PI).toFixed(0),
          circle.x + Math.cos(angle) * (radius - TICK_WIDTH*2),
          circle.y - Math.sin(angle) * (radius - TICK_WIDTH*2));
      }
      context.restore();
    }
  // Initialization .................................

    context.shadowColor = 'rgba(0,0,0,0.4)';
    context.shadowOffsetX = 2;
    context.shadowOffsetY = 2;
    context.shadowBlur = 4;

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    drawGrid('lightgray', 10, 10);
    drawDial();

效果图:

canvas - 基础知识 - 仪表盘的绘制