在讲解优美水果模型之前,作者想为大家分享下水果模型是如何动起来的,以及我们是如何基本绘制的😎
水果的运动
一个二维物体,为了方便其运动,我们可以建系来控制其轨迹。
是不是梦回高中数学了呢🤓🐣
废话少说,直接上代码:
class GameObject {
constructor(x, y, type) { // 构造函数
this.x = x; // 初始化x坐标
this.y = y; // 初始化y坐标
this.type = type; // 初始化类型
this.sliced = false; // 初始化切割状态
this.velocityX = Math.random() * 5 - 4; // 初始化x方向速度
this.velocityY = -15 - Math.random() * 3; // 初始化y方向速度
this.gravity = 0.3; // 初始化重力
this.rotation = 0; // 初始化旋转角度
this.rotationSpeed = Math.random() * 0.5 - 0.05; // 初始化旋转速度
定义了一个名为 GameObject 的 JavaScript 类,它用于创建具有特定属性和行为的游戏对象。
很明显,先用constructor来构造函数
x,y对应初始坐标(二维坐标系)
type:定义对象类型,其值由constructor传入的参数决定。
this.sliced=false,false为布尔属性,这里表示默认未被切割。
• this.velocityX 和 this.velocityY 分别表示对象在 x 方向和 y 方向上的速度。
• Math.random() 返回一个 0(包含)到 1(不包含)之间的随机数。
• Math.random() * 5 - 4 的结果范围是 -4 到 1,表示 x 方向的速度在 -4 到 1 之间随机变化。
• -15 - Math.random() * 3 的结果范围是 -18 到 -15,表示 y 方向的速度始终为负(向上移动),且在 -18 到 -15 之间随机变化。
• this.rotation 表示对象的旋转角度,初始值为 0。
• this.rotationSpeed 表示对象的旋转速度,其值由 Math.random
()*0.5- 0.05 计算得出,范围是-0.05 到 0.45。
•这意味着对象在创建时会随机获得一个旋转速度,可能是顺时针或逆时针旋转。
总结:
1. 位置:由x和y坐标确定。
2. 类型:由type 属性标识。
3. 状态:是否被切割(sliced)。
4. 运动:具有水平和垂直方向的速度(velocityX 和 velocityY),受到重力影响(gravity)。
5. 旋转:具有初始旋转角度(rotation) 和随机的旋转速度(rotationSpeed).
反正该有的我觉得都有了😋🫵
绘图
在绘制前,作者为大家介绍一位新朋友
canvas api
HTML5 Canvas 是一种通过 JavaScript 绘制图形的技术。它在网页上提供了一个绘图区域,开发者可以使用 Canvas API 在这个区域上绘制各种图形、图像和文本,实现丰富的视觉效果。
1. canvas 元素:
- 在 HTML 中,使用 <canvas>标签定义绘图区域。
示例
<canvas id="myCanvas" width="800" height="600"></canvas>
width 和 height属性定义了画布的宽度和高度。
2. 绘图上下文(Context): 通过 canvas.getContext('2d') 获取 2D 绘图上下文对象。
示例:
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
绘制图形
矩形
填充矩形:
ctx.fillRect(x, y, width, height);
x、y:矩形左上角的坐标。
width、height:矩形的宽度和高度。
描边矩形:
ctx.strokeRect(x, y, width, height);
路径
开始路径:
ctx.beginPath();
移动画笔:
ctx.moveTo(x, y);
绘制直线:
ctx.lineTo(x, y);
绘制曲线:
具体贝塞尔曲线画法及原理 可以看作者的 致敬童年系列--切水果2
二次贝塞尔曲线:
ctx.quadraticCurveTo(cpx, cpy, x, y);
cpx、cpy:控制点的坐标。
x、y:终点的坐标。
三次贝塞尔曲线:
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
cp1x、cp1y:第一个控制点的坐标。
cp2x、cp2y:第二个控制点的坐标。
x、y:终点的坐标。
绘制弧形:
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
x、y:圆心的坐标。
radius:半径。
startAngle:起始角度(以弧度为单位)。
endAngle:结束角度(以弧度为单位)。
anticlockwise:可选,布尔值,指定弧形的方向(顺时针或逆时针)。
关闭路径:
ctx.closePath();
填充路径:
ctx.fill();
描边路径:
ctx.stroke();
圆形
绘制圆形:
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
样式和颜色
1.填充颜色:
ctx.fillStyle = color;
color可以是颜色名称、十六进制颜色值或 rgba 颜色值。
2.描边颜色:
ctx.strokeStyle = color;
3.线条宽度:
ctx.lineWidth = width;
4.线条样式:
虚线:
ctx.setLineDash([5, 15]);
参数是一个数组,表示虚线的绘制模式。
5. 渐变:
线性渐变:
const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
gradient.addColorStop(offset, color);
ctx.fillStyle = gradient;
x0,y0:渐变起点的坐标。
x1、y1:渐变终点的坐标。
offset:颜色停止点的位置(范围 0 到 1)。
color:颜色值。
径向渐变
const gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
gradient.addColorStop(offset, color);
ctx.fillStyle = gradient;
x0、y0、r0:内圆的圆心坐标和半径。
x1、y1、r1:外圆的圆心坐标和半径。
6. 图案填充
const pattern = ctx.createPattern(image, repetition);
ctx.fillStyle = pattern;
image:用于填充的图像。
repetition:指定图案的重复方式(repeat、repeat-x、repeat-y,no-repeat)。
绘制文本
1. 填充文本
ctx.fillText(text, x, y, maxWidth);
text:要绘制的文本。
x、y:文本的左上角坐标。
maxWidth:可选,文本的最大宽度。
2. 描边文本
ctx.strokeText(text, x, y, maxWidth);
3. 文本样式:
字体
ctx.font = 'italic bold 20px Arial';
文本对齐方式
ctx.textAlign = 'start' | 'end' | 'left' | 'right' | 'center';
文本基线
ctx.textBaseline = 'top' | 'hanging' | 'middle' | 'alphabetic' | 'ideographic' | 'bottom';
图像操作
1.绘制图像:
ctx.drawImage(image, dx, dy, dWidth, dHeight);
image:要绘制的图像。
dx,dy:图像在画布上的左上角坐标。
dWidth、dHeight:可选,图像在画布上的宽度和高度。
2. 从画布创建图像:
const image = canvas.toDataURL('image/png');
变换
1. 保存和恢复绘图状态:
ctx.save();
// 绘图操作
ctx.restore();
2. 平移
ctx.translate(x, y);
3. 旋转
ctx.rotate(angle);
angle:旋转角度(以弧度为单位)。
4. 缩放:
ctx.scale(x, y);
合成和裁剪
1.合成模式:
ctx.globalCompositeOperation = 'source-over' | 'source-in' | 'source-out' | 'source-atop' | 'destination-over' | 'destination-in' | 'destination-out' | 'destination-atop' | 'lighter' | 'copy' | 'xor';
2. 剪区域
ctx.clip();
事件处理
Canvas 本身不支持事件处理,但可以通过监听画布上的事件(如鼠标事件或触摸事件)来实现交互功能。
动画
通过不断清除画布并重新绘制图形,可以实现动画效果。通常使用 requestAnimationFrame 来实现流畅的动画。
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘图操作
requestAnimationFrame(animate);
}
animate();
相关绘制效果图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas 线条绘制</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="800" height="600"></canvas>
<script>
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 模拟切割轨迹数据
let sliceActive = true;
let sliceTrail = [
{ x: 100, y: 100 },
{ x: 200, y: 150 },
{ x: 300, y: 100 },
{ x: 400, y: 200 },
{ x: 500, y: 150 }
];
// 绘制切割轨迹
function drawSliceTrail() {
if (sliceActive && sliceTrail.length > 1) {
ctx.beginPath();
ctx.moveTo(sliceTrail[0].x, sliceTrail[0].y);
for (let i = 1; i < sliceTrail.length; i++) {
ctx.lineTo(sliceTrail[i].x, sliceTrail[i].y);
}
ctx.strokeStyle = 'blue';
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();
ctx.shadowColor = '#FFF';
ctx.shadowBlur = 15;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = 15;
ctx.stroke();
ctx.shadowBlur = 0;
}
}
// 调用绘制函数
drawSliceTrail();
</script>
</body>
</html>
下面是成果图
代码与成果图目的均为帮助大家复习本文知识
小结
本文主要讲了关于水果轨迹及水果图形的基本绘制,同时引入了canvas的相关知识,帮助大家更好的绘制相关的模型,体验游戏制作的乐趣。
下期预告
致敬童年系列----切水果4,将考虑讲解切割模型的制作或者是监听事件的完成,学业最近过多,更新速度会减缓些,感谢您的谅解与阅读,期待你的关注🫵🫵🤓🥳