需求

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

分析

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

我们绘制的逻辑:

  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();

Canvas入门08-绘制仪表盘的更多相关文章

  1. Canvas入门(1):绘制矩形、圆、直线、曲线等基本图形

    来源:http://www.ido321.com/968.html 一.Canvas的基础知识 Canvas是HTML 5中新增的元素,专门用于绘制图形.canvas元素就相当于一块“画布”,一块无色 ...

  2. Canvas入门(2):图形渐变和图像形变换

    来源:http://www.ido321.com/986.html 一.图形渐变(均在最新版Google中测试) 1.绘制线性渐变 1: // 获取canvas 的ID 2: var canvas = ...

  3. HTML5 canvas入门

    HTML5 Canvas入门 <canvas> 标签定义图形,比如图表和其他图像,您必须使用脚本来绘制图形.在画布上(Canvas)画一个红色矩形,渐变矩形,彩色矩形,和一些彩色的文字. ...

  4. canvas入门之时钟的实现

    canvas 入门之作: 三步实现一个时钟: 直接上效果:   step 1  : 背景制作首先制作从1-12的数字: var canvas = document.getElementById('ca ...

  5. canvas学习笔记(中篇) -- canvas入门教程-- 颜色/透明度/渐变色/线宽/线条样式/虚线/文本/阴影/图片/像素处理

    [中篇] -- 建议学习时间4小时  课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...

  6. Canvas 入门案例

    五.  Canvas 入门案例 1.  canvas 圆形绘制 <!DOCTYPE html> <html lang="en"> <head> ...

  7. Canvas入门笔记-实现极简画笔

    今天学习了Html5 Canvas入门,已经有大神写得很详细了http://www.cnblogs.com/tim-li/archive/2012/08/06/2580252.html#8 在学习过后 ...

  8. -_-#【Canvas】导出在<canvas>元素上绘制的图像

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. Canvas入门到高级详解(上)

    神奇的 canvas--AICODER 全栈培训 IT 培训专家 一.canvas 简介 1.1 什么是 canvas?(了解) 是 HTML5 提供的一种新标签 <canvas>< ...

随机推荐

  1. 远程连接工具rdcman

    介绍一个远程连接的工具RDCMan.RDCMan全称Remote Desktop Connection Manager(多远程桌面管理)是微软Windows Live体验团队的主要开发者 Julian ...

  2. 两种常用的数据交换格式:XML和JSON

    不同编程语言之间的数据传输,需要一种通用的数据交换格式,它需要简洁.易于数据储存.快速读取,且独立于各种编程语言.我们往往传输的是文本文件,比如我们都知道的csv(comma seperated va ...

  3. markdown编辑器常用命令

    # 标题H1## 标题H2### 标题H3#### 标题H4##### 标题H5###### 标题H5插入java代码: 以```java表示java代码开始,以```表示代码结束 ```javapu ...

  4. [模板] Kruskal算法 && 克鲁斯卡尔重构树

    克鲁斯卡尔重构树 发现没把板子放上来... 现在放一下 克鲁斯卡尔算法的正确性是利用反证法证明的. 简要地说, 就是如果不加入当前权值最小的边 \(e_1\), 那么之后加入的边和这条边会形成一个环. ...

  5. #381 Div2 Problem C Alyona and mex (思维 && 构造)

    题意 : 题目的要求是构造出一个长度为 n 的数列, 构造条件是在接下来给出的 m 个子区间中, 要求每一个子区间的mex值最大, 然后在这 m 个子区间产生的mex值中取最小的输出, 并且输出构造出 ...

  6. 关于win7系统下gitbook的安装与gitbook editor的配合使用

    1.安装nodejs 2.node -v,可查看node版本: npm -v,可查看npm版本 3.npm install gitbook-cli -g,安装gitbook 此过程经常报错,如果报错, ...

  7. 进程and线程and协程效率对比

    1.进程与进程池的效率对比 多进程:p.start()过程中,只是向操作系统发送一个信号,至于什么时候执行,都是操作系统的事情,操作系统接收到信号时,帮该进程申请一块内存空间+拷贝父进程的地址空间 # ...

  8. java list去重方式,以及效率问题

    之前面试被问到关于java如何去重的问题,当时没怎么留意,今天刚好项目中用到了,所以记录一下. 实体类: /** * 用户类 */ class User{ private String usernam ...

  9. Java并发编程的艺术笔记(一)——volatile和syncronized关键字

    一.线程间的通信 volatile和syncronized关键字 volatile 修饰变量,告知任何对该变量的访问必须从共享内存获取,对它的改变必须同步刷新至共享内存,由此保证可见性. syncro ...

  10. Route53 health check与 Cloudwatch alarm 没法绑定

    原因 即使在控制台创建 创建的alarm会在us-east-1 不会再其他区域,目前route53 metric 在其他区域不存在. 所以使用cloudformation 创建 route53 hea ...