您现在的位置是:网站首页 > <canvas>-图形绘制画布文章详情

<canvas>-图形绘制画布

<canvas> 是 HTML5 引入的一个原生标签,用于通过 JavaScript 动态绘制图形、动画或交互式可视化内容。它提供了一块空白画布,开发者可以通过 API 直接操作像素,实现从简单几何图形到复杂游戏场景的渲染。

<canvas> 的基本用法

<canvas> 标签本身只是一个容器,需要通过 JavaScript 获取其上下文(context)才能进行绘制。基本语法如下:

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

如果不设置 widthheight 属性,画布默认大小为 300px × 150px。通过 CSS 设置尺寸会导致画布内容拉伸,建议始终使用属性定义画布大小。

绘制基础图形

矩形绘制

<canvas> 提供了三种矩形绘制方法:

ctx.fillRect(50, 50, 100, 80);   // 绘制填充矩形 (x, y, width, height)
ctx.strokeRect(200, 50, 100, 80); // 绘制描边矩形
ctx.clearRect(75, 75, 50, 30);    // 清除矩形区域

路径绘制

复杂图形需要通过路径 API 构建:

ctx.beginPath();                  // 开始新路径
ctx.moveTo(100, 100);             // 移动笔触到起点
ctx.lineTo(200, 100);             // 绘制直线
ctx.arc(150, 150, 50, 0, Math.PI); // 绘制半圆
ctx.closePath();                  // 闭合路径
ctx.stroke();                     // 描边路径

样式与颜色控制

填充与描边样式

ctx.fillStyle = '#FF5733';        // 设置填充色
ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)'; // 半透明描边色
ctx.lineWidth = 5;                // 线宽
ctx.lineCap = 'round';            // 线端样式 (butt|round|square)

渐变与图案

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

// 图案填充
const img = new Image();
img.src = 'texture.png';
img.onload = () => {
  const pattern = ctx.createPattern(img, 'repeat');
  ctx.fillStyle = pattern;
};

文本渲染

<canvas> 支持基础文本绘制:

ctx.font = 'bold 24px Arial';     // 字体设置
ctx.fillText('Hello Canvas', 50, 50); // 填充文本
ctx.strokeText('Outline Text', 50, 100); // 描边文本

// 文本对齐方式
ctx.textAlign = 'center';         // start|end|left|right|center
ctx.textBaseline = 'middle';       // top|hanging|middle|alphabetic|ideographic|bottom

图像操作

可以在画布上绘制图像并处理:

const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
  // 基本绘制
  ctx.drawImage(img, 0, 0);
  
  // 缩放绘制
  ctx.drawImage(img, 100, 100, 200, 150);
  
  // 切片绘制
  ctx.drawImage(img, 
    10, 10, 100, 100,  // 源图像切片区域
    200, 200, 150, 150 // 画布目标区域
  );
};

变形与合成

坐标变换

ctx.save();                       // 保存当前状态
ctx.translate(100, 100);          // 移动原点
ctx.rotate(Math.PI/4);            // 旋转45度
ctx.scale(1.5, 0.8);              // 缩放
ctx.fillRect(0, 0, 50, 50);        // 绘制变形后的矩形
ctx.restore();                    // 恢复之前状态

图像合成

ctx.globalAlpha = 0.7;            // 全局透明度
ctx.globalCompositeOperation = 'xor'; // 合成模式
// 常见模式: source-over, destination-over, multiply, screen, overlay等

动画实现

通过 requestAnimationFrame 实现流畅动画:

let x = 0;
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillRect(x, 100, 50, 50);
  x += 2;
  if (x < canvas.width) {
    requestAnimationFrame(animate);
  }
}
animate();

性能优化技巧

  1. 离屏渲染:复杂图形先在内存中的 canvas 绘制

    const offscreen = document.createElement('canvas');
    const offCtx = offscreen.getContext('2d');
    // ...在offscreen上绘制...
    ctx.drawImage(offscreen, 0, 0);
    
  2. 批量操作:减少状态变更

    // 不好:频繁改变样式
    ctx.fillStyle = 'red'; ctx.fillRect(0,0,50,50);
    ctx.fillStyle = 'blue'; ctx.fillRect(60,0,50,50);
    
    // 好:批量绘制同样式图形
    ctx.fillStyle = 'red';
    ctx.fillRect(0,0,50,50);
    ctx.fillRect(100,0,50,50);
    
  3. 分层渲染:静态内容与动态内容分开绘制

交互实现

通过事件监听实现画布交互:

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  ctx.arc(x, y, 20, 0, Math.PI*2);
  ctx.fill();
});

高级特性

像素级操作

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// 修改像素数据 (RGBA格式)
for (let i = 0; i < data.length; i += 4) {
  data[i] = 255 - data[i];     // 红色通道反相
  data[i+1] = 255 - data[i+1]; // 绿色通道反相
}
ctx.putImageData(imageData, 0, 0);

WebGL 集成

<canvas> 还支持 3D 渲染上下文:

const gl = canvas.getContext('webgl');
if (gl) {
  // WebGL 代码...
}

实际应用示例

绘制图表

function drawBarChart(data) {
  const barWidth = 40;
  const maxValue = Math.max(...data);
  
  data.forEach((value, i) => {
    const barHeight = (value / maxValue) * canvas.height;
    ctx.fillStyle = `hsl(${i * 360 / data.length}, 70%, 50%)`;
    ctx.fillRect(
      i * (barWidth + 10), 
      canvas.height - barHeight,
      barWidth,
      barHeight
    );
  });
}

签名板实现

let isDrawing = false;
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', endDrawing);

function startDrawing(e) {
  isDrawing = true;
  const {x, y} = getCanvasPosition(e);
  ctx.beginPath();
  ctx.moveTo(x, y);
}

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

function endDrawing() {
  isDrawing = false;
}

function getCanvasPosition(e) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  };
}

浏览器兼容性注意事项

  1. 始终检查上下文获取是否成功

    if (!ctx) {
      alert('您的浏览器不支持Canvas');
    }
    
  2. 对于不支持 <canvas> 的旧浏览器:

    <canvas>
      <!-- 后备内容 -->
      <p>您的浏览器不支持HTML5 Canvas</p>
    </canvas>
    
  3. 移动端需要考虑触控事件和高DPI屏幕适配:

    // 高DPI适配
    const dpr = window.devicePixelRatio || 1;
    canvas.width = canvas.clientWidth * dpr;
    canvas.height = canvas.clientHeight * dpr;
    ctx.scale(dpr, dpr);
    

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步