数据可视化(三):Canvas

简介: 数据可视化(三):Canvas

1. 什么是 Canvas ?

Canvas 最初由 Apple 于 2004 年 引入,用于 Mac OS X Webkit 组件,为仪表盘小组件和 Safari 浏览器等应用程序提供支持。后来,它被 Gecko 内核的浏览器(尤其是 Mozilla Firefox),Opera 和 Chrome 实现,并被网页超文本应用技术工作小组提议为下一代的网络技术的标准元素(HTML5新增元素)。

Canvas 提供了非常多的 JavaScript绘图 API (比如:绘制路径、举行、圆、文本和图像等方法),与 <canvas>元素可以绘制各种 2D 图形。

Canvas API 主要聚焦于 2D 图形。当然也可以使用 <canvas> 元素对象的 WebGL API 来绘制 2D 和 3D 图形。

Canvas 可用于动画、游戏画面、数据可视化、图片编辑以及实现视频处理等方面。

浏览器兼容性

image.png

  • Canvas 优点:

    • Canvas 提供的功能更原始,适合像素处理,动态渲染和数据量大的绘制,如:图片编辑、热力图、炫光尾迹特效等。
    • Canvas 非常适合图像密集的游戏开发,适合频繁重绘许多的对象。
    • Canvas能够以 .png 或 .jpg 格式保存结果图片,适合对图像进行像素级的处理。
  • Canvas 缺点:

    • 在移动端可能因为 Canvas 数量多,而导致内存占用超出了手机的承受能力,导致浏览器崩溃。
    • Canvas 绘图只能通过 JavaScript 脚本操作 all in js。
    • Canvas 是由一个个像素点构成的图形,放大会使图形变得颗粒状和像素化,导致模糊。

2. Canvas 绘制图形

Canvas 支持两种方式来绘制矩形:矩形方法路径方法

2.1 矩形方法

  • 路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。
  • 除了矩形,其他的图形都是通过一条或者多条路径组合而成的。
  • 通常我们会通过众多的路径来绘制复杂的图形。
  1. fillRect(x, y, width, height): 绘制一个填充的矩形
  2. strokeRect(x, y, width, height): 绘制一个矩形的边框
  3. clearRect(x, y, width, height): 清除指定矩形区域,让清除部分完全透明。

Canvas 绘制一个矩形:

<canvas id="tutorial" width="300" height="300px">
  你的浏览器不兼容Canvas,请升级您的浏览器!
</canvas>

<script>
  window.onload = function() {
    let canvasEl = document.getElementById('tutorial')
    if(!canvasEl.getContext){
      return
    }
    let ctx = canvasEl.getContext('2d') // 2d | webgl
    
    ctx.fillRect(0,0, 100, 50) // 单位也是不用写 px
  }
</script>

2.2 路径方法

使用路径绘制图形的步骤:

  1. 首先需要创建路径起始点(beginPath)。
  2. 然后使用画图命令去画出路径( arc绘制圆弧 、lineTo画直线 )。
  3. 之后把路径闭合( closePath , 不是必须)。
  4. 一旦路径生成,就能通过 描边(stroke)填充路径区域(fill) 来渲染图形。

以下是绘制路径时,所要用到的函数

  • beginPath():新建一条路径,生成之后,图形绘制命令被指向到新的路径上绘图,不会关联到旧的路径
  • closePath():闭合路径之后图形绘制命令又重新指向到 beginPath之前的上下文中。
  • stroke():通过线条来绘制图形轮廓/描边 (针对当前路径图形)
  • fill():通过填充路径的内容区域生成实心的图形 (针对当前路径图形)
<canvas id="tutorial" width="300" height="300px">
  你的浏览器不兼容Canvas,请升级您的浏览器!
</canvas>

<script>
  window.onload = function () {
    let canvasEl = document.getElementById("tutorial");
    if (!canvasEl.getContext) {
      return;
    }
    let ctx = canvasEl.getContext("2d"); // 2d | webgl

    // 1.创建一个路径
    ctx.beginPath();
    // 2.绘图指令
    // ctx.moveTo(0, 0)
    // ctx.rect(100, 100, 100, 50);
    ctx.moveTo(100, 100);
    ctx.lineTo(200, 100);
    ctx.lineTo(200, 150);
    ctx.lineTo(100, 150);

    // 3.闭合路径
    ctx.closePath();
    // 4.填充和描边
    ctx.stroke();
  };
</script>

lineTo 和 arc 两个函数结合既能绘制直线也能绘制圆弧,因此路径方法还可以绘制许多图形,比如三角形、菱形、梯形、椭圆形、圆形等等。。。

3. Canvas 样式和颜色

3.1 色彩 Colors

如果我们想要给图形上色,有两个重要的属性可以做到:

  • fillStyle = color: 设置图形的填充颜色,需在 fill() 函数前调用
  • strokeStyle = color: 设置图形轮廓的颜色,需在 stroke() 函数前调用

:::warning{title="注意"}
一旦设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值。

如果你要给图形上不同的颜色,你需要重新设置 fillStyle 或 strokeStyle 的
:::

3.2 透明度 Transparent

除了可以绘制实色图形,我们还可以用 canvas 来绘制半透明的图形。

  1. 方式一:strokeStyle 和 fillStyle属性结合RGBA:
// 指定透明颜色,用于描边和填充样式
ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.fillStyle = "rgba(255,0,0,0.5)";
  1. 方式二:globalAlpha 属性
// 针对于Canvas中所有的图形生效
ctx.globalAlpha = 0.3

// 2.修改画笔的颜色
// ctx.fillStyle = 'rgba(255, 0, 0, 0.3)'
ctx.fillRect(0,0, 100, 50) // 单位也是不用写 px

ctx.fillStyle = 'blue'
ctx.fillRect(200, 0, 100, 50)

ctx.fillStyle = 'green' // 关键字, 十六进制, rbg , rgba
ctx.beginPath()
ctx.rect(0, 100, 100, 50)
ctx.fill()

:::warning{title="注意"}
globalAlpha = 0 ~ 1

✓ 这个属性影响到 canvas 里所有图形的透明度

✓ 有效的值范围是 0.0(完全透明)到 1.0(完全不透明),默认是 1.0。
:::

3.3 线型 Line styles

调用lineTo()函数绘制的线条,是可以通过一系列属性来设置线的样式。

  1. lineWidth = value: 设置线条宽度。
  2. lineCap = type: 设置线条末端样式。
  3. lineJoin = type: 设定线条与线条间接合处的样式。
  4. ......
  • lineWidth

    • 设置线条宽度的属性值必须为正数。默认值是 1.0px,不需单位。( 零、负数、Infinity和NaN值将被忽略
    • 线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半
    • 如果你想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,你会得到像第二幅图一样的结果。

    ✓ 路径的两边个各延伸半个像素填充并渲染出1像素的线条(深蓝色部分)
    ✓ 两边剩下的半个像素又会以实际画笔颜色一半色调来填充(浅蓝部分)
    ✓ 实际画出线条的区域为(浅蓝和深蓝的部分),填充色大于1像素了,这就是为何宽度为 1.0 的线经常并不准确的原因。

    • 要解决这个问题,必须对路径精确的控制。如,1px的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从 (3.5 ,1) 到 (3.5, 5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为 1.0 的线条。

image.png

  • lineCap: 属性的值决定了线段端点显示的样子。它可以为下面的三种的其中之一:

    • butt 截断,默认是 butt。
    • round 圆形
    • square 正方形
  • lineJoin: 属性的值决定了图形中线段连接处所显示的样子。它可以是这三种之一:

    • round 圆形
    • bevel 斜角
    • miter 斜槽规,默认是 miter。

image.png

3.4 绘制文本

canvas 提供了两种方法来渲染文本:

  • fillText(text, x, y [, maxWidth])
    ✓ 在 (x,y) 位置,填充指定的文本
    ✓ 绘制的最大宽度(可选)。
  • strokeText(text, x, y [, maxWidth])
    ✓ 在 (x,y) 位置,绘制文本边框
    ✓ 绘制的最大宽度(可选)。

文本的样式(需在绘制文本前调用)

  • font = value: 当前绘制文本的样式。这个字符串使用和 CSS font 属性相同的语法。默认的字体是:10px sans-serif
  • textAlign = value:文本对齐选项。可选的值包括:start, end, left, right or center. 默认值是 start
  • textBaseline = value:基线对齐选项。可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom
    ✓ 默认值是 alphabetic。
<canvas id="tutorial" width="300" height="300px">
  你的浏览器不兼容Canvas,请升级您的浏览器!
</canvas>

<script>
  window.onload = function () {
    let canvasEl = document.getElementById("tutorial");
    if (!canvasEl.getContext) {
      return;
    }
    let ctx = canvasEl.getContext("2d"); // 2d | webgl

    ctx.font = "60px sen-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.strokeStyle = "red";
    ctx.fillStyle = "red";

    // 将字体绘制在 100, 100 这个坐标点
    ctx.fillText("Ay", 100, 100);
    // ctx.strokeText("Ay", 100, 100);
  };
</script>

image.png

3.5 绘制图片

绘制图片,可以使用 drawImage 方法将它渲染到 canvas 里。drawImage 方法有三种形态:

  • drawImage(image, x, y)

    • 其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标
  • drawImage(image, x, y, width, height)

    • 这个方法多了 2 个参数:width 和 height,这两个参数用来控制 当向 canvas 画入时应该缩放的大小
  • drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

    • 第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。其它 8 个参数最好是参照右边的图解,前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小

image.png

:::info{title="图片的来源"}

  1. HTMLImageElement:这些图片是由Image()函数构造出来的,或者任何的 <img> 元素。
  2. HTMLVideoElement:用一个 HTML 的 <video> 元素作为你的图片源,可以从视频中抓取当前帧作为一个图像。
  3. HTMLCanvasElement:可以使用另一个 <canvas> 元素作为你的图片源。
  4. 等等

:::

4. Canvas 状态和形变

4.1 Canvas 绘画状态-保存和恢复

Canvas 绘画状态是当前绘画时所产生的样式和变形的一个快照,Canvas 在绘画时,会产生相应的绘画状态,其实我们是可以将某些绘画的状态存储在栈中来为以后复用,Canvas 绘画状态的可以调用 save 和 restore 方法是用来保存和恢复,这两个方法都没有参数,并且它们是成对存在的。

保存和恢复(Canvas)绘画状态

  1. save():保存画布 (canvas) 的所有绘画状态
  2. restore():恢复画布 (canvas) 的所有绘画状态

Canvas绘画状态包括:

  1. 当前应用的变形(即移动,旋转和缩放)
  2. 以及这些属性strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, font, textAlign, textBaseline......
  3. 当前的裁切路径(clipping path)

4.2 移动 - translate

translate方法,它用来移动 canvas 和它的原点到一个不同的位置。

  • translate(x, y)

    • x 是左右偏移量,y 是上下偏移量(无需单位)。
  • 移动 canvas 原点的好处

    • 如不使用 translate方法,那么所有矩形默认都将被绘制在相同的(0,0)坐标原点。
    • translate方法可让我们任意放置图形,而不需要手工一个个调整坐标值。
  • 移动矩形案例

    • 第一步:先保存一下canvas当前的状态
    • 第二步:在绘制图形前translate移动画布
    • 第三步:开始绘制图形,并填充颜色
<script>
///1.形变( 没有保存状态)
ctx.translate(100, 100);
ctx.fillRect(0, 0, 100, 50); // 单位也是不用写 px

ctx.translate(100, 100);
ctx.strokeRect(0, 0, 100, 50);
</script>
<script>
// 2.形变(保存形变之前的状态)
ctx.save();
ctx.translate(100, 100);
ctx.fillRect(0, 0, 100, 50); // 单位也是不用写 px
ctx.restore(); // 恢复了形变之前的状态( 0,0)

ctx.save(); // (保存形变之前的状态)
ctx.translate(100, 100);
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 50, 30);
ctx.restore();
</script>

image.png

4.3 移动 - rotate

rotate方法,它用于以原点为中心旋转 canvas,即沿着 z轴 旋转。

rotate(angle)

  • 只接受一个参数:旋转的角度 (angle),它是顺时针方向,以弧度为单位的值。
  • 角度与弧度的 JS 表达式:弧度=( Math.PI / 180 ) * 角度 ,即 1角度 = Math.PI/180 个弧度。
  • 比如:旋转90°:Math.PI / 2; 旋转180°:Math.PI ; 旋转360°:Math.PI * 2; 旋转-90°:-Math.PI / 2;
  • 旋转的中心点始终是 canvas 的原坐标点,如果要改变它,我们需要用到 translate方法。
<SCRIPT>
// 保存形变之前的状态
ctx.save()
// 1.形变
ctx.translate(100, 100)
// 360 -> Math.PI * 2
// 180 -> Math.PI
// 1 -> Math.PI / 180
// 45 -> Math.PI / 180 * 45
ctx.rotate(Math.PI / 180 * 45)
ctx.fillRect(0, 0, 50, 50) 

// ctx.translate(100, 0)
// ctx.fillRect(0, 0, 50, 50)
// 绘图结束(恢复形变之前的状态)
ctx.restore()


ctx.save()
ctx.translate(100, 0)
ctx.fillRect(0, 0, 50, 50)
ctx.restore()

// ....下面在继续写代码的话,坐标轴就是参照的是原点了
<SCRIPT>

image.png

4.4 移动 - scale

scale(x, y) 方法可以缩放画布。可用它来增减图形在 canvas 中的像素数目,对图形进行缩小或者放大。x 为水平缩放因子,y 为垂直缩放因子,也支持负数。

<script>
// 保存形变之前的状态
ctx.save()
// 1.形变
ctx.translate(100, 100) // 平移坐标系统
ctx.scale(2, 2) // 对坐标轴进行了放大(2倍)
ctx.translate(10, 0) // 10px  -> 20px
ctx.fillRect(0, 0, 50, 50)
// 绘图结束(恢复形变之前的状态)
ctx.restore()

// ....下面在继续写代码的话,坐标轴就是参照的是原点了
</script>

image.png

5. Canvas 动画和案例

5.1 Canvas 动画

Canvas绘图都是通过JavaScript 去操控的,如要实现一些交互性动画是相当容易的。我们可以使用 setIntervalsetTimeoutrequestAnimationFrame 三种方法来定期执行指定函数进行重绘。

Canvas 画出一帧动画的基本步骤(如要画出流畅动画,1s 需绘60帧)

  • 第一步:用 clearRect 方法清空 canvas ,除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。
  • 第二步:保存 canvas 状态,如果加了 canvas 状态的设置(样式,变形之类的),又想在每画一帧之时都是原始状态的话,

你需要先保存一下,后面再恢复原始状态。

  • 第三步:绘制动画图形(animated shapes) ,即绘制动画中的一帧。
  • 第四步:恢复 canvas 状态,如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

5.2 案例:太阳系动画效果

2.gif

代码

<script>
  window.onload = function () {
    let canvasEl = document.getElementById("tutorial");
    if (!canvasEl.getContext) {
      return;
    }
    let ctx = canvasEl.getContext("2d"); // 2d | webgl

    let sun = new Image();
    sun.src = "../../images/canvas_sun.png";
    // sun.onload = function() {
    //   // draw
    // }
    let earth = new Image();
    earth.src = "../../images/canvas_earth.png";
    let moon = new Image();
    moon.src = "../../images/canvas_moon.png";

    requestAnimationFrame(draw);

    /**
   1秒钟会回调 61次
  */
    function draw() {
      console.log("draw");
      ctx.clearRect(0, 0, 300, 300);
      ctx.save();
      // 1.绘制背景
      drawBg();
      // 2.地球
      drawEarth();
      ctx.restore();
      requestAnimationFrame(draw);
    }

    function drawBg() {
      ctx.save();
      ctx.drawImage(sun, 0, 0); // 背景图
      ctx.translate(150, 150); // 移动坐标
      ctx.strokeStyle = "rgba(0, 153, 255, 0.4)";
      ctx.beginPath(); // 绘制轨道
      ctx.arc(0, 0, 105, 0, Math.PI * 2);
      ctx.stroke();
      ctx.restore();
    }

    function drawEarth() {
      let time = new Date();
      let second = time.getSeconds();
      let milliseconds = time.getMilliseconds();
      ctx.save(); // earth start
      ctx.translate(150, 150); // 中心点坐标系
      // 地球的旋转
      // Math.PI * 2  一整个圆的弧度
      // Math.PI * 2 / 60   分成 60 份
      // Math.PI * 2 / 60   1s
      // Math.PI * 2 / 60 / 1000    1mm

      // 1s 1mm
      // Math.PI * 2 / 60 * second + Math.PI * 2 / 60 / 1000 * milliseconds
      ctx.rotate(
        ((Math.PI * 2) / 10) * second +
          ((Math.PI * 2) / 10 / 1000) * milliseconds
      );
      ctx.translate(105, 0); // 圆上的坐标系
      ctx.drawImage(earth, -12, -12);
      // 3.绘制月球
      drawMoon(second, milliseconds);
      // 4.绘制地球的蒙版
      drawEarthMask();

      ctx.restore(); // earth end
    }

    function drawMoon(second, milliseconds) {
      ctx.save(); // moon start
      // 月球的旋转
      // Math.PI * 2   一圈   360
      // Math.PI * 2 / 10  1s(10s一圈)
      // Math.PI * 2 / 10 * 2  2s(10s一圈)

      // Math.PI * 2 / 10 / 1000  1mm 的弧度

      // 2s + 10mm = 弧度
      //  Math.PI * 2 / 10  * second + Math.PI * 2 / 10 / 1000 * milliseconds

      ctx.rotate(
        ((Math.PI * 2) / 2) * second +
          ((Math.PI * 2) / 2 / 1000) * milliseconds
      );
      ctx.translate(0, 28);
      ctx.drawImage(moon, -3.5, -3.5);
      ctx.restore(); // moon end
    }

    function drawEarthMask() {
      // 这里的坐标系是哪个? 圆上的坐标系
      ctx.save();
      ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
      ctx.fillRect(0, -12, 40, 24);
      ctx.restore();
    }
  };
</script>
相关文章
|
数据可视化 定位技术
svg数据可视化地图实战笔记
svg数据可视化地图实战笔记
265 0
|
Web App开发 数据可视化 BI
数据可视化D3系列——饼状图
饼状图是数据统计中经常用到的另一类图表,饼图可以直观地显示一个数据系列中各项的大小与各项总和的比例,本文将使用D3上手制作一个简单的饼状图 什么是布局 布局是D3中非常重要的内容,有了布局D3才能画出复杂的矢量图。但布局并不是直接绘制图形,只是将初始数据转换成容易画图的图形语言,画图工具能读懂图形语言来进行绘制。 在绘制饼状图中,例如有一组数据[1, 2, 3],只依靠这些数据是画不出的,需要将这些数据转化为圆形的起始角度和终止角度,第一块的角度区域为[0, π/3],第二块的角度区域为[π/3, π]……绘制工具能根据这些角度值进行绘制。「布局只进行数据转换」 D3还提供其他常用图表的
数据可视化D3系列——饼状图
|
24天前
|
数据可视化 JavaScript 前端开发
数据可视化进阶:D3.js在复杂数据可视化中的应用
【10月更文挑战第26天】数据可视化是将数据以图形、图表等形式呈现的过程,帮助我们理解数据和揭示趋势。D3.js(Data-Driven Documents)是一个基于JavaScript的库,使用HTML、SVG和CSS创建动态、交互式的数据可视化。它通过数据驱动文档的方式,将数据与DOM元素关联,提供高度的灵活性和定制性,适用于复杂数据的可视化任务。 示例代码展示了如何使用D3.js创建一个简单的柱状图,展示了其基本用法。D3.js的链式调用和回调函数机制使代码简洁易懂,支持复杂的布局和交互逻辑。
64 3
|
5月前
|
数据可视化 Python
利用Matplotlib绘制数据可视化图表
**摘要:** 本文介绍了Python的绘图库Matplotlib在数据分析和科学计算中的重要性。Matplotlib是一个开源库,提供类似MATLAB的接口,支持静态、动态和交互式图表的绘制,并能保存为多种格式。文章详细讲解了Matplotlib的基本用法,包括安装库、导入模块和绘制简单折线图的步骤。还展示了如何绘制柱状图并添加数据标签。通过这些例子,读者可以了解如何利用Matplotlib进行数据可视化,并对其进行自定义以满足特定需求。
55 4
|
数据可视化
第5章 数据可视化——5.1 图形绘制
第5章 数据可视化——5.1 图形绘制
|
XML 人工智能 数据可视化
svg数据可视化地图开发中遇到的问题总结
svg数据可视化地图开发中遇到的问题总结
284 0
|
XML 编解码 人工智能
数据可视化(四):Svg
数据可视化(四):Svg
数据可视化(四):Svg
|
SQL 数据可视化 算法
数据可视化—绘制简单的折线图
在使用matplotlib绘制简单的折线图之前首先需要安装matplotlib,直接在pycharm终端pip install matplotlib即可
258 0
数据可视化—绘制简单的折线图
|
前端开发 JavaScript 数据可视化
数据可视化——从0-1实现折线图(二)
画XY坐标轴 坐标轴本质上就是两条直线,所以第一步确定坐标原点,然后以坐标原点画出垂直和水平的两条直线。我们设置坐标原点离画布的左内边距和底部内边距,这样我们可以通过画布的高度减去底部内边距得到 原点的y, 然后通过画布的宽度减去左内边距得到x, 有了坐标原点画坐标轴就没什么大问题了。代码如下: //定义坐标轴相对于画布的内边距 this.paddingLeft = 30 // 至少大于绘制文字的宽度 this.paddingBottom = 30 // 至少大于绘制文字的高度 this.origin = new Point2d( this.paddingLeft,
数据可视化——从0-1实现折线图(二)
|
前端开发 数据可视化 JavaScript
数据可视化——从0-1实现折线图(一)
前言 终于又到周末了,上一周的一篇3d文章 带你入门three.js——从0到1实现一个3d可视化地图很开心😺收到了这么多小伙伴的喜欢,这是对我知识输出的肯定。再次感谢大家!这周我又来了,这次给大家分享一下可视化图表比较简单的图表📈但同时我们又不得不学会的 那就是————「折线图」。读完本篇文章你可以学到什么 js实现直线方程 折线图的表达 canvas的一些api灵活的运用 直线折线图 我们先去非常有名的Echarts 官网看一看,他的折线图是什么样子的?如图: 图片 echats折线图 从图中可以得到以下2d图形元素: 直线(两个端点是圆的) 直线(两个端点是直线的) 文字 好
数据可视化——从0-1实现折线图(一)
下一篇
无影云桌面