|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
HTML5 Canvas是HTML5标准中最重要的技术之一,它提供了一个通过JavaScript绘制图形的API。Canvas可以用于绘制图形、创建动画、游戏开发、数据可视化、图像处理等多种场景。与SVG不同,Canvas是基于像素的位图绘制技术,一旦绘制完成,就不再记住绘制的图形,而是作为像素存在。这使得Canvas在处理大量图形和复杂动画时具有性能优势。
Canvas基础
Canvas是HTML5中的一个元素,我们可以通过在HTML文档中添加<canvas>标签来创建一个画布。
- <canvas id="myCanvas" width="800" height="600"></canvas>
复制代码
上面的代码创建了一个宽度为800像素,高度为600像素的Canvas元素。要在这个Canvas上绘制图形,我们需要获取Canvas的2D渲染上下文:
- const canvas = document.getElementById('myCanvas');
- const ctx = canvas.getContext('2d');
复制代码
getContext('2d')方法返回一个CanvasRenderingContext2D对象,这个对象提供了大量的方法和属性用于绘制图形。
Canvas使用一个二维坐标系,左上角为原点(0,0),x轴向右延伸,y轴向下延伸。这与我们常见的数学坐标系不同,y轴是向下而不是向上。
基本图形绘制
线条
绘制线条需要用到beginPath()、moveTo()、lineTo()和stroke()方法:
- // 开始一条新路径
- ctx.beginPath();
- // 将画笔移动到起点(50, 50)
- ctx.moveTo(50, 50);
- // 画一条线到(200, 50)
- ctx.lineTo(200, 50);
- // 画一条线到(200, 200)
- ctx.lineTo(200, 200);
- // 描边路径
- ctx.stroke();
复制代码
矩形
Canvas提供了三种绘制矩形的方法:fillRect()、strokeRect()和clearRect():
- // 填充矩形
- ctx.fillRect(50, 50, 200, 100);
- // 描边矩形
- ctx.strokeRect(300, 50, 200, 100);
- // 清除矩形区域
- ctx.clearRect(100, 75, 100, 50);
复制代码
圆形
绘制圆形需要使用arc()方法:
- ctx.beginPath();
- ctx.arc(150, 150, 50, 0, Math.PI * 2); // 圆心(150, 150),半径50,从0到2π
- ctx.stroke(); // 描边
- // 或者
- ctx.fill(); // 填充
复制代码
路径
路径是Canvas中强大的绘图工具,可以创建复杂的形状:
- ctx.beginPath();
- ctx.moveTo(50, 50);
- ctx.lineTo(150, 50);
- ctx.lineTo(100, 150);
- ctx.closePath(); // 连接起点和终点,形成闭合路径
- ctx.stroke();
复制代码
颜色与样式
填充和描边颜色
我们可以使用fillStyle和strokeStyle属性设置填充和描边的颜色:
- ctx.fillStyle = 'red';
- ctx.fillRect(50, 50, 200, 100);
- ctx.strokeStyle = 'blue';
- ctx.lineWidth = 5; // 设置线宽
- ctx.strokeRect(300, 50, 200, 100);
复制代码
颜色可以使用多种格式指定:
• 颜色名称:’red’, ‘blue’, ‘green’等
• 十六进制:’#FF0000’, ‘#00FF00’, ‘#0000FF’等
• RGB:’rgb(255, 0, 0)‘, ‘rgb(0, 255, 0)’, ‘rgb(0, 0, 255)‘等
• RGBA:’rgba(255, 0, 0, 0.5)‘等,最后一个参数是透明度
渐变
Canvas支持线性渐变和径向渐变:
- // 线性渐变
- const linearGradient = ctx.createLinearGradient(50, 50, 250, 50);
- linearGradient.addColorStop(0, 'red');
- linearGradient.addColorStop(1, 'blue');
- ctx.fillStyle = linearGradient;
- ctx.fillRect(50, 50, 200, 100);
- // 径向渐变
- const radialGradient = ctx.createRadialGradient(400, 100, 10, 400, 100, 50);
- radialGradient.addColorStop(0, 'red');
- radialGradient.addColorStop(1, 'blue');
- ctx.fillStyle = radialGradient;
- ctx.fillRect(350, 50, 100, 100);
复制代码
图案
我们可以使用图像作为填充图案:
- const img = new Image();
- img.src = 'pattern.png';
- img.onload = function() {
- const pattern = ctx.createPattern(img, 'repeat');
- ctx.fillStyle = pattern;
- ctx.fillRect(50, 200, 200, 100);
- };
复制代码
文本绘制
Canvas提供了绘制文本的方法:
- ctx.font = '30px Arial';
- ctx.fillText('Hello World', 50, 50); // 填充文本
- ctx.strokeText('Hello World', 50, 100); // 描边文本
复制代码
我们可以设置文本的对齐方式:
- ctx.textAlign = 'center'; // 可以是 'left', 'right', 'center', 'start', 'end'
- ctx.textBaseline = 'middle'; // 可以是 'top', 'bottom', 'middle', 'alphabetic', 'hanging'
- ctx.fillText('Aligned Text', canvas.width / 2, canvas.height / 2);
复制代码
我们还可以测量文本的宽度:
- const text = 'Hello World';
- const metrics = ctx.measureText(text);
- console.log('Text width:', metrics.width);
复制代码
图像处理
绘制图像
Canvas可以绘制图像:
- const img = new Image();
- img.src = 'image.jpg';
- img.onload = function() {
- // 绘制图像到(50, 50),宽度为200,高度为150
- ctx.drawImage(img, 50, 50, 200, 150);
- };
复制代码
裁剪
我们可以使用路径来定义裁剪区域:
- // 创建一个圆形裁剪区域
- ctx.beginPath();
- ctx.arc(150, 150, 50, 0, Math.PI * 2);
- ctx.clip();
- // 现在绘制的任何内容都只会显示在圆形区域内
- const img = new Image();
- img.src = 'image.jpg';
- img.onload = function() {
- ctx.drawImage(img, 100, 100, 100, 100);
- };
复制代码
像素操作
Canvas允许我们直接操作像素:
- // 获取图像数据
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
- const data = imageData.data;
- // 修改像素数据
- for (let i = 0; i < data.length; i += 4) {
- // 红色通道
- data[i] = 255 - data[i]; // 反转红色
- // 绿色通道
- data[i + 1] = 255 - data[i + 1]; // 反转绿色
- // 蓝色通道
- data[i + 2] = 255 - data[i + 2]; // 反转蓝色
- // alpha通道
- // data[i + 3] = 255; // 不修改alpha
- }
- // 将修改后的图像数据放回Canvas
- ctx.putImageData(imageData, 0, 0);
复制代码
变换
Canvas支持多种变换操作,包括平移、旋转和缩放:
平移
- ctx.save(); // 保存当前状态
- ctx.translate(100, 100); // 将原点移动到(100, 100)
- ctx.fillRect(0, 0, 50, 50); // 现在矩形的位置是相对于新原点的
- ctx.restore(); // 恢复之前保存的状态
复制代码
旋转
- ctx.save();
- ctx.translate(150, 150); // 先将原点移动到旋转中心
- ctx.rotate(Math.PI / 4); // 旋转45度
- ctx.fillRect(-25, -25, 50, 50); // 矩形中心在原点
- ctx.restore();
复制代码
缩放
- ctx.save();
- ctx.translate(150, 150);
- ctx.scale(2, 0.5); // x轴放大2倍,y轴缩小到0.5倍
- ctx.fillRect(-25, -25, 50, 50);
- ctx.restore();
复制代码
组合变换
我们可以组合多种变换:
- ctx.save();
- ctx.translate(150, 150);
- ctx.rotate(Math.PI / 4);
- ctx.scale(2, 2);
- ctx.fillRect(-25, -25, 50, 50);
- ctx.restore();
复制代码
动画基础
使用Canvas创建动画的基本方法是使用requestAnimationFrame函数:
- let x = 0;
- let y = 0;
- let dx = 2;
- let dy = 3;
- function animate() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制一个移动的球
- ctx.beginPath();
- ctx.arc(x, y, 20, 0, Math.PI * 2);
- ctx.fill();
-
- // 更新位置
- x += dx;
- y += dy;
-
- // 边界检测
- if (x + 20 > canvas.width || x - 20 < 0) {
- dx = -dx;
- }
- if (y + 20 > canvas.height || y - 20 < 0) {
- dy = -dy;
- }
-
- requestAnimationFrame(animate);
- }
- animate();
复制代码
高级动画技术
缓动函数
缓动函数可以使动画更加自然:
- // 线性缓动
- function linear(t) {
- return t;
- }
- // 二次缓入
- function easeInQuad(t) {
- return t * t;
- }
- // 二次缓出
- function easeOutQuad(t) {
- return t * (2 - t);
- }
- // 二次缓入缓出
- function easeInOutQuad(t) {
- return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
- }
- // 使用缓动函数的动画
- let startTime = null;
- const duration = 2000; // 2秒
- const startX = 0;
- const endX = canvas.width;
- const startY = canvas.height / 2;
- const endY = canvas.height / 2;
- function animateWithEasing(timestamp) {
- if (!startTime) startTime = timestamp;
- const elapsed = timestamp - startTime;
- const progress = Math.min(elapsed / duration, 1);
-
- // 使用缓动函数
- const easedProgress = easeInOutQuad(progress);
-
- // 计算当前位置
- const x = startX + (endX - startX) * easedProgress;
- const y = startY + (endY - startY) * easedProgress;
-
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.beginPath();
- ctx.arc(x, y, 20, 0, Math.PI * 2);
- ctx.fill();
-
- if (progress < 1) {
- requestAnimationFrame(animateWithEasing);
- }
- }
- requestAnimationFrame(animateWithEasing);
复制代码
物理模拟
我们可以模拟简单的物理效果,如重力、摩擦力等:
- let ball = {
- x: canvas.width / 2,
- y: 50,
- radius: 20,
- vx: 2, // x方向速度
- vy: 0, // y方向速度
- gravity: 0.2, // 重力加速度
- damping: 0.8, // 反弹衰减
- friction: 0.99 // 摩擦力
- };
- function animatePhysics() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 应用重力
- ball.vy += ball.gravity;
-
- // 更新位置
- ball.x += ball.vx;
- ball.y += ball.vy;
-
- // 边界检测和反弹
- if (ball.x + ball.radius > canvas.width) {
- ball.x = canvas.width - ball.radius;
- ball.vx = -ball.vx * ball.damping;
- } else if (ball.x - ball.radius < 0) {
- ball.x = ball.radius;
- ball.vx = -ball.vx * ball.damping;
- }
-
- if (ball.y + ball.radius > canvas.height) {
- ball.y = canvas.height - ball.radius;
- ball.vy = -ball.vy * ball.damping;
-
- // 应用摩擦力
- ball.vx *= ball.friction;
- } else if (ball.y - ball.radius < 0) {
- ball.y = ball.radius;
- ball.vy = -ball.vy * ball.damping;
- }
-
- // 绘制球
- ctx.beginPath();
- ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
- ctx.fill();
-
- requestAnimationFrame(animatePhysics);
- }
- animatePhysics();
复制代码
粒子系统
粒子系统可以创建复杂的效果,如烟花、雨、雪等:
- // 粒子类
- class Particle {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.vx = (Math.random() - 0.5) * 5;
- this.vy = (Math.random() - 0.5) * 5;
- this.radius = Math.random() * 3 + 1;
- this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
- this.alpha = 1;
- this.decay = Math.random() * 0.02 + 0.01;
- }
-
- update() {
- this.x += this.vx;
- this.y += this.vy;
- this.alpha -= this.decay;
- }
-
- draw(ctx) {
- ctx.save();
- ctx.globalAlpha = this.alpha;
- ctx.fillStyle = this.color;
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
- ctx.fill();
- ctx.restore();
- }
- }
- // 粒子系统
- const particles = [];
- function createParticles(x, y, count) {
- for (let i = 0; i < count; i++) {
- particles.push(new Particle(x, y));
- }
- }
- function animateParticles() {
- ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-
- // 随机创建新粒子
- if (Math.random() < 0.1) {
- createParticles(
- Math.random() * canvas.width,
- Math.random() * canvas.height,
- 10
- );
- }
-
- // 更新和绘制粒子
- for (let i = particles.length - 1; i >= 0; i--) {
- const particle = particles[i];
- particle.update();
- particle.draw(ctx);
-
- // 移除透明度为0的粒子
- if (particle.alpha <= 0) {
- particles.splice(i, 1);
- }
- }
-
- requestAnimationFrame(animateParticles);
- }
- animateParticles();
复制代码
性能优化
减少重绘
减少不必要的重绘可以显著提高性能:
- // 不好的做法:每一帧都清除整个Canvas
- function animate() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- // 绘制很多对象
- requestAnimationFrame(animate);
- }
- // 好的做法:只清除需要更新的区域
- function animateOptimized() {
- // 只清除上一帧的对象所在区域
- ctx.clearRect(lastX - radius, lastY - radius, radius * 2, radius * 2);
-
- // 绘制新位置的对象
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.fill();
-
- // 更新上一帧的位置
- lastX = x;
- lastY = y;
-
- requestAnimationFrame(animateOptimized);
- }
复制代码
离屏Canvas
使用离屏Canvas可以预先绘制复杂的图形,然后将其绘制到主Canvas上:
- // 创建离屏Canvas
- const offscreenCanvas = document.createElement('canvas');
- offscreenCanvas.width = 200;
- offscreenCanvas.height = 200;
- const offscreenCtx = offscreenCanvas.getContext('2d');
- // 在离屏Canvas上绘制复杂图形
- function drawComplexShape(ctx) {
- // 绘制复杂图形
- ctx.fillStyle = 'blue';
- ctx.beginPath();
- ctx.moveTo(100, 0);
- for (let i = 0; i < 8; i++) {
- const angle = (i * Math.PI * 2) / 8;
- const x = 100 + Math.cos(angle) * 80;
- const y = 100 + Math.sin(angle) * 80;
- ctx.lineTo(x, y);
-
- const innerAngle = ((i + 0.5) * Math.PI * 2) / 8;
- const innerX = 100 + Math.cos(innerAngle) * 40;
- const innerY = 100 + Math.sin(innerAngle) * 40;
- ctx.lineTo(innerX, innerY);
- }
- ctx.closePath();
- ctx.fill();
- }
- drawComplexShape(offscreenCtx);
- // 在主Canvas上使用离屏Canvas
- function animateWithOffscreen() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 旋转并绘制离屏Canvas
- ctx.save();
- ctx.translate(canvas.width / 2, canvas.height / 2);
- ctx.rotate(Date.now() * 0.001);
- ctx.drawImage(offscreenCanvas, -100, -100);
- ctx.restore();
-
- requestAnimationFrame(animateWithOffscreen);
- }
- animateWithOffscreen();
复制代码
分层Canvas
对于复杂的场景,可以使用多个Canvas分层渲染:
- <div style="position: relative; width: 800px; height: 600px;">
- <canvas id="backgroundLayer" width="800" height="600" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
- <canvas id="gameLayer" width="800" height="600" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
- <canvas id="uiLayer" width="800" height="600" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
- </div>
复制代码- const backgroundCanvas = document.getElementById('backgroundLayer');
- const backgroundCtx = backgroundCanvas.getContext('2d');
- const gameCanvas = document.getElementById('gameLayer');
- const gameCtx = gameCanvas.getContext('2d');
- const uiCanvas = document.getElementById('uiLayer');
- const uiCtx = uiCanvas.getContext('2d');
- // 绘制背景(不经常更新)
- function drawBackground() {
- backgroundCtx.fillStyle = '#87CEEB'; // 天蓝色
- backgroundCtx.fillRect(0, 0, backgroundCanvas.width, backgroundCanvas.height);
-
- // 绘制地面
- backgroundCtx.fillStyle = '#8B4513'; // 棕色
- backgroundCtx.fillRect(0, backgroundCanvas.height - 50, backgroundCanvas.width, 50);
- }
- drawBackground();
- // 游戏层(经常更新)
- let playerX = 400;
- let playerY = 300;
- function drawGame() {
- gameCtx.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
-
- // 绘制玩家
- gameCtx.fillStyle = 'red';
- gameCtx.beginPath();
- gameCtx.arc(playerX, playerY, 20, 0, Math.PI * 2);
- gameCtx.fill();
- }
- // UI层(不经常更新)
- function drawUI() {
- uiCtx.fillStyle = 'white';
- uiCtx.font = '20px Arial';
- uiCtx.fillText('Score: 0', 10, 30);
- }
- drawUI();
- // 游戏循环
- function gameLoop() {
- // 更新游戏状态
- playerX += (Math.random() - 0.5) * 5;
- playerY += (Math.random() - 0.5) * 5;
-
- // 边界检查
- playerX = Math.max(20, Math.min(gameCanvas.width - 20, playerX));
- playerY = Math.max(20, Math.min(gameCanvas.height - 70, playerY));
-
- // 只重绘游戏层
- drawGame();
-
- requestAnimationFrame(gameLoop);
- }
- gameLoop();
复制代码
实战案例
下面是一个综合应用前面所学知识的实战案例:一个简单的打砖块游戏。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Breakout Game</title>
- <style>
- body {
- margin: 0;
- padding: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
- background-color: #f0f0f0;
- }
- canvas {
- border: 1px solid #333;
- background-color: #fff;
- }
- </style>
- </head>
- <body>
- <canvas id="gameCanvas" width="800" height="600"></canvas>
- <script>
- const canvas = document.getElementById('gameCanvas');
- const ctx = canvas.getContext('2d');
- // 游戏状态
- let gameRunning = true;
- let score = 0;
- let lives = 3;
- // 球
- const ball = {
- x: canvas.width / 2,
- y: canvas.height - 100,
- radius: 10,
- dx: 4,
- dy: -4,
- speed: 4
- };
- // 挡板
- const paddle = {
- width: 100,
- height: 15,
- x: (canvas.width - 100) / 2,
- y: canvas.height - 30,
- speed: 8,
- dx: 0
- };
- // 砖块
- const brickRowCount = 5;
- const brickColumnCount = 9;
- const brickWidth = 75;
- const brickHeight = 20;
- const brickPadding = 10;
- const brickOffsetTop = 60;
- const brickOffsetLeft = 35;
- const bricks = [];
- // 初始化砖块
- function initBricks() {
- for (let c = 0; c < brickColumnCount; c++) {
- bricks[c] = [];
- for (let r = 0; r < brickRowCount; r++) {
- bricks[c][r] = {
- x: 0,
- y: 0,
- status: 1,
- color: `hsl(${r * 40}, 70%, 50%)`
- };
- }
- }
- }
- // 键盘控制
- let rightPressed = false;
- let leftPressed = false;
- document.addEventListener('keydown', keyDownHandler);
- document.addEventListener('keyup', keyUpHandler);
- document.addEventListener('mousemove', mouseMoveHandler);
- function keyDownHandler(e) {
- if (e.key === 'Right' || e.key === 'ArrowRight') {
- rightPressed = true;
- } else if (e.key === 'Left' || e.key === 'ArrowLeft') {
- leftPressed = true;
- }
- }
- function keyUpHandler(e) {
- if (e.key === 'Right' || e.key === 'ArrowRight') {
- rightPressed = false;
- } else if (e.key === 'Left' || e.key === 'ArrowLeft') {
- leftPressed = false;
- }
- }
- function mouseMoveHandler(e) {
- const relativeX = e.clientX - canvas.offsetLeft;
- if (relativeX > 0 && relativeX < canvas.width) {
- paddle.x = relativeX - paddle.width / 2;
- }
- }
- // 碰撞检测
- function collisionDetection() {
- for (let c = 0; c < brickColumnCount; c++) {
- for (let r = 0; r < brickRowCount; r++) {
- const b = bricks[c][r];
- if (b.status === 1) {
- if (ball.x > b.x &&
- ball.x < b.x + brickWidth &&
- ball.y > b.y &&
- ball.y < b.y + brickHeight) {
- ball.dy = -ball.dy;
- b.status = 0;
- score++;
-
- // 检查是否所有砖块都被击破
- if (score === brickRowCount * brickColumnCount) {
- alert('恭喜你,赢了!');
- document.location.reload();
- }
- }
- }
- }
- }
- }
- // 绘制球
- function drawBall() {
- ctx.beginPath();
- ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
- ctx.fillStyle = '#0095DD';
- ctx.fill();
- ctx.closePath();
- }
- // 绘制挡板
- function drawPaddle() {
- ctx.beginPath();
- ctx.rect(paddle.x, paddle.y, paddle.width, paddle.height);
- ctx.fillStyle = '#0095DD';
- ctx.fill();
- ctx.closePath();
- }
- // 绘制砖块
- function drawBricks() {
- for (let c = 0; c < brickColumnCount; c++) {
- for (let r = 0; r < brickRowCount; r++) {
- if (bricks[c][r].status === 1) {
- const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
- const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
- bricks[c][r].x = brickX;
- bricks[c][r].y = brickY;
- ctx.beginPath();
- ctx.rect(brickX, brickY, brickWidth, brickHeight);
- ctx.fillStyle = bricks[c][r].color;
- ctx.fill();
- ctx.closePath();
- }
- }
- }
- }
- // 绘制分数
- function drawScore() {
- ctx.font = '16px Arial';
- ctx.fillStyle = '#0095DD';
- ctx.fillText('分数: ' + score, 8, 20);
- }
- // 绘制生命值
- function drawLives() {
- ctx.font = '16px Arial';
- ctx.fillStyle = '#0095DD';
- ctx.fillText('生命: ' + lives, canvas.width - 65, 20);
- }
- // 更新游戏状态
- function update() {
- if (!gameRunning) return;
- // 移动挡板
- if (rightPressed && paddle.x < canvas.width - paddle.width) {
- paddle.x += paddle.speed;
- } else if (leftPressed && paddle.x > 0) {
- paddle.x -= paddle.speed;
- }
- // 移动球
- ball.x += ball.dx;
- ball.y += ball.dy;
- // 球碰到左右墙壁
- if (ball.x + ball.dx > canvas.width - ball.radius || ball.x + ball.dx < ball.radius) {
- ball.dx = -ball.dx;
- }
- // 球碰到上墙壁
- if (ball.y + ball.dy < ball.radius) {
- ball.dy = -ball.dy;
- }
- // 球碰到挡板
- else if (ball.y + ball.dy > paddle.y - ball.radius &&
- ball.x > paddle.x &&
- ball.x < paddle.x + paddle.width) {
- // 根据球击中挡板的位置改变反弹角度
- const hitPos = (ball.x - paddle.x) / paddle.width;
- ball.dx = 8 * (hitPos - 0.5);
- ball.dy = -ball.dy;
- }
- // 球碰到底部
- else if (ball.y + ball.dy > canvas.height - ball.radius) {
- lives--;
- if (!lives) {
- alert('游戏结束');
- document.location.reload();
- } else {
- ball.x = canvas.width / 2;
- ball.y = canvas.height - 100;
- ball.dx = 4;
- ball.dy = -4;
- paddle.x = (canvas.width - paddle.width) / 2;
- }
- }
- // 碰撞检测
- collisionDetection();
- }
- // 绘制游戏
- function draw() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- drawBricks();
- drawBall();
- drawPaddle();
- drawScore();
- drawLives();
- }
- // 游戏循环
- function gameLoop() {
- update();
- draw();
- requestAnimationFrame(gameLoop);
- }
- // 初始化游戏
- initBricks();
- gameLoop();
- </script>
- </body>
- </html>
复制代码
这个打砖块游戏综合运用了Canvas的多种技术:
• 基本图形绘制(球、挡板、砖块)
• 颜色和样式(填充颜色)
• 文本绘制(分数和生命值)
• 动画(使用requestAnimationFrame)
• 碰撞检测
• 用户输入处理(键盘和鼠标)
• 游戏状态管理
总结与展望
HTML5 Canvas是一个强大的网页图形绘制技术,它提供了丰富的API用于创建各种图形、动画和交互式应用。从基本的图形绘制到复杂的动画效果,Canvas都能胜任。
随着Web技术的发展,Canvas的应用场景越来越广泛,包括:
• 数据可视化:图表、地图等
• 游戏:2D游戏、简单的3D游戏
• 图像处理:滤镜、特效等
• 创意编程:生成艺术、交互式艺术等
未来,随着WebGL和WebGPU等技术的发展,网页图形处理能力将进一步提升,Canvas也将继续演进,为开发者提供更强大的工具和更好的性能。
掌握Canvas技术,不仅能让你创建出丰富多彩的网页应用,还能为你打开一扇通往图形编程和游戏开发的大门。希望这篇文章能帮助你全面了解和掌握HTML5 Canvas的核心技术,从基础到高级应用,成为一名优秀的网页图形开发者。 |
|