Canvas 使用手册

Marimo_z
2025-04-21 / 0 评论 / 3 阅读 / 正在检测是否收录...

一、Canvas基础

Canvas是HTML5提供的2D绘图API,通过JavaScript动态生成图形、图像及动画。其核心元素是<canvas>标签,默认尺寸300×150像素,可通过width/height属性自定义。

<canvas id="myCanvas" width="800" height="600"></canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d'); // 获取2D上下文

二、基本图形绘制

1. 矩形

  • fillRect(x,y,w,h):填充矩形
  • strokeRect(x,y,w,h):描边矩形
  • clearRect(x,y,w,h):清除区域
ctx.fillStyle = "#FF0000";      // 设置填充色(默认黑色)
ctx.fillRect(10, 10, 150, 100); // 红色填充矩形

ctx.strokeStyle = "red";        // 设置边框颜色(默认黑色)
ctx.lineWidth = 3;              // 设置边框宽度
ctx.strokeRect(50, 50, 80, 80); // 红色边框矩形

ctx.clearRect(45, 45, 60, 60);  // 擦除中心区域

2. 线条与多边形

路径操作需遵循beginPath() → 绘制 → fill/stroke流程:

ctx.beginPath();
ctx.moveTo(20, 20);   // 起点
ctx.lineTo(200, 50);  // 直线
ctx.lineTo(20, 100);  
ctx.closePath();      // 闭合路径
ctx.stroke();         // 描边三角形

3. 圆与圆弧

arc(x,y,r,s,e):圆 / 圆弧

  • xy(圆心坐标)
  • radius(半径)
  • startAngle(起始角度)

    • 0 弧度对应三点钟方向(水平向右),这是 Canvas 的默认角度基准点。
  • endAngle(结束角度)

    • Math.PI/2 表示 90 度(正上方)。
    • Math.PI 表示 180 度(九点钟方向)。
    • Math.PI * 2 表示 360 度,与 startAngle=0 组合时绘制完整圆形
  • counterclockwise(绘制方向):未显式指定

    • false(默认):顺时针方向绘制(从 startAngleendAngle)。
    • true逆时针方向绘制。
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI*2); // 圆心(100,100),半径50
ctx.fillStyle = "blue"; 
ctx.fill(); // 填充圆形

4. 颜色与样式

// 渐变效果
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, "red");
gradient.addColorStop(1, "blue");
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100); // 红到蓝渐变填充

三、贝塞尔曲线

1. 二次贝塞尔曲线

quadraticCurveTo(cpX, cpY, endX, endY)
控制点决定曲线弧度:

ctx.moveTo(50, 200);
ctx.quadraticCurveTo(150, 50, 250, 200); 
ctx.stroke(); // 绘制U型曲线

2. 三次贝塞尔曲线

bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, endX, endY)
双控制点实现复杂曲线:

ctx.moveTo(50, 50);
ctx.bezierCurveTo(20,100, 180,100, 150,50);
ctx.stroke(); // S型曲线

四、鼠标操作

1. 事件监听

canvas.addEventListener('mousedown', startDrawing); // 触发绘制开始,记录起始坐标
canvas.addEventListener('mousemove', draw); // 实时跟踪鼠标轨迹,绘制动态路径 
canvas.addEventListener('mouseup', stopDrawing); // 结束绘制,重置状态

2. 坐标转换

鼠标事件返回的坐标需转换为 Canvas 坐标系:

function getCanvasPos(e) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: e.clientX - rect.left * (canvas.width/rect.width),
    y: e.clientY - rect.top * (canvas.height/rect.height)
  };
}

3. 绘制轨迹示例

let isDrawing = false;
function startDrawing(e) {
  isDrawing = true;
  const pos = getCanvasPos(e);
  ctx.beginPath();
  ctx.moveTo(pos.x, pos.y); 
}

function draw(e) {
  if (!isDrawing) return;
  const pos = getCanvasPos(e);
  ctx.lineTo(pos.x, pos.y);
  ctx.stroke();
}

五、动画实现

1. 动画循环

使用requestAnimationFrame优化性能:

function animate() {
  // 1. 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 2. 更新对象状态(位置、颜色等)
  update();
  
  // 3. 绘制新帧
  draw();
  
  // 4. 递归调用
  requestAnimationFrame(animate);
}

animate(); // 启动动画

2. 控制动画速度

使用时间差(Delta Time)确保动画速度不受帧率影响。

let lastTime = 0;

function animate(timestamp) {
  const deltaTime = timestamp - lastTime;
  lastTime = timestamp;

  update(deltaTime);
  draw();
  requestAnimationFrame(animate);
}

3. 运动示例(弹跳球)

let x = 100, y = 100, dx = 2, dy = -2;
function drawBall() {
  ctx.beginPath();
  ctx.arc(x, y, 10, 0, Math.PI*2);
  ctx.fill();
}

function update() {
  if(x < 0 || x > canvas.width) dx = -dx;
  if(y < 0 || y > canvas.height) dy = -dy;
  x += dx;
  y += dy;
}

function animate() {
  ctx.clearRect(0,0,canvas.width,canvas.height);
  drawBall();
  update();
  requestAnimationFrame(animate);
}

六、高级技巧

1. 渐变与阴影

// 线性渐变
const gradient = ctx.createLinearGradient(0,0,200,0);
gradient.addColorStop(0, "red");
gradient.addColorStop(1, "white");
ctx.fillStyle = gradient;

// 阴影效果
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;

2. 物理效果

重力与反弹:添加重力、碰撞检测等效果。

class Ball {
  constructor(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.vy = 0; // 垂直速度
    this.gravity = 0.5;
    this.bounce = -0.8; // 反弹系数
  }

  update() {
    this.vy += this.gravity;
    this.y += this.vy;

    // 底部碰撞检测
    if (this.y + this.radius > canvas.height) {
      this.y = canvas.height - this.radius;
      this.vy *= this.bounce;
    }
  }

  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fill();
  }
}

3. 性能优化

  • 离屏 Canvas:预渲染复杂图形到离屏 Canvas。
  • 减少重绘区域:仅重绘变化的区域。
  • 避免频繁操作:减少 ctx.save()ctx.restore() 的调用。
  • 使用双缓冲:在内存中绘制完成后一次性渲染到主 Canvas。
1

评论 (0)

取消