Canvas入门08-绘制仪表盘

需求

实现下图所示的仪表盘的绘制。

分析

我们先来将仪表盘进行图形拆分,并定义尺寸。

我们绘制的逻辑:

  1. 绘制中心圆
  2. 绘制环外圈圆
  3. 绘制环内圈圆
  4. 绘制刻度内圈圆
  5. 绘制刻度线
  6. 绘制刻度文字
  7. 绘制指针

定义圆

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

绘制中心圆

中心圆半径是10,圆心是画布中心。

const CENTROID_RADIUS = 10;
const CENTROID_STROKE_STYLE = 'rgba(0,0,0,.5)';
const CENTROID_FILL_STYLE = 'rgba(80,190,240,.6)';

// 画仪表盘中心
function drawCentroid() {
    context.beginPath();
    context.save();
    context.strokeStyle = CENTROID_STROKE_STYLE;
    context.fillStyoe = CENTROID_FILL_STYLE;
    context.arc(circle.x, circle.y, CENTROID_RADIUS, 0, 2 * Math.PI, false);
    context.stroke();
    context.fill();
    context.restore();
}

绘制环

这里应用了剪纸效果技巧,环外圈圆顺时针绘制,环内圈圆逆时针顺时针绘制,需要注意方向。


const RING_INNER_RADIUS = 35;
const RING_OUTER_RADIUS = 55;

// 绘制环外圈圆
function drawRingOuterCircle() {
    context.shadowColor = 'rgba(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, 2 * Math.PI, true);
    context.stroke();

}


// 绘制环外圈圆
function drawRingInnerCircle() {
    context.strokeStyle = 'rgba(0,0,0,.1)';
    context.arc(circle.x, circle.y, circle.radius + RING_INNER_RADIUS, 0, 2 * Math.PI, false);
    context.fillStyle = 'rgba(100,140,230,.1)';
    context.fill();
    context.stroke();
}

绘制效果:

绘制刻度内圈圆

const TICK_WIDTH = 10;

// 绘制刻度内圆
function drawTickInnerCircle() {
    context.save();
    context.beginPath();
    context.strokeStyle = 'rgba(0,0,0,.1)';
    context.arc(circle.x, circle.y, circle.radius + RING_INNER_RADIUS - TICK_WIDTH, 0, 2 * Math.PI, false);
    context.stroke();
    context.restore();
}

绘制效果:

绘制刻度线

每条刻度线,其实是一个短线段,需要确定Line的起始坐标和终止坐标。

const TICK_WIDTH = 10;

// 绘制刻度
function drawTicks() {
    var radius = circle.radius + RING_INNER_RADIUS;
    var ANGLE_MAX = 2 * Math.PI;
    // var ANGLE_DELTA = Math.PI / 64;
    var ANGLE_DELTA = Math.PI / 24;

    var tickWidth;

    context.save();

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

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

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

}

绘制刻度值

注意刻度值是每个2个刻度线,绘制一个text。

const ANNOTATIONS_FILL_STYLE = 'rgba(0,0,230,.9)';
const ANNOTATIONS_TEXT_SIZE = 12;

function drawAnnotations() {
    var radius = circle.radius + RING_INNER_RADIUS;
    // var deltaAngle = Math.PI /8;
    var deltaAngle = Math.PI / 12;
    context.save();
    context.fillStyle = ANNOTATIONS_FILL_STYLE;
    context.font = ANNOTATIONS_TEXT_SIZE + 'px Helvetica';

    for (var angle = 0; angle < 2 * Math.PI; angle += deltaAngle) {
        context.beginPath();
        var degree = (angle * 180 / Math.PI).toFixed(0);
        var pt = {
            x: circle.x + (radius - TICK_WIDTH * 2) * Math.cos(angle),
            y: circle.x - (radius - TICK_WIDTH * 2) * Math.sin(angle)
        }
        if (degree !== '360') {
            context.fillText(degree, pt.x, pt.y);
        }
    }

    context.restore();
}

效果:

绘制指针

这里没有动画,所以给的是固定角度。


// 绘制指针
function drawCentroidGuidewire(loc) {
    var angle = -Math.PI / 4;
    var radius = circle.radius + RING_OUTER_RADIUS;
    var endpt;

    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, 2 * Math.PI, false);
    context.fill();
    context.stroke();

    context.restore();
}

最后调用的时候,先绘制指针,再绘制中心点,这样可以使指针在中心点下层,好看一些。

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


    drawCentroidGuidewire(loc);
    drawCentroid();
    drawRingOuterCircle();
    drawRingInnerCircle();
    drawTickInnerCircle();
    drawTicks();
    drawAnnotations();
}


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

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

drawDial();


posted @ 2018-11-13 16:32  生活总得有些仪式感  阅读(2213)  评论(0编辑  收藏  举报