☰
The Nature of Code 第 3 章开篇:Processing/p5.js 角度与角运动(Angles and Angular Motion)完全指南
2026/10/14 22:15:07 网站建设 项目流程
  • 教程
  • 文档
  • 示例工程

【免费下载链接】website-archive

Archive of the Coding Train website (first version)

项目地址:https://gitcode.com/gh_mirrors/we/website-archive
点击查看免费下载

本篇指南以仓库存档文档 _more/archive/nature-of-code/3-Oscillation/3.1-Angles-and-Angular-Motion.md 为核心骨架,系统讲解"角度(angles)"与"角运动(angular motion)"两个核心概念:从角度单位(度与弧度)讲起,再到rotate()旋转形状,最终建立"角度 → 角速度 → 角加速度"的角运动模型,并给出可直接运行的 p5.js 示例。读完本文,你将掌握如何让任意形状以可控制的角速度与角加速度旋转,并能把角运动与重力吸引等向量运动无缝结合,为第 3 章后续的极坐标、简谐运动、摆与弹簧打下基础。

1. 存档文档与它在 Nature of Code 系列中的位置

仓库中的存档文档记录了 2015 年 8 月的早期视频(Processing 版本),属于《The Nature of Code》第 3 章Oscillation(振荡运动)系列的第一集,标题为Angles and Angular Motion:

This video describes how angles work in Processing and how you can model the angular motion of a shape.

该系列存档页见 _more/archive/nature-of-code/3-Oscillation/index.md,它明确指出这些视频课程配套《The Nature of Code》书籍第 3 章(Oscillation)。同目录下的后继存档文档勾勒出整章的知识脉络:

  • 3.2-Trigonometry-and-Polar-Coordinates.md:三角学与极坐标;
  • 3.3-Simple-Harmonic-Motion.md:利用正弦、余弦实现简谐运动;
  • 3.4-Pendulum-Simulation.md:摆的仿真;
  • 3.5-Springs.md:基于胡克定律的弹簧力仿真。

这套旧版视频后来被重制为 p5.js 版本,对应的详细课程页为 _learning/nature-of-code/3.1-angles-rotation.md 与 _learning/nature-of-code/3.2-angular-motion.md,完整可运行的源码存放在 learning/nature-of-code/3.1-angles-rotation 与 learning/nature-of-code/3.2-angular-motion。本文的代码示例即取自这些仓库源码,与旧版视频"角度 + 角运动"的主题一脉相承。

2. 角度(Angle):旋转多少的量

角度的本质是"旋转了多少"。在二维坐标系中,一个形状围绕某个支点转动的量就是它的角度。与位置、速度、加速度这类向量量不同,角度是一个标量——它只有一个数值(大小),没有方向分量,方向感由"顺时针还是逆时针"来体现。

在开始任何旋转之前,必须回答一个基本问题:用什么单位度量角度?

2.1 两种单位:度(degrees)与弧度(radians)

  • 度(degrees):把一圈均分为 360 份,一份是 1°。90° 是四分之一圈,180° 是半圈,360° 是一整圈。这是日常最直觉的单位。
  • 弧度(radians):数学与物理中最自然的单位。定义是"弧长与半径之比",因此一整圈对应2π弧度:
旋转量度弧度
四分之一圈90°π/2
半圈180°π
四分之三圈270°3π/2
一整圈360°2π

2.2 为什么 π(以及 τ)如此重要?

在弧度制下,旋转一整圈就是2π。仓库重制版课程页 _learning/nature-of-code/3.1-angles-rotation.md 中专门有一个话题 "Why is pi so important?"。原因在于:弧度把"旋转"与"三角函数"(正弦、余弦)直接挂钩,而三角函数正是第 3 章后续所有振荡、极坐标、摆、弹簧仿真的数学基础。用弧度计算三角函数、微积分、求导都远比度数自然——例如sin与cos的导数只有在弧度制下才是简洁的cos与-sin。

也正因如此,许多现代编程环境(包括 p5.js)默认使用弧度。如果你更习惯"一圈 360"的直觉,可以改用TAU(即2π)来思考——p5.js 提供TAU常量,一整圈就是TAU弧度,这在心理上等价于"一圈 360°"。

2.3 弧度与度的换算

两个方向的手动换算公式:

  • 度 → 弧度:radians = degrees × π / 180
  • 弧度 → 度:degrees = radians × 180 / π

p5.js 也内置了radians()与degrees()两个转换函数,但在实际编程中更推荐直接切换全局角度模式(见下一节),让引擎替你换算。

3. 设置角度单位:angleMode()

p5.js 用angleMode()统一控制所有角度相关 API(rotate()、arc()、atan2()等)使用的单位。仓库源码 learning/nature-of-code/3.1-angles-rotation/P5/sketch.js 在setup()中这样设置:

function setup() { createCanvas(400, 400); angleMode(RADIANS); // 显式声明使用弧度 }
  • angleMode(RADIANS):弧度制(默认值),旋转一整圈需要2 * PI(即TAU)。
  • angleMode(DEGREES):角度制,此时rotate(90)表示旋转 90°。

虽然弧度是默认值,但显式调用angleMode(RADIANS)是良好习惯——它让代码意图一目了然,也避免读者误以为单位是度。若某个项目你更希望用"90、180、360"这样的数值思考,在setup()中写angleMode(DEGREES)即可,之后所有旋转代码都按度解释。

4. 旋转一个形状:translate + rotate 的协作

仓库中 3.1 的完整示例(sketch.js)演示了如何让一个矩形绕画布中心持续旋转:

let angle = 0; function setup() { createCanvas(400, 400); angleMode(RADIANS); } function draw() { background(146, 83, 161); noStroke(); fill(240, 99, 164); rectMode(CENTER); translate(200, 200); // 把坐标系原点移到画布中心 rotate(angle); // 绕新的原点旋转 rect(0, 0, 128, 64); // 在旋转后的坐标系里绘制矩形 angle += 0.05; // 每帧增加角度 → 持续转动 }

4.1 三个关键细节

  1. translate()改变旋转支点:rotate()总是绕当前坐标系原点旋转。translate(200, 200)先把原点移到画布中心,随后rotate(angle)便让矩形绕画布中心转动。若不调用translate(),矩形将绕画布左上角(默认原点)旋转,效果完全不同。
  2. rectMode(CENTER):默认模式下矩形以左上角定位,rect(0, 0, 128, 64)会在原点右上区域展开;rectMode(CENTER)让矩形以坐标点为中心,绘制出的图形与旋转支点严格对齐,视觉上更"像绕自身中心转"。
  3. 每帧累加角度:angle += 0.05让角度每帧增大 0.05 弧度。当角度超过2π(约 6.28 弧度)时并不需要归零——三角函数和旋转对任意大角度都有效,角度自然会"环绕"。0.05 弧度/帧 ≈ 每秒 3 弧度(约每秒半圈),是一个视觉上舒服的转速。

4.2 旋转方向

p5.js 中角度的正方向为顺时针(因为屏幕坐标系 y 轴向下)。若想让物体逆时针旋转,只需把增量改为负数,例如angle -= 0.05。理解这一点对后续交互式角速度控制很重要。

5. 角运动:把向量运动的三大要素迁移到角度

第 1 章我们建立过"位置 → 速度 → 加速度"的向量运动模型:

position += velocity; velocity += acceleration;

角运动(angular motion)几乎一模一样,只是把所有量换成标量:

angle += angleV; // 角位置 += 角速度 angleV += angleA; // 角速度 += 角加速度

仓库 learning/nature-of-code/3.2-angular-motion/rect-acc/sketch.js 正是这个模型的直接实现:

let angle = 0; // 角位置(弧度) let angleV = 0; // 角速度:每帧转动的弧度 let angleA = 0.001; // 角加速度:每帧角速度的改变量 function setup() { createCanvas(400, 400); angleMode(RADIANS); } function draw() { // 用鼠标水平位置实时映射角加速度 angleA = map(mouseX, 0, width, -0.01, 0.01); // 限制角速度,避免越转越快失控 angleV = constrain(angleV, -0.2, 0.2); background(146, 83, 161); noStroke(); fill(240, 99, 164); rectMode(CENTER); translate(200, 200); rotate(angle); rect(0, 0, 256, 32); angle += angleV; // 位置积分 angleV += angleA; // 速度积分 }

5.1 参数解读

  • angleA(角加速度):由鼠标位置映射而来。map(mouseX, 0, width, -0.01, 0.01)把鼠标在画布上的 x 坐标(0 到画布宽)线性映射到 -0.01 到 0.01 弧度的角加速度范围。鼠标在左半边时矩形逆时针加速,右半边时顺时针加速,中间位置(约 width/2)角加速度接近 0。
  • angleV(角速度):constrain(angleV, -0.2, 0.2)把角速度钳制在 ±0.2 弧度/帧之间,防止加速度无限累积导致旋转失控——这对应第 2 章里对速度做限制的思路。
  • angle(角位置):每帧累加角速度,等价于对速度做数值积分,得到当前旋转角度。

这三个变量的命名也出现在重制版课程页 _learning/nature-of-code/3.2-angular-motion.md 的主题列表中("Code! Angular velocity"、"Code! Angular acceleration"),说明这正是该视频演示的核心代码路径。

6. 交互式角运动:让旋转跟随鼠标

仓库同目录下的 grab 练习(learning/nature-of-code/3.2-angular-motion/grab/sketch.js)把角运动升级为交互式:按住鼠标时矩形"指向"鼠标位置,松开后保持惯性继续旋转。

核心是利用 p5.Vector 的heading()方法——它返回向量方向的弧度角:

function mouseDragged() { let v = createVector(pmouseX - width / 2, pmouseY - height / 2); pAngle = v.heading(); // 记录拖动前的角度 } function mouseReleased() { let v2 = createVector(mouseX - width / 2, mouseY - height / 2); angleV = v2.heading() - pAngle; // 角度差即角速度 console.log(angleV); } function draw() { if (mouseIsPressed) { let v = createVector(mouseX - width / 2, mouseY - height / 2); angle = v.heading(); // 按住时矩形始终指向鼠标 } // ... 绘制与积分代码同前 angle += angleV; angleV += angleA; }

这段代码体现了两个实用技巧:

  1. heading()把位置差转成角度:mouseX - width / 2与mouseY - height / 2构成从画布中心指向鼠标的向量,heading()直接给出该向量的方向角,省去手写atan2。
  2. "角速度 = 角度差":松开鼠标瞬间,把"当前角度 − 拖动时角度"作为角速度注入,实现"甩出去继续转"的惯性手感——这本质上是用位置差近似速度的差分思想。

7. 综合案例:把角运动融入引力吸引

角运动最有价值的应用是让它与第 2 章的物理引擎协同工作。仓库 learning/nature-of-code/3.2-angular-motion/gravitational-attraction 下的三件套源码演示了完整的"引力吸引 + 自旋":

  • sketch.js:创建 10 个 Mover 与 1 个 Attractor,每帧更新并施加引力;
  • attractor.js:基于万有引力公式(引力常量G = 5)计算引力,距离平方被constrain(force.magSq(), 100, 1000)钳制以避免奇点;
  • mover.js:每个运动物体在平移之外还携带自身的角状态。

Mover 类的角运动部分(mover.js)展示了如何把加速度"翻译"成角加速度:

this.angle = 0; this.angleV = 0; this.angleA = 0; update() { this.vel.add(this.acc); this.pos.add(this.vel); // 关键:把竖直方向加速度(标量)映射为角加速度 this.angleA = this.acc.y / 50.0; this.angleV += this.angleA; this.angle += this.angleV; this.acc.set(0, 0); // 每帧清零加速度,等待下一帧外力 } show() { push(); translate(this.pos.x, this.pos.y); // 平移到物体位置 rotate(this.angle); // 叠加自身旋转 line(0, 0, this.r, 0); // 一条"指针"线 ellipse(0, 0, this.r * 2); pop(); }

这里有一个非常简洁的物理直觉:物体加速下落/上升时,竖直加速度acc.y驱动旋转。this.acc.y / 50.0只是把加速度数值缩放到合适的角加速度量级(50 是经验性缩放因子)。push()/pop()保证translate+rotate只作用于当前物体,不影响其他绘制状态。于是我们同时拥有了"平移物理"(位置、速度、加速度)与"旋转物理"(角度、角速度、角加速度)两套并行的运动系统,这正是存档视频标题 "model the angular motion of a shape" 所指的能力。

8. 运行这些示例

仓库中的示例均为 p5.js 项目,每个目录都包含sketch.js与 index.html。index.html通过 CDN 引入 p5.js 库(如https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.min.js),随后加载本地sketch.js。运行方式:

  1. 在浏览器中直接打开对应目录下的index.html(p5.js 的本地文件无需构建步骤);
  2. 或在 p5.js Web Editor 中新建草图,把仓库源码复制进去运行;
  3. 学习型调试:修改angle += 0.05的步长、map()的映射范围、constrain()的上下界,观察转速、转向与"惯性"的变化,直观理解每个参数的作用。

9. 小结与下一步

围绕存档文档"角度 + 角运动"的主题,本文建立了完整的技术链路:

概念代码要素仓库证据
角度单位angleMode(RADIANS/DEGREES)、TAU3.1 源码
旋转绘制translate+rotate+rectMode(CENTER)同上
角运动三要素angle、angleV、angleA逐帧积分rect-acc 源码
交互控制heading()、mouseDragged/mouseReleasedgrab 源码
融合物理角加速度映射自acc.y,push/pop隔离状态mover.js

掌握了角度与角运动之后,第 3 章的下一站是三角学与极坐标——用sin、cos把角度换算成位置;再往后是简谐运动、摆的仿真与弹簧,它们都以本节的角度概念为地基。建议动手练习:把本节任何一个示例中的矩形换成ellipse、triangle或文字,观察旋转支点与绘制顺序对视觉效果的影响——这是建立"旋转坐标系"直觉最快的方式。

  • 教程
  • 文档
  • 示例工程

【免费下载链接】website-archive

Archive of the Coding Train website (first version)

项目地址:https://gitcode.com/gh_mirrors/we/website-archive
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询