当前位置: 首页 > news >正文

Canvas使用详细教学:从基础绘图到进阶动画再到实战(海报生成、Flappy Bird 小游戏等),掌握绘图与动画的秘诀

在这里插入图片描述

一、Canvas基础

1. Canvas简介

Canvas是HTML5引入的一种基于矢量图形的绘图技术,它是一个嵌入HTML文档中的矩形区域,允许开发者使用JavaScript直接操作其内容进行图形绘制。Canvas元素不包含任何内在的绘图能力,而是提供了一个空白的画布,通过JavaScript调用Canvas API来绘制图形、图像、文字及实现复杂的视觉效果。

<canvas id="myCanvas" width="500" height="500"></canvas>

上述代码定义了一个ID为myCanvas的Canvas元素,宽度为500像素,高度为500像素。这两个属性是必需的,用于确定Canvas在页面上的尺寸。

2. 获取绘图环境

要开始在Canvas上绘图,首先要通过JavaScript获取其2D绘图上下文:

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

getContext('2d')返回一个二维绘图环境对象(CanvasRenderingContext2D),它是所有绘图操作的基础。

二、基础绘图方法

Canvas API提供了丰富的绘图方法,允许开发者在HTML <canvas> 元素上绘制各种图形、线条、文字以及处理图像。以下是对Canvas基础绘图方法的详细解释和实例演示:

1. 绘制直线

方法详解
  • beginPath(): 开始一个新的路径或重置当前路径。

  • moveTo(x, y): 将绘图笔移动到指定的 (x, y) 坐标。

  • lineTo(x, y): 从当前绘图笔位置绘制一条直线到指定的 (x, y) 坐标。

  • stroke(): 描绘当前路径的轮廓。

示例代码
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');// 开始路径
ctx.beginPath();// 移动到起点 (100, 50)
ctx.moveTo(100, 50);// 绘制直线到终点 (300, 100)
ctx.lineTo(300, 100);// 描边路径
ctx.stroke();

2. 绘制矩形

方法详解
  • fillRect(x, y, width, height): 绘制一个填充矩形,左上角坐标为 (x, y),尺寸为 (width, height)

  • strokeRect(x, y, width, height): 绘制一个矩形边框,左上角坐标为 (x, y),尺寸为 (width, height)

示例代码
// 填充矩形
ctx.fillRect(50, .jpg', 150, 100);// 边框矩形
ctx.strokeRect(200, 50, 150, 100);

3. 绘制圆形与弧线

方法详解
  • arc(x, y, radius, startAngle, endAngle, anticlockwise): 绘制一个圆弧或部分圆,中心点坐标为 (x, y),半径为 radius,起始角度为 startAngle(以弧度计),结束角度为 endAngleanticlockwise 参数为 true 表示逆时针方向绘制,否则顺时针。

  • fill(): 填充当前路径。

  • stroke(): 描绘当前路径的轮廓。

示例代码
// 完整圆
ctx.beginPath();
ctx.arc(250, 150, 50, 0, Math.PI * 2); // 0到2π代表整个圆
ctx.fill();// 圆弧
ctx.beginPath();
ctx.arc(400, 150, 50, 0, Math.PI, true); // 绘制半圆,逆时针方向
ctx.stroke();

4. 绘制路径

方法详解
  • quadraticCurveTo(cpX, cpY, x, y): 绘制二次贝塞尔曲线,cpXcpY 为控制点坐标,(x, y) 为目标点坐标。

  • bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, x, y): 绘制三次贝塞尔曲线,cp1Xcp1Ycp2Xcp2Y 分别为两个控制点坐标,(x, y) 为目标点坐标。

  • closePath(): 结束当前路径并自动连接起始点和终点,形成封闭路径。

示例代码
// 二次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(.png', 200);
ctx.quadraticCurveTo(200,, 250, 250);
ctx.stroke();// 三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(400, 200);
ctx.bezierCurveTo(400, 100, 500, 100, 500, 200);
ctx.stroke();

5. 绘制与填充文本

方法详解
  • fillText(text, x, y [, maxWidth]): 在指定 (x, y) 位置绘制填充文本,可选参数 maxWidth 限制文本的最大宽度。

  • strokeText(text, x, y [, maxWidth]): 在指定 (x, y) 位置绘制文本轮廓,可选参数 maxWidth 同上。

  • font: 设置字体样式,如 ctx.font = '24px Arial';

  • textAlign: 设置文本对齐方式,如 ctx.textAlign = 'center';

  • textBaseline: 设置文本基线位置,如 ctx.textBaseline = 'middle';

示例代码
// 设置字体样式和对齐方式
ctx.font = '36px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';// 绘制填充文本
ctx.fillText('Hello, Canvas!', canvas.width / 2, canvas.height / 2);// 绘制文本轮廓
ctx.strokeStyle = 'blue';
ctx.strokeText('Outline Text', 100, 250);

6. 变换操作

方法详解
  • translate(x, y): 平移坐标系,所有后续绘制将基于新的原点 (x, y)

  • rotate(angle): 旋转坐标系,angle 为旋转角度(以弧度计)。

  • scale(xScale, yScale): 缩放坐标系,xScaleyScale 分别为水平和垂直方向的缩放因子。

  • 保存/恢复状态:ctx.save()保存当前绘图状态,ctx.restore()恢复至上一次保存的状态。

示例代码
// 保存当前状态
ctx.save();// 平移、旋转、缩放
ctx.translate(300, 200);
ctx.rotate(Math.PI / 4);
ctx.scale(0.5, 1.5);// 在变换后的坐标系中绘制矩形
ctx.fillRect(-50, -25, 100, 50);// 恢复原坐标系
ctx.restore();

以上就是Canvas基础绘图方法的详细说明和示例代码,通过这些方法可以组合绘制出各种复杂的图形和场景。实际应用中,还需要结合颜色设置、阴影效果、图像操作等其他Canvas API特性,以实现更丰富的视觉效果。

三、颜色与样式

Canvas API 提供了多种方式来设置颜色、填充样式、描边样式以及阴影效果,使开发者能够灵活地装饰和美化绘制的图形。以下是对Canvas颜色与样式设置的详细解释及示例:

1.设置颜色

  • fillStyle: 设置填充颜色,影响fill()方法填充的图形内部颜色。

  • strokeStyle: 设置描边颜色,影响stroke()方法描绘的图形轮廓颜色。

颜色值类型
  • CSS颜色字符串:可以是颜色名(如 'red''blue')、十六进制颜色码(如 '#FF0000''#000')、RGB/RGBA值(如 'rgb(255, 0, 0)''rgba(255, 0, 0, 0.5)')、HSL/HSLA值(如 'hsl(0, 100%, 50%)''hsla(0, 100%, 50%, 0.5)')。
示例代码
// 设置填充颜色为红色,描边颜色为蓝色
ctx.fillStyle = 'red';
ctx.strokeStyle = 'blue';// 绘制一个填充矩形和描边矩形
ctx.fillRect(50, 50, 100, 100);
ctx.strokeRect(200, 50, 100, 100);

2.渐变与图案

  • 线性渐变:使用 createLinearGradient(x1, y1, x2, y2) 创建线性渐变对象,然后通过渐变对象的 addColorStop(position, color) 方法添加颜色停止点。

  • 径向渐变:使用 createRadialGradient(x1, y1, r1, x2, y2, r2) 创建径向渐变对象,同样使用 addColorStop() 添加颜色停止点。

  • 模式(Pattern):使用 createPattern(image, repetition) 创建模式对象,其中 image 是一个HTMLImageElement、HTMLCanvasElement或HTMLVideoElement,repetition 是模式的重复方式(如 'repeat''repeat-x''no-repeat' 等)。

示例代码
// 线性渐变
let gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'yellow');
ctx.fillStyle = gradient;
ctx.fillRect(50, 150, 200, 100);// 径向渐变
let radialGradient = ctx.createRadialGradient(250, 150, 90, 250, 150, ½);
radialGradient.addColorStop(0, 'orange');
radialGradient.addColorStop(1, 'purple');
ctx.fillStyle = radialGradient;
ctx.fillRect(200, 100, 150, 150);// 图案
let img = new Image();
img.src = 'example.png';
img.onload = function () {let pattern = ctx.createPattern(img, 'repeat');ctx.fillStyle = pattern;ctx.fillRect(400, 100, 150, 150);
};

3.设置描边样式

  • lineCap: 设置线端样式,可取值为 'butt'(默认,无延伸)、'round'(圆头)、'square'(方头)。

  • lineJoin: 设置线段连接处样式,可取值为 'miter'(默认,尖角)、'round'(圆角)、'bevel'(斜角)。

  • lineWidth: 设置线条宽度。

  • miterLimit: 当lineJoin'miter'时,设置最大斜接长度。

示例代码
// 设置线端为圆头,连接处为圆角,线宽为10像素
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = 10;// 绘制带圆角的矩形边框
ctx.strokeRect(50, 300, 100, 100);

4.设置阴影

方法详解
  • shadowColor: 设置阴影颜色。

  • shadowOffsetX & shadowOffsetY: 设置阴影相对于形状的水平和垂直偏移量。

  • shadowBlur: 设置阴影模糊半径。

示例代码
// 设置阴影颜色为灰色,偏移量(2, 2),模糊半径为10像素
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 10;// 绘制带有阴影的矩形
ctx.fillRect(200, 300, 100, 100);

通过以上方法,您可以灵活调整Canvas中图形的颜色、填充样式、描边样式以及阴影效果,创造出丰富多样的视觉表现。记得在绘制相关图形之前设置相应的样式属性,因为这些属性通常只影响后续的绘图操作。

四、图像操作

Canvas API 提供了强大的图像处理能力,包括加载、绘制、裁剪、变换、像素级操作等。以下是对Canvas图像操作的详细说明及示例:

1. 加载图像

方法详解
  • 使用new Image()创建一个HTMLImageElement对象。

  • 为图像对象设置src属性,指定图像文件路径。

  • 监听load事件确保图像已加载完成后再进行绘制。

示例代码
let img = new Image();
img.src = 'example.jpg';
img.onload = function () {// 图像加载完成后在此处进行绘制
};

2. 绘制图像

方法详解
  • 使用context.drawImage(image, dx, dy[, dWidth, dHeight])方法绘制图像。参数含义如下:

    • image: 要绘制的HTMLImageElement、HTMLCanvasElement或HTMLVideoElement对象。

    • dx, dy: 目标位置的x和y坐标。

    • dWidth, dHeight: 可选,目标尺寸。如果不指定,图像将以其原始尺寸绘制。

示例代码
// 原始尺寸绘制
ctx.drawImage(img, 50, 50);// 缩放并定位绘制
ctx.drawImage(img, 200, 50, 150, 100); // 缩放到150x100并放置在(200, 50)

3. 图像裁剪

方法详解
  • 利用clip()方法结合路径绘制来实现图像裁剪。先调用beginPath(),接着绘制所需裁剪区域(如矩形、圆形等),最后调用clip()

  • 使用drawImage()仅绘制裁剪后的部分。

示例代码
// 定义裁剪区域
ctx.beginPath();
ctx.rect(100, 100, 200, 150); // 裁剪区域为(100, 100, 200, 150)
ctx.clip();// 绘制完整图像,但只会显示裁剪区域内部分
ctx.drawImage(img, 0, 0);

4. 图像变换

方法详解
  • 使用translate(x, y)rotate(angle)scale(xScale, yScale)对坐标系统进行平移、旋转、缩放。

  • 变换应用于后续的绘图操作,包括绘制图像。

示例代码
// 平移坐标系
ctx.translate(100, 100);// 旋转坐标系
ctx.rotate(Math.PI / 4); // 顺时针旋转45度// 缩放坐标系
ctx.scale(0.5, 0.5); // 缩小至原尺寸的一半// 在变换后的坐标系中绘制图像
ctx.drawImage(img, 0, 0);

5. 像素级操作

方法详解
  • 使用getImageData(sx, sy, sw, sh)获取图像数据,返回一个ImageData对象,包含一个data数组,存储每个像素的RGBA值。

  • 使用putImageData(imagedata, dx, dy[, dirtyX, dirtyY, dirtyWidth, dirtyHeight])将ImageData对象中的数据绘制回Canvas。

示例代码
// 获取图像的一部分数据
let imageData = ctx.getImageData(0, 0, 100, 100);// 修改像素数据(例如反转颜色)
for (let i = 0; i < imageData.data.length; i += 4) {imageData.data[i + 0] = 255 - imageData.data[i + 0]; // RimageData.data[i + 1] = 255 - imageData.data[i + 1]; // GimageData.data[i + 2] = 255 - imageData.data[i + 2]; // B// 不修改 A (i + 3)
}// 将修改后的数据绘制回Canvas
ctx.putImageData(imageData, 0, 0);

6. 图像合成与混合模式

方法详解
  • 使用globalCompositeOperation属性设置图像的合成模式,控制新绘制的内容如何与已有内容交互。
示例代码
// 设置混合模式为“正片叠底”(类似Photoshop中的效果)
ctx.globalCompositeOperation = 'multiply';// 新绘制的图像会与已有内容按照“正片叠底”规则混合
ctx.drawImage(img2, 0, 0);

通过上述操作,您可以对Canvas中的图像进行加载、绘制、裁剪、变换、像素级操作以及利用不同的合成模式实现复杂图像效果。结合实际需求,灵活运用这些方法可以创造出丰富的视觉体验。

五、动画

Canvas动画API提供了创建动态图形和视觉效果所需的工具。以下是对关键API的详细解释以及具体示例,展示如何利用它们来创建动画:

1. 初始化Canvas元素

在HTML中创建一个<canvas>元素作为动画的画布:

<canvas id="myCanvas" width="500" height="500"></canvas>

2. 获取Canvas上下文

通过JavaScript获取Canvas的2D绘图上下文:

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

3. 动画循环:requestAnimationFrame()

requestAnimationFrame(callback)用于启动和维护动画循环。浏览器会在下一次重绘前调用指定的回调函数,确保动画与屏幕刷新同步,达到流畅的效果。

let animationId;function animate(currentTime) {// 更新动画状态或计算下一帧内容update(currentTime);// 清除并重新绘制Canvasrender();// 请求下一帧动画animationId = window.requestAnimationFrame(animate);
}// 启动动画
animate(performance.now());

4. 清除与重绘:clearRect() 和绘图方法

clearRect(x, y, width, height)

清除指定矩形区域内的像素,为下一帧动画做准备。

function render() {ctx.clearRect(0, 0, canvas.width, canvas.height);// ... 绘制新帧内容
}
绘图方法

使用fillRect(), strokeRect(), arc(), fillText(), drawImage(), beginPath(), moveTo(), lineTo(), bezierCurveTo(), quadraticCurveTo()等方法绘制动画帧内容。

5. 动画状态管理

更新动画对象的位置、尺寸、颜色等属性,根据时间、速度、加速度等参数。

function update(currentTime) {const deltaTime = currentTime - lastUpdateTime;// 更新对象位置obj.x += obj.velocityX * deltaTime;obj.y += obj.velocityY * deltaTime;// 检查边界碰撞、速度变化等逻辑handleCollisions();lastUpdateTime = currentTime;
}function handleCollisions() {if (obj.x < 0 || obj.x > canvas.width) {obj.velocityX = -obj.velocityX;}if (obj.y < 0 || obj.y > canvas.height) {obj.velocityY = -obj.velocityY;}
}

6. 变换与动画:transform()setTransform()

transform(a, b, c, d, e, f)

对当前坐标系统应用矩阵变换(平移、旋转、缩放等),影响后续绘图操作。

function updateRotation(currentTime) {const rotationSpeed = Math.PI / 2; // 每秒旋转180度const deltaRotation = rotationSpeed * (currentTime - lastUpdateTime) / 1000;// 更新旋转角度obj.angle += deltaRotation;// 应用旋转ctx.transform(1, 0, 0, 1, 0, 0);ctx.rotate(obj.angle);
}function render() {// 绘制旋转对象ctx.drawImage(rotatableImage, obj.x, obj.y);
}
setTransform(a, b, c, d, e, f)

重置当前坐标系统到单位矩阵,然后应用新的矩阵变换。

7. 动画性能优化

避免不必要的重绘

仅重绘有变化的部分,使用离屏Canvas(OffscreenCanvas)预渲染复杂部分。

使用硬件加速

针对某些CSS属性(如transformopacity)和Canvas的合成操作,浏览器可能会启用硬件加速。

适当降低帧率

非实时性要求不高的动画,可以适当减少requestAnimationFrame()的调用频率。

总结起来,使用Canvas API创建动画包括初始化Canvas、设置动画循环、适时清除和重绘Canvas、更新动画状态、应用变换以及进行性能优化。结合以上API和策略,可以实现从简单到复杂的各种动画效果。

为了更直观地理解如何使用Canvas API创建动画,以下是一些具体的动画实例举例:

实例一:移动的矩形

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas Moving Rectangle</title><style>body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f5f5; }canvas { border: 1px solid #ccc; }</style>
</head>
<body><canvas id="myCanvas" width="400" height="400"></canvas><script>const canvas = document.getElementById('myCanvas');const ctx = canvas.getContext('2d');let rectX = ½ * canvas.width;let rectY = ½ * canvas.height;const rectSize =* canvas.width;const velocity = 5;let directionX = 1;let directionY = 1;function animate() {requestAnimationFrame(animate);// Clear canvasctx.clearRect(0, 0, canvas.width, canvas.height);// Update rectangle positionrectX += velocity * directionX;rectY += velocity * directionY;// Bounce off edgesif (rectX < rectSize || rectX + rectSize > canvas.width) {directionX = -directionX;}if (rectY < rectSize || rectY + rectSize > canvas.height) {directionY = -directionY;}// Draw rectanglectx.fillStyle = '#0095DD';ctx.fillRect(rectX, rectY, rectSize, rectSize);}animate();</script>
</body>
</html>

这个例子中,我们创建了一个在Canvas上不断移动并反弹边缘的蓝色矩形。动画循环中,我们更新矩形的位置,并检查是否触碰到Canvas边缘,若触碰则改变移动方向,最后绘制矩形。

实例二:旋转的圆形

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas Rotating Circle</title><style>body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f5f5; }canvas { border: 1px solid #ccc; }</style>
</head>
<body><canvas id="myCanvas" width="400" height="400"></canvas><script>const canvas = document.getElementById('myCanvas');const ctx = canvas.getContext('2d');let circleX = ½ * canvas.width;let circleY = ½ * canvas.height;const radius =* canvas.width;let rotationAngle = 0;const rotationSpeed = 0.01;function animate() {requestAnimationFrame(animate);// Clear canvasctx.clearRect(0, 0, canvas.width, canvas.height);// Update rotation anglerotationAngle += rotationSpeed;// Rotate and draw circlectx.save();ctx.translate(circleX, circleY);ctx.rotate(rotationAngle);ctx.fillStyle = '#FF5733';ctx.beginPath();ctx.arc(0, 0, radius, 0, 2 * Math.PI);ctx.fill();ctx.restore();}animate();</script>
</body>
</html>

在这个例子中,我们绘制了一个在Canvas中心持续旋转的红色圆形。动画循环中,我们增加旋转角度,然后使用save()translate()rotate()beginPath()arc()fill()方法绘制旋转的圆形,最后使用restore()恢复原坐标系。

实例三:粒子系统

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas Particle System</title><style>body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f5f5; }canvas { border: 1px solid #ccc; }</style>
</head>
<body><canvas id="myCanvas" width="800" height="600"></canvas><script>const canvas = document.getElementById('myCanvas');const ctx = canvas.getContext('2d');const particleCount = ¼ * canvas.width;const particles = [];const colors = ['#FDB813', '#00BFFF', '#8A2BE2', '#DC143C'];function createParticles() {for (let i = 0; i < particleCount; i++) {particles.push({x: Math.random() * canvas.width,y: Math.random() * canvas.height,vx: (Math.random() - 0.5) * 2,vy: (Math.random() - 0.5) * 2,radius: Math.random() * .png,color: colors[Math.floor(Math.random() * colors.length)],});}}function animate() {requestAnimationFrame(animate);ctx.clearRect(0, 0, canvas.width, canvas.height);particles.forEach((particle, index) => {particle.x += particle.vx;particle.y += particle.vy;// Bounce off edgesif (particle.x < 0 || particle.x > canvas.width) {particle.vx = -particle.vx;}if (particle.y < 0 || particle.y > canvas.height) {particle.vy = -particle.vy;}ctx.fillStyle = particle.color;ctx.beginPath();ctx.arc(particle.x, particle.y, particle.radius, 0, 2 * Math.PI);ctx.fill();});}createParticles();animate();</script>
</body>
</html>

这是一个简单的粒子系统动画,创建多个随机位置、速度、半径和颜色的粒子,在Canvas上自由移动并反弹边缘。动画循环中,我们遍历所有粒子,更新其位置并检查边缘碰撞,然后绘制每个粒子。

这些实例展示了如何使用Canvas API创建不同类型的动画效果,包括移动、旋转和复杂的粒子系统。实际应用中,可以根据需求调整动画参数、添加交互逻辑或融合更多视觉效果。

六、进阶技巧

1. 复杂图形与算法

  • 多边形:结合ctx.moveTo()ctx.lineTo()ctx.closePath()绘制自定义多边形。

  • 圆角矩形:使用arcTo()方法或手动绘制四条圆弧实现圆角矩形。

  • 贝塞尔曲线:灵活运用二次、三次贝塞尔曲线构造平滑曲线和复杂形状。

  • 矢量图形:解析SVG路径数据,利用Canvas API绘制SVG路径。

2. 混合模式与像素操作

  • 混合模式:通过ctx.globalCompositeOperation设置不同的图像合成模式(如source-overmultiplyscreen等)。

  • 像素操作:使用ctx.getImageData()获取画布某区域的像素数据,ctx.putImageData()将像素数据绘制回画布,实现滤镜、像素艺术等效果。

3. WebGL集成

虽然本文主要关注Canvas 2D绘图,但若需要更高级的3D图形或硬件加速功能,可以考虑集成WebGL,在Canvas上创建WebGLRenderingContext,使用OpenGL ES规范进行编程。

七、实战项目示例

1.使用Canvas生成海报

以下是一个使用HTML5 Canvas生成海报的实例,包含基本的海报元素如背景图、文字标题、副标题、二维码、以及装饰性图形等。请注意,由于文本环境限制,这里仅提供代码示例,您需要将其复制到一个HTML文件中并在浏览器中运行以查看效果。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas Poster Generator</title><style>body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }canvas { border: 1px solid #ccc; }</style>
</head>
<body><canvas id="posterCanvas" width="800" height="1200"></canvas><script>const canvas = document.getElementById('posterCanvas');const ctx = canvas.getContext('2d');// Poster dataconst backgroundImageUrl = 'https://example.com/path/to/your/background-image.jpg'; // Replace with your actual image URLconst title = 'YOUR POSTER TITLE';const subtitle = 'YOUR POSTER SUBTITLE';const qrCodeImageUrl = 'https://example.com/path/to/your/qrcode-image.png'; // Replace with your actual QR code image URLconst logoImageUrl = 'https://example.com/path/to/your/logo-image.png'; // Replace with your actual logo image URL// Load images asynchronouslyconst backgroundImage = new Image();backgroundImage.src = backgroundImageUrl;backgroundImage.onload = drawPoster;const qrCodeImage = new Image();qrCodeImage.src = qrCodeImageUrl;qrCodeImage.onload = drawPoster;const logoImage = new Image();logoImage.src = logoImageUrl;logoImage.onload = drawPoster;function drawPoster() {// All images loaded, proceed with drawing the posterif (!backgroundImage.complete || !qrCodeImage.complete || !logoImage.complete) return;// Draw background imagectx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);// Draw titlectx.font = 'bold ˜50px sans-serif';ctx.fillStyle = '#333';ctx.textAlign = 'center';ctx.fillText(title, canvas.width / 2, 100);// Draw subtitlectx.font = 'italic 24px sans-serif';ctx.fillStyle = '#666';ctx.fillText(subtitle, canvas.width / 2, 140);// Draw QR codectx.drawImage(qrCodeImage, canvas.width - 150, canvas.height - 150, 100, 100);// Draw logoctx.drawImage(logoImage, 20, canvas.height - ¾ * logoImage.height, logoImage.width, logoImage.height);// Add decorative shapes (e.g., a triangle)ctx.beginPath();ctx.moveTo(100, 200);ctx.lineTo(200, 300);ctx.lineTo(300, 200);ctx.closePath();ctx.fillStyle = '#FDB813';ctx.fill();// Save as an image or use canvas.toDataURL() to get a base64 encoded image// let posterImage = new Image();// posterImage.src = canvas.toDataURL('image/png');// document.body.appendChild(posterImage);}</script>
</body>
</html>

在这个实例中:

  • 首先定义了一个HTML canvas 元素作为海报的画布。
  • 定义了海报所需的各项数据,如背景图URL、标题、副标题、二维码URL、以及logo图URL。
  • 异步加载所需图片资源,并在图片加载完成后触发drawPoster函数。
  • drawPoster函数中:
    • 绘制背景图。
    • 使用指定字体、颜色和对齐方式绘制标题和副标题。
    • 将二维码和logo图片放置在指定位置。
    • 为了演示,还绘制了一个简单的三角形装饰图形。

请注意替换示例中的占位符URL和文本内容为实际的图片和文本数据。运行此代码后,您将在浏览器中看到生成的海报。如果需要进一步操作,如保存为图片或分享到其他平台,您可以使用canvas.toDataURL()方法获取Base64编码的图像数据。

此外,对于更复杂的需求,如动态调整布局、响应式设计、用户交互式编辑等,可能需要结合JavaScript库(如Fabric.js)来简化和增强Canvas的使用体验。如果您需要实现这样的功能,请告知具体需求,以便提供更针对性的帮助。

2. Flappy Bird 小游戏

基于HTML5 Canvas 实现一个Flappy Bird游戏:

玩法

  • 玩家控制一只小鸟在不断上升的管道间飞行,通过点击屏幕使小鸟短暂升空,避免撞到管道或坠落到地面。
  • 每成功穿越一对管道,计分增加;碰撞则游戏结束。

实现要点

  • 使用Canvas绘制背景、小鸟、管道等元素。
  • 监听鼠标点击(或触屏)事件,更新小鸟的垂直速度。
  • 使用物理模拟计算小鸟的运动轨迹,包括重力下降和空气阻力。
  • 定时更新画面,检测小鸟与管道的碰撞,根据碰撞结果更新游戏状态。

需要编写JavaScript代码来处理游戏逻辑、绘制图形以及响应用户交互。以下是一个简化版Flappy Bird实例的核心步骤和关键代码片段:

1. 创建HTML结构

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Flappy Bird</title><style>canvas {display: block;margin: auto;background-color: #70c5ce;}</style>
</head>
<body><canvas id="gameCanvas" width="400" height="600"></canvas><script src="flappy-bird.js"></script>
</body>
</html>

2. 编写flappy-bird.js文件

在这个JavaScript文件中,我们将定义游戏所需的变量、函数和主循环。以下是一些关键部分:

// 获取Canvas元素并创建绘图上下文
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");// 游戏设置
const birdSize = 30;
const pipeGap = 90;
const pipeSpeed = 4;
const gravity = 0.½;// 游戏状态
let birdY = canvas.height / 2 - birdSize / 2;
let birdVY = 0;
let pipes = [];
let score = 0;
let gameOver = false;// 图像资源(这里仅列出函数名,实际需要加载图片资源)
function loadImages() {// 加载鸟、管道等图片资源
}// 绘制函数
function draw() {// 清除画布ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制背景ctx.fillStyle = "#70c5ce";ctx.fillRect(0, 0, canvas.width, canvas.height);// 绘制管道和间隙for (const pipe of pipes) {// 绘制上方管道ctx.drawImage(pipeImage, pipe.x, pipe.top);// 绘制下方管道ctx.drawImage(pipeImage, pipe.x, pipe.bottom);// 如果鸟穿过间隙,增加分数if (pipe.x <= birdX && pipe.x + pipeWidth > birdX && birdY + birdSize >= pipe.top && birdY <= pipe.bottom) {score++;}}// 绘制鸟ctx.drawImage(birdImage, birdX, birdY);// 绘制分数ctx.font = "20px Arial";ctx.fillStyle = "#ffffff";ctx.fillText(`Score: ${score}`, 10, 30);
}// 更新函数
function update() {// 更新鸟的位置birdVY += gravity;birdY += birdVY;// 检查鸟是否碰到地面或管道,如果发生碰撞,标记游戏结束if (birdY + birdSize >= canvas.height || pipes.some(pipe => pipe.x <= birdX && pipe.x + pipeWidth > birdX &&(birdY + birdSize >= pipe.top || birdY <= pipe.bottom))) {gameOver = true;}// 更新管道位置pipes.forEach(pipe => pipe.x -= pipeSpeed);// 如果最左侧管道移出屏幕,移除并添加新的管道if (pipes[0].x + pipeWidth < 0) {pipes.shift();pipes.push(createPipe());}
}// 用户输入处理函数
function handleInput(event) {if (event.type === "click" || event.keyCode === 32) { // 点击或空格键birdVY = -birdJumpVelocity;}
}// 主循环
function gameLoop() {if (!gameOver) {update();draw();}requestAnimationFrame(gameLoop);
}// 初始化游戏
loadImages().then(() => {document.addEventListener("click", handleInput);document.addEventListener("keydown", handleInput);gameLoop();});

以上代码示例展示了Flappy Bird游戏的基本实现框架。实际开发时,需要补充图像资源加载、图像绘制细节、游戏开始/重置逻辑、更精细的碰撞检测以及可能的音效支持等。此外,为了优化性能,可以考虑使用离屏Canvas绘制静态背景、预加载图像资源等技术。

通过这些项目,不仅可以巩固所学知识,还能提升实际应用Canvas解决复杂问题的能力。

八、资源与学习路径

  • 官方文档:查阅MDN Web Docs的Canvas API参考文档,了解详细方法、属性及用法。
  • 在线教程:利用CSDN、W3Schools、Codecademy等平台提供的Canvas教程进行系统学习。
  • 开源项目:研究GitHub上的Canvas相关开源项目源码,借鉴实际项目中的最佳实践。

综上所述,从掌握Canvas基础绘图方法,到深入理解动画原理与帧率控制,再到运用进阶技巧实现复杂效果,通过理论学习与实战练习相结合,您将逐步精通Canvas的使用,为网页、游戏、数据可视化等领域开发丰富多彩的图形与动画内容。

在这里插入图片描述

相关文章:

Canvas使用详细教学:从基础绘图到进阶动画再到实战(海报生成、Flappy Bird 小游戏等),掌握绘图与动画的秘诀

一、Canvas基础 1. Canvas简介 Canvas是HTML5引入的一种基于矢量图形的绘图技术&#xff0c;它是一个嵌入HTML文档中的矩形区域&#xff0c;允许开发者使用JavaScript直接操作其内容进行图形绘制。Canvas元素不包含任何内在的绘图能力&#xff0c;而是提供了一个空白的画布&a…...

【MATLAB 分类算法教程】_2粒子群算法优化支持向量机SVM分类 - 教程和对应MATLAB代码

分类代码案例2:粒子群算法优化支持向量机SVM分类 - MATLAB完全代码教程 1. 初始化代码2. 读取数据代码3.数据预处理代码4.利用粒子群算法PSO求解最佳的SVM参数c和g代码5.根据最佳的参数进行SVM模型训练代码6.SVM模型预测代码7.准确率分析以及分类结果对比作图代码本文以红酒数…...

Vue2电商前台项目(三):完成Search搜索模块业务

目录 一、请求数据并展示 1.写Search模块的接口 2.写Vuex中的search仓库&#xff08;三连环&#xff09; 3.组件拿到search仓库的数据 用getters简化仓库中的数据 4.渲染商品数据到页面 5.search模块根据不同的参数获取数据展示 &#xff08;1&#xff09;把派发action…...

算法思想总结:链表

一、链表的常见技巧总结 二、两数相加 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {//利用t来存进位信息int t0;ListNode*newheadnew ListNode(0);//创建一个哨兵节点&#xff0c;方便尾插List…...

Android Room 记录一个Update语句不生效的问题解决记录

代码展示 1.数据实体类 Entity public class User {PrimaryKey(autoGenerate true)private long id;private String name;private String age;private String sex;public User(String name, String age, String sex) {this.name name;this.age age;this.sex sex;}public …...

使用SpringBoot3+Vue3开发公寓管理系统

项目介绍 公寓管理系统可以帮助公寓管理员更方便的进行管理房屋。功能包括系统管理、房间管理、租户管理、收租管理、房间家具管理、家具管理、维修管理、维修师傅管理、退房管理。 功能介绍 系统管理 用户管理 对系统管理员进行管理&#xff0c;新增管理员&#xff0c;修改…...

有且仅有的10个常见的排序算法,东西不多,怎么就背不下来呢

就这么跟你说吧&#xff0c;面试中肯定会出排序算法的算法题&#xff0c;你只需要背下来代码背下来他们的时间复杂度和空间复杂度就能蒙混过关。 不管你是前端还是后端&#xff0c;关于排序的算法有且仅有这 10个&#xff0c;如果你用心了&#xff0c;怎么会记不住呢。看完这篇…...

Mac安装配置ElasticSearch和Kibana 8.13.2

系统环境&#xff1a;Mac M1 (MacOS Sonoma 14.3.1) 一、准备 从Elasticsearch&#xff1a;官方分布式搜索和分析引擎 | Elastic上下载ElasticSearch和Kibana 笔者下载的是 elasticsearch-8.13.2-darwin-aarch64.tar.gz kibana-8.13.2-darwin-aarch64.tar.gz 并放置到个人…...

javaWeb项目-快捷酒店管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Spring Boot框架 …...

闲不住,手写一个数据库文档生成工具

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 逛博客的时候&#xff0c;发现了一个很有意思的文章&#xff1a;数据库表结构导…...

在群晖上安装GPT4Free

什么是 GPT4Free &#xff1f; GPT4Free 简称 G4F&#xff0c;是一个强大的大型语言模型命令行界面&#xff08;LLM-CLI&#xff09;&#xff0c;旨在去中心化并提供免费访问先进人工智能技术的能力。G4F 的目标是通过提供用户友好和高效的工具&#xff0c;使人工智能民主化&am…...

C# 语言类型(四)—传递参数及其修饰符

总目录 C# 语法总目录 参考链接&#xff1a; C#语法系列:C# 语言类型(一)—预定义类型值之数值类型 C#语法系列:C# 语言类型(二)—预定义类型之字符串及字符类型简述 C#语法系列:C# 语言类型(三)—数组/枚举类型/结构体 C#语法系列:C# 语言类型(四)—传递参数及其修饰符 C#语法…...

刷穿力扣006-剑指offer一数组——02寻找目标值-二维数组

刷穿力扣006-剑指offer<一>数组——02寻找目标值-二维数组 基本面试题都是我带大家刷的力扣热题100和剑指offer的75道题&#xff0c;建议刷两遍&#xff01;&#xff08;ps:想找工作实习的同学&#xff0c;文末有面试八股和简历模板&#xff09; 题目&#xff1a; 语言…...

爬虫(小案例)

点开其中一个链接&#xff0c; http://desk.zol.com.cn/dongman/huoyingrenzhe/&#xff08;前面为浏览器自动补全&#xff0c;在代码里需要自己补全&#xff09; 可以看到图片的下载地址以及打开本图集下一张图片的链接 了解完网站的图片构造后动手写代码&#xff0c;我们筛…...

环信 IM 客户端将适配鸿蒙 HarmonyOS

自华为推出了自主研发操作系统鸿蒙 HarmonyOS 后&#xff0c;国内许多应用软件开始陆续全面兼容和接入鸿蒙操作系统。环信 IM 客户端计划将全面适配统鸿蒙 HarmonyOS &#xff0c;助力开发者快速实现社交娱乐、语聊房、在线教育、智能硬件、社交电商、在线金融、线上医疗等广泛…...

伪元素的使用

.box::after{content: ;display: block;// 定义元素位置margin-top: 12rpx;margin-right: 20rpx;// 定义元素宽高width: 36rpx;height: 36rpx;// background-image无法引用本地资源&#xff0c;故需要用网络地址background-image: url($urlcalendar.png);background-size: 100%…...

TensorFlow学习之:高级应用和扩展

生成对抗网络&#xff1a;了解GAN的基本原理&#xff0c;使用TensorFlow实现简单的GAN 生成对抗网络&#xff08;Generative Adversarial Networks&#xff0c;GAN&#xff09;由两部分组成&#xff1a;生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discrimin…...

maya模板导入动画

maya模板导入动画&#xff0c;第一帧为模板姿态 要将一个FBX文件中的动画数据导入另一个FBX文件的模板&#xff0c;并使得第一帧是模板的初始姿势&#xff0c;第二帧开始是动画&#xff0c;你可以在Maya中采用以下步骤来操作&#xff1a; 步骤 1: 导入模板FBX 首先&#xff…...

【微信小程序之分包】

微信小程序之分包 什么是分包分包的好处分包前的结构图分包后的结构图分包的加载规则分包的体积限制使用分包打包原则引用原则独立分包独立分包的配置方法独立分包的引用原则分包预下载配置分包的预下载分包预下载限制 什么是分包 分包指的是把一个完整小程序项目&#xff0c;…...

STM32-ADC(独立模式、双重模式)

ADC简介 18个通道&#xff1a;外部信号源就是16个GPIO回。在引脚上直接接模拟信号就行了&#xff0c;不需要侄何额外的电路。引脚就直接能测电压。2个内部信号源是内部温度传感器和内部参考电压。 逐次逼近型ADC: 它是一个独立的8位逐次逼近型ADC芯片&#xff0c;这个ADC0809是…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...