当前位置: 首页 > 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是…...

03.卸载MySQL

卸载MySQL 1.Windows卸载MySQL8 停止服务 用命令停止或者在服务中停止都可以 net stop mysql&#xff08;服务名字可以去服务里面看一下&#xff09;控制面板卸载MySQL 卸载MySQL8.0的程序可以和其他桌面应用程序一样直接在控制面板选择卸载程序&#xff0c;并在程序列表中…...

2024.4.13 蓝桥杯软件类C++B组山东省赛 小记

大三老狗了 &#xff0c; 还是把精力放在考研上了 &#xff0c;所以只是蓝桥杯的前一晚上把常用算法翻了翻。 其实还做了一场小模拟&#xff0c;两个题分值200分我狂砍了17分&#xff0c;bfs写半小时写不明白&#xff0c;所以晚上已经是心如死灰了&#xff0c;所以就早早睡觉了…...

Windows下IntelliJ IDEA远程连接服务器中Hadoop运行WordCount(详细版)

使用IDEA直接运行Hadoop项目&#xff0c;有两种方式&#xff0c;分别是本地式&#xff1a;本地安装HadoopIDEA&#xff1b;远程式&#xff1a;远程部署Hadoop&#xff0c;本地安装IDEA并连接&#xff0c; 本文介绍第二种。 一、安装配置Hadoop (1)虚拟机伪分布式 见上才艺&a…...

【每日刷题】Day16

【每日刷题】Day16 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 2. 160. 相交链表 - 力扣&…...

【K8s】:在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从)

【K8s】&#xff1a;在 Kubernetes 集群中部署 MySQL8.0 高可用集群&#xff08;1主2从&#xff09; 一、准备工作二、搭建nfs服务器2.1 安装 NFS 服务器软件包&#xff08;所有节点执行&#xff09;2.2 设置共享目录2.3 启动 NFS 服务器2.4 设置防火墙规则&#xff08;可选&am…...

Vue内置组件TransitionGroup详细介绍

<TransitionGroup> 是一个内置组件&#xff0c;用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。 和 <Transition> 的区别​ <TransitionGroup> 支持和 <Transition> 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器…...

【机器学习300问】71、神经网络中前向传播和反向传播是什么?

我之前写了一篇有关计算图如何帮助人们理解反向传播的文章&#xff0c;那为什么我还要写这篇文章呢&#xff1f;是因为我又学习了一个新的方法来可视化前向传播和反向传播&#xff0c;我想把两种方法总结在一起&#xff0c;方便我自己后续的复习。对了顺便附上往期文章的链接方…...

【ZZULIOJ】1067: 有问题的里程表(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy 提示 code 题目描述 某辆汽车有一个里程表&#xff0c;该里程表可以显示一个整数&#xff0c;为该车走过的公里数。然而这个里程表有个毛病&#xff1a;它总是从3变到5&#xff0c;而跳过数字4&#xff0c;里程表…...

A21 STM32_HAL库函数 之 I2c通用驱动程序 -- B -- 所有函数的介绍及使用

A21 STM32_HAL库函数 之 I2c通用驱动程序 -- B -- 所有函数的介绍及使用 1 该驱动函数预览1.12 HAL_I2C_Master_Sequential_Receive_IT1.13 HAL_I2C_Slave_Transmit_IT1.14 HAL_I2C_Slave_Receive_IT1.15 HAL_I2C_Slave_Sequential_Transmit_IT1.16 HAL_I2C_Slave_Sequential_R…...

简介:Asp.Net Core进阶高级编程教程

课程简介目录 &#x1f680;前言一、课程背景二、课程目的三、课程特点四、课程适合人员六、最后 &#x1f680;前言 本文是《.Net Core进阶编程课程》教程专栏的导航站&#xff08;点击链接&#xff0c;跳转到专栏主页&#xff0c;欢迎订阅&#xff0c;持续更新…&#xff09…...

网站主机与服务器吗/seo服务建议

2019独角兽企业重金招聘Python工程师标准>>> 记录自己的一次历程&#xff0c;最终结果并不是最优&#xff0c;但是具有可操作性 首先介绍使用k均值算法 然后介绍k个块状空间的弊端 最后介绍两倍距离查找最短距离的方法&#xff0c;同时指出其弊端 以二维平面为例&am…...

娄底高端网站建设/交换友情链接推广法

python中通过pip安装库文件时出现“EnvironmentError: [WinError 5] 拒绝访问”的问题及解决方案,原因,报错,已经有了,解决方法,试了 python中通过pip安装库文件时出现“EnvironmentError: [WinError 5] 拒绝访问”的问题及解决方案 易采站长站&#xff0c;站长之家为您整理了p…...

wordpress 访问密码/长沙关键词优化服务

遇到一个这样的问题在hibernate应用时&#xff0c;Qurey对象qurey Query query ses.createQuery(HQL); query.setDate("endTime",new Date()); 这个时候的new Date()被截去了时分秒 解决办法 Query query ses.createQuery(HQL); query.setTimestamp("endT…...

wordpress 好玩的插件/深圳今天重大事件新闻

一个案例教会你如何用报表管理质量&#xff0c; 实现卓越绩效&#xff01; &#xff08;一&#xff09;企业背景 某家公司曾经是我们国家最大的煤机制造企业之一&#xff0c;煤炭行业曾经有一段是五年一个波动。 第一个高潮的时候&#xff0c;它很有钱&#xff0c;搞多元化…...

手机访问网站下面电话怎么做/最好的搜索引擎

原理 上面这幅图&#xff0c;存在两类线性可分的数据&#xff0c;在两个数据集之间存在无数个可进行分割的超平面&#xff0c;而SVM的目的是找到几何间隔最大的超平面&#xff0c;且这个超平面是唯一存在的。如上图的超平面WTX b 0&#xff0c;就是上图数据集的几何间隔最大超…...

福州网站建设服务价格最实惠/软件开发公司有哪些

一&#xff1a;sqlserver 字段类型详解 bit 整型 bit数据类型是整型&#xff0c;其值只能是0、1或空值。这种数据类型用于存储只有两种可能值的数据&#xff0c;如Yes 或No、True 或False 、On 或Off. 注意&#xff1a;很省空间的一种数据类型&#xff0c;如果能够满足需求应该…...