阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > '<canvas>'标签的基本使用

'<canvas>'标签的基本使用

作者:陈川 阅读数:35657人阅读 分类: HTML

<canvas> 是 HTML5 引入的一个强大元素,允许通过 JavaScript 动态绘制图形、动画和交互式内容。它提供了一种像素级的绘图方式,适用于游戏开发、数据可视化、图像处理等场景。

<canvas> 的基本语法

<canvas> 标签的语法非常简单,只需在 HTML 中声明即可:

<canvas id="myCanvas" width="500" height="300"></canvas>
  • id 属性用于在 JavaScript 中引用该元素。
  • widthheight 属性定义画布的尺寸(单位为像素)。如果不设置,默认宽度为 300px,高度为 150px。

获取绘图上下文

要在 <canvas> 上绘图,必须先获取其绘图上下文(rendering context)。目前主要支持 2D 和 WebGL(3D)两种上下文。以下是获取 2D 上下文的示例:

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

getContext('2d') 返回一个 CanvasRenderingContext2D 对象,提供了丰富的绘图方法。

绘制基本形状

绘制矩形

CanvasRenderingContext2D 提供了三种绘制矩形的方法:

// 填充矩形(填充色由 fillStyle 决定)
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 100, 75);

// 描边矩形(边框色由 strokeStyle 决定)
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.strokeRect(200, 50, 100, 75);

// 清除矩形区域(变为透明)
ctx.clearRect(80, 80, 40, 20);

绘制路径

路径可以绘制任意形状,基本步骤是:

  1. 调用 beginPath() 开始新路径
  2. 使用路径方法(如 moveTo(), lineTo())绘制
  3. 调用 stroke()fill() 渲染
// 绘制三角形
ctx.beginPath();
ctx.moveTo(100, 100);  // 起点
ctx.lineTo(150, 50);   // 第二条边
ctx.lineTo(200, 100);  // 第三条边
ctx.closePath();       // 闭合路径(可选)
ctx.stroke();          // 描边

// 绘制圆形
ctx.beginPath();
ctx.arc(300, 200, 50, 0, Math.PI * 2); // (x, y, 半径, 起始角, 结束角)
ctx.fillStyle = 'green';
ctx.fill();

样式和颜色

颜色设置

ctx.fillStyle = 'orange';       // 颜色名称
ctx.fillStyle = '#FFA500';      // 十六进制
ctx.fillStyle = 'rgb(255,165,0)'; // RGB
ctx.fillStyle = 'rgba(255,165,0,0.5)'; // 带透明度

线型样式

ctx.lineWidth = 5;          // 线宽
ctx.lineCap = 'round';      // 线端样式(butt|round|square)
ctx.lineJoin = 'bevel';     // 转角样式(miter|round|bevel)
ctx.setLineDash([10, 5]);   // 虚线模式(实线长度, 间隙长度)

绘制文本

ctx.font = '30px Arial';    // 字体样式
ctx.fillStyle = 'black';
ctx.fillText('Hello Canvas', 50, 50); // 填充文本

ctx.strokeStyle = 'blue';
ctx.strokeText('Hello Canvas', 50, 100); // 描边文本

文本对齐方式可以通过 textAligntextBaseline 属性调整:

ctx.textAlign = 'center';   // left|right|center|start|end
ctx.textBaseline = 'middle'; // top|hanging|middle|alphabetic|ideographic|bottom

图像操作

绘制图像

const img = new Image();
img.src = 'example.jpg';
img.onload = function() {
  // 绘制原始尺寸
  ctx.drawImage(img, 50, 50);
  
  // 缩放绘制
  ctx.drawImage(img, 200, 50, 100, 80);
  
  // 裁剪并绘制
  ctx.drawImage(img, 
    10, 10, 100, 100,  // 源图像裁剪区域 (sx, sy, sw, sh)
    300, 50, 150, 150  // 画布绘制区域 (dx, dy, dw, dh)
  );
};

像素操作

可以直接操作像素数据:

// 获取像素数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray

// 修改像素(RGBA格式)
for (let i = 0; i < data.length; i += 4) {
  data[i] = 255 - data[i];     // R
  data[i+1] = 255 - data[i+1]; // G
  data[i+2] = 255 - data[i+2]; // B
  // Alpha通道保持不变
}

// 写回修改后的像素
ctx.putImageData(imageData, 0, 0);

变换和状态管理

坐标变换

ctx.translate(100, 100);  // 移动原点
ctx.rotate(Math.PI / 4);  // 旋转45度
ctx.scale(1.5, 1.5);      // 缩放

// 绘制一个旋转的矩形
ctx.fillStyle = 'purple';
ctx.fillRect(0, 0, 50, 30);

保存和恢复状态

save()restore() 方法可以保存/恢复当前的绘图状态(包括样式、变换等):

ctx.fillStyle = 'red';
ctx.save();              // 保存当前状态

ctx.fillStyle = 'blue';
ctx.translate(100, 100);
ctx.fillRect(0, 0, 50, 50); // 蓝色矩形

ctx.restore();           // 恢复之前的状态
ctx.fillRect(50, 50, 50, 50); // 红色矩形

动画实现

通过 requestAnimationFrame 可以实现流畅的动画效果:

let x = 0;

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  ctx.fillStyle = 'red';
  ctx.fillRect(x, 100, 50, 50);
  
  x += 2;
  if (x > canvas.width) x = -50;
  
  requestAnimationFrame(animate);
}

animate();

事件交互

<canvas> 本身是一个 DOM 元素,可以添加事件监听器:

canvas.addEventListener('click', function(event) {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  
  ctx.beginPath();
  ctx.arc(x, y, 10, 0, Math.PI * 2);
  ctx.fillStyle = 'blue';
  ctx.fill();
});

性能优化技巧

  1. 分层渲染:使用多个 <canvas> 叠加,将静态内容和动态内容分开
  2. 避免频繁状态改变:将相同样式的绘制操作集中处理
  3. 离屏渲染:先在内存中的 canvas 绘制复杂图形,再复制到主 canvas
  4. 合理使用 clearRect():只清除需要更新的区域
// 离屏渲染示例
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 100;
offscreenCanvas.height = 100;
const offscreenCtx = offscreenCanvas.getContext('2d');

// 在离屏canvas上绘制复杂图形
offscreenCtx.beginPath();
offscreenCtx.arc(50, 50, 40, 0, Math.PI * 2);
offscreenCtx.fillStyle = 'rgba(255,0,0,0.5)';
offscreenCtx.fill();

// 复制到主canvas
ctx.drawImage(offscreenCanvas, 50, 50);

实际应用示例

绘制图表

// 简单柱状图
const data = [30, 60, 90, 40, 70];
const barWidth = 50;
const spacing = 20;
let x = 50;

ctx.fillStyle = 'steelblue';
data.forEach(value => {
  const height = value * 2;
  ctx.fillRect(x, canvas.height - height, barWidth, height);
  x += barWidth + spacing;
});

// 添加坐标轴
ctx.beginPath();
ctx.moveTo(30, 30);
ctx.lineTo(30, canvas.height - 30);
ctx.lineTo(canvas.width - 30, canvas.height - 30);
ctx.strokeStyle = 'black';
ctx.stroke();

简单画板实现

<canvas id="drawingCanvas" width="600" height="400"></canvas>
<script>
const canvas = document.getElementById('drawingCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;

canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);

function startDrawing(e) {
  isDrawing = true;
  draw(e);
}

function draw(e) {
  if (!isDrawing) return;
  
  ctx.lineWidth = 5;
  ctx.lineCap = 'round';
  ctx.strokeStyle = '#000';
  
  ctx.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
}

function stopDrawing() {
  isDrawing = false;
  ctx.beginPath();
}
</script>

浏览器兼容性注意事项

虽然现代浏览器都支持 <canvas>,但需要注意:

  1. 始终提供后备内容:
<canvas>
  <p>您的浏览器不支持Canvas,请升级浏览器</p>
</canvas>
  1. IE9 以下版本不支持,可以使用 ExplorerCanvas 作为 polyfill

  2. 移动端需要注意触控事件和高分辨率屏幕适配:

// 处理高DPI屏幕
function setupCanvas(canvas) {
  const dpr = window.devicePixelRatio || 1;
  const rect = canvas.getBoundingClientRect();
  
  canvas.width = rect.width * dpr;
  canvas.height = rect.height * dpr;
  
  const ctx = canvas.getContext('2d');
  ctx.scale(dpr, dpr);
  
  return ctx;
}

const ctx = setupCanvas(document.getElementById('myCanvas'));

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌