canvas知识总结

一. 基本概念

1.<canvas> 元素

  • 是一个双标签元素,默认大小为 300px × 150px。

  • 通过 width 和 height 属性可以设置画布的大小。

  • Canvas 的默认分辨率较低,可以通过 CSS 和 canvas.width/canvas.height 调整。

    2.渲染上下文(Rendering Context)

    • <canvas> 本身只是一个容器,需要通过 JavaScript 获取其渲染上下文来进行绘图。

    • 常见的上下文类型:

      • 2D 上下文:用于绘制 2D 图形(CanvasRenderingContext2D)。

      • WebGL 上下文:用于绘制 3D 图形(基于 OpenGL ES)。

二. 2D 绘图基础

1.获取上下文

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

2.绘制基本形状

  • 矩形

ctx.fillRect(x, y, width, height); // 填充矩形
ctx.strokeRect(x, y, width, height); // 描边矩形
ctx.clearRect(x, y, width, height); // 清除矩形区域

 路径

ctx.beginPath(); // 开始路径
ctx.moveTo(x, y); // 移动到起点
ctx.lineTo(x, y); // 画线到某点
ctx.arc(x, y, radius, startAngle, endAngle); // 画弧
ctx.closePath(); // 闭合路径
ctx.stroke(); // 描边路径
ctx.fill(); // 填充路径

 3.颜色和样式

  •     设置填充颜色:ctx.fillStyle = 'red';
    • 设置描边颜色:ctx.strokeStyle = 'blue';

    • 设置线宽:ctx.lineWidth = 5;

4.文本

ctx.font = '20px Arial'; // 设置字体
ctx.fillText('Hello Canvas', x, y); // 填充文本
ctx.strokeText('Hello Canvas', x, y); // 描边文本

 5.图像

const img = new Image();
img.src = 'image.png';
img.onload = () => {
  ctx.drawImage(img, x, y, width, height); // 绘制图像
};

 三. 高级特性

1.变换

  • 平移:ctx.translate(x, y);

  • 旋转:ctx.rotate(angle);

  • 缩放:ctx.scale(scaleX, scaleY);

  • 保存和恢复状态:

ctx.save(); // 保存当前状态
ctx.restore(); // 恢复到上次保存的状态

2.渐变

  • 线性渐变:

const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;

 径向渐变:

const gradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;

 3.阴影

ctx.shadowColor = 'black';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;

 4.裁剪区域

ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.clip(); // 设置裁剪区域

四. 动画

1.基本动画

  • 使用 requestAnimationFrame 实现动画循环。

  • 示例:

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
  // 绘制内容
  requestAnimationFrame(draw);
}
draw();

 2.性能优化

  • 避免频繁的 clearRect,可以只清除需要更新的区域。
  • 使用离屏 Canvas 预渲染复杂图形。

 五. 事件处理

1.鼠标事件

  • 监听 clickmousemovemousedownmouseup 等事件。

  • 示例:

canvas.addEventListener('click', (e) => {
  const x = e.clientX - canvas.offsetLeft;
  const y = e.clientY - canvas.offsetTop;
  console.log(`Clicked at (${x}, ${y})`);
});

 2.键盘事件

          监听 keydownkeyup 等事件,结合 Canvas 实现交互。

六. WebGL(3D 绘图)

  • 基本概念

    • WebGL 是基于 OpenGL ES 的 3D 绘图 API。

    • 通过 canvas.getContext('webgl') 获取 WebGL 上下文。

    • 需要了解着色器(Shader)、顶点缓冲(VBO)、纹理(Texture)等概念。

  • 简单示例

const gl = canvas.getContext('webgl');
// 设置视口
gl.viewport(0, 0, canvas.width, canvas.height);
// 清除画布
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

七.补充


  1.ctx.arc() 画圆

  • 0, 0: 这是圆心的坐标。第一个0代表x轴的位置,第二个0代表y轴的位置。在这个例子中,圆心位于Canvas坐标的原点(0, 0)。
  • 10: 这是圆的半径。它决定了圆的大小,在这个例子中,圆的半径是10个像素。
  • 0: 这是起始角度,以弧度为单位,从正x轴开始计算。在这个例子中,起始角度为0弧度,意味着从正x轴开始画。
  • Math.PI * 2: 这是结束角度,同样以弧度为单位。Math.PI * 2表示360度,也就是一个完整的圆。因此,该圆将被完整地绘制出来。
  • true: 最后一个布尔值参数指示了绘制方向。如果设置为true,则表示逆时针方向;如果是false,则是顺时针方向。在这个例子中,圆将按照逆时针方向绘制。
ctx.arc(0, 0, 10, 0, Math.PI * 2, true);

2.ctx.save() ctx.restore()的作用

1. ctx.save() 的作用

ctx.save() 用于将当前的绘图状态保存到一个状态栈中。绘图状态包括以下属性:

  • 当前的变换矩阵(translaterotatescale 等)。

  • 当前的绘图样式(strokeStylefillStylelineWidthlineCap 等)。

  • 当前的裁剪区域(clip)。

  • 其他上下文属性(如 globalAlphaglobalCompositeOperation 等)。 

调用 ctx.save() 后,当前的状态会被推入状态栈中,后续的修改不会影响已保存的状态。

2. ctx.restore() 的作用

ctx.restore() 用于从状态栈中弹出最近保存的绘图状态,并将其恢复到当前上下文中。调用 ctx.restore() 后,上下文会恢复到之前保存的状态。

3. 为什么需要 ctx.save() 和 ctx.restore()

在复杂的绘图操作中,我们经常需要临时修改上下文的状态(例如旋转、缩放、改变颜色等),但又不希望这些修改影响后续的绘图操作。通过 ctx.save() 和 ctx.restore(),可以方便地保存和恢复上下文状态,避免手动记录和恢复每个属性。

  ctx.save() //调用 ctx.save() 后,当前的状态会被推入状态栈中,后续的修改不会影响已保存的状态。
        for (let i = 0; i < 12; i++) {
          ctx.beginPath()
          ctx.rotate(Math.PI / 6)
          ctx.moveTo(100, 0) //从距中心100像素处开始
          ctx.lineTo(120, 0) // 画线到距中心120像素处
          ctx.stroke() // 绘制线条
        }
        ctx.restore()

3.浏览器读取本地图片方法

 1.FileReader
 2.URL:createObjectURL() 静态方法

let img = null // 全局变量

function handleFileChange(e) {
  file.value = e.target.files[0]
  //将文件进行base64编码
  /* const reader = new FileReader()
  reader.readAsDataURL(file.value)
  reader.onload = (e) => {
    let result = e.target.result
    console.log(result)
    let img = new Image()
    img.src = result
    img.onload = function () {
      document.body.appendChild(img)
    }
  } */
  // const canvas = document.getElementById('canvas')
  // const ctx = canvas.getContext('2d')

  //生成一个本地的URL
  const url = URL.createObjectURL(file.value)
  img = new Image()
  img.src = url
  img.onload = function () {
    //将图片绘制到这个canvas上
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
    isImageLoaded.value = true //图片加载完毕
    enableDrawing() // 启用绘制功能
  }
}

4.Element:contextmenu 事件

 contextmenu 事件会在用户尝试打开上下文菜单时触发。该事件通常在鼠标点击右键或者按下键盘上的菜单键时被触发。

在后一种情况下,上下文菜单显示在聚焦元素的左下方,除非该元素是树形,在这种情况下,上下文菜单显示在当前行的左下方。

任何没有被禁用的鼠标右击事件(通过调用单击事件的 preventDefault()方法)将会使得 contextmenu 事件在目标元素上被触发。

// 为canvas元素添加一个事件监听器,监听'contextmenu'事件,即用户右击鼠标时触发。
canvas.addEventListener('contextmenu', function (e) {
  // 阻止默认行为,即阻止浏览器默认的上下文菜单出现。
  e.preventDefault();
  
  // 移除任何可能存在的mousemove事件监听器,确保不会发生不必要的交互。
  canvas.onmousemove = null;
  
  // 获取鼠标点击时相对于视口的位置坐标。
  let x = e.clientX; // 鼠标指针距离视口左侧的距离。
  let y = e.clientY; // 鼠标指针距离视口顶部的距离。
  
  // 尝试获取ID为'menu'的现有元素。
  let menu = document.getElementById('menu');
  
  // 如果存在旧的菜单,则将其从DOM中移除,以避免多次创建菜单。
  if (menu) {
    menu.remove(); // 移除旧菜单。
  }
  
  // 创建一个新的div元素用于自定义菜单。
  menu = document.createElement('div');
  
  // 设置新创建的div元素的ID为'menu',以便后续可以找到它。
  menu.id = 'menu';
  
  // 设置菜单样式为固定位置,使其相对于浏览器窗口定位。
  menu.style.position = 'fixed';
  
  // 根据鼠标点击位置设置菜单的top和left样式属性,使菜单出现在鼠标点击处。
  menu.style.top = y + 'px'; // 设置菜单距离页面顶部的位置。
  menu.style.left = x + 'px'; // 设置菜单距离页面左侧的位置。
  
  // 设置菜单的背景颜色。
  menu.style.backgroundColor = '#ccc'; // 浅灰色背景。
  
  // 添加内边距,使文本不紧贴边界。
  menu.style.padding = '5px';
  
  // 设置圆角边框,提高视觉效果。
  menu.style.borderRadius = '5px';
  
  // 设置菜单内部文本内容为“清除所有”。
  menu.innerText = '清除所有';
  
  // 将创建的菜单元素添加到文档的body中,使其可见。
  document.body.appendChild(menu);
  
  // 为菜单添加一个单击事件监听器。
  menu.onclick = function () {
    // 清空shapes数组,假设这是一个保存所有形状数据的数组。
    shapes.length = 0;
    
    // 当菜单被点击后,移除菜单自身。
    menu.remove();
  }
});

八、JavaScript 补充

1.生成一个介于 0 和 (max - min) 之间的随机数。

function getRandom(min, max) {
  return Math.random() * (max - min) + min;
}
  • Math.random():生成一个介于 0(包括)和 1(不包括)之间的随机浮点数。

  • (max - min):计算范围的大小。

  • Math.random() * (max - min):生成一个介于 0 和 (max - min) 之间的随机数。

  • + min:将随机数偏移到 min 和 max 之间。

 2.Math.hypot

 计算二维空间中两个点之间的欧几里得距离

const distance = Math.sqrt(
          Math.pow(pointA.x - pointB.x, 2) + Math.pow(pointA.y - pointB.y, 2)
)
  • const: 这是一个关键字,用来声明一个常量,即一旦赋值后不能被重新赋值或改变的变量。这里distance被声明为常量,意味着它所指向的值在初始化后不应再被改变。

  • Math.sqrt(): 这是JavaScript内置的数学对象Math的一个静态方法,用于返回一个数的平方根。该方法接收一个参数,即要计算平方根的数值。

  • Math.pow(): 同样是Math对象下的一个静态方法,用于返回基数(第一个参数)的指数次幂(第二个参数)。在这里它用来计算每个坐标差的平方。

  • pointA.x, pointB.x, pointA.y, pointB.y: 这些表示的是两个点(pointApointB)各自的x轴和y轴坐标。假定这两个点都是具有xy属性的对象,这些属性分别代表它们在二维平面上的位置。

 

 3.使用 class 关键字定义一个类

 在JavaScript中,class 是一种用于创建对象的语法糖,它基于原型继承机制。通过 class 关键字,可以更清晰、更简洁地定义构造函数和原型方法。

 1)使用 class 关键字定义一个类。
  • 类名通常采用首字母大写的驼峰命名法(如 Point 和 View)。

class Point {
  // 类的内容
}
2)构造函数 (constructor)
  • constructor 是一个特殊的方法,用于创建和初始化类的实例。

  • 当你使用 new 关键字创建类的实例时,constructor 方法会被自动调用。

  • 你可以在 constructor 中定义实例属性。

class Point {
  constructor(r = 10) {
    this.r = r;
    this.x = getRandom(this.r, canvas.width - this.r);
    this.y = getRandom(this.r, canvas.height - this.r);
  }
}
3)实例方法
  • 在类中定义的方法会成为实例方法,每个实例都可以调用这些方法。

  • 实例方法可以访问和操作实例的属性。

class Point {
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
    ctx.fillStyle = "#fff";
    ctx.fill();
  }
}
4) 实例属性
  • 实例属性是通过 this 关键字在 constructor 或实例方法中定义的。

  • 每个实例都有自己独立的属性。

class Point {
  constructor(r = 10) {
    this.r = r;  // 实例属性
    this.x = getRandom(this.r, canvas.width - this.r);  // 实例属性
    this.y = getRandom(this.r, canvas.height - this.r);  // 实例属性
  }
}
5) 类的实例化
  • 使用 new 关键字创建类的实例。

  • 实例化时会调用 constructor 方法。

const point = new Point(15);
6)类的数组初始化
  • 你可以使用 Array.fill() 和 Array.map() 方法来初始化一个包含多个类实例的数组。

this.pointsArr = new Array(points).fill().map(() => new Point(15));
7)类的继承
  • 使用 extends 关键字可以实现类的继承。

  • 子类可以继承父类的属性和方法,并可以重写或扩展它们。

class ChildClass extends ParentClass {
  constructor() {
    super();  // 调用父类的构造函数
  }
}
8) this 的绑定
  • 在类的方法中,this 指向当前实例。

  • 如果方法作为回调函数传递(如 requestAnimationFrame),可能会丢失 this 的上下文。此时可以使用 bind 方法来绑定 this

requestAnimationFrame(this.draw.bind(this));

9)类的静态方法

  • 使用 static 关键字可以定义静态方法。

  • 静态方法属于类本身,而不是类的实例,因此不能通过实例调用。

class Point {
  static staticMethod() {
    console.log('This is a static method');
  }
}
Point.staticMethod();  // 直接通过类名调用
10) 类的封装
  • 类提供了一种封装数据和行为的机制,使得代码更加模块化和易于维护。

  • 通过类,你可以将相关的属性和方法组织在一起,形成一个独立的单元。

11) 类的多态
  • 多态是指子类可以重写父类的方法,从而在调用时表现出不同的行为。

  • 这是面向对象编程中的一个重要概念。

12) 类的使用场景
  • 类非常适合用于创建具有相似属性和行为的对象。

总结
  • class 提供了一种更清晰、更结构化的方式来定义对象和它们的行为。

  • 通过 constructor 可以初始化实例属性。

  • 实例方法可以操作实例属性,并且可以通过 this 访问当前实例。

  • 类可以用于封装数据和行为,使代码更易于维护和扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super毛毛穗

今天晚饭加什么?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值