前言

本篇主要讲坐标旋转及其应用,这是编程动画必不可少的技术。
阅读本篇前请先打好前面的基础。
本人能力有限,欢迎牛人共同讨论,批评指正。

坐标旋转

模拟场景:已知一个中心点(centerX,centerY),旋转前物体ball(x1,y1),旋转弧度(rotation);求旋转后物体(x2,y2)。(如下图)

坐标旋转就是说围绕某个点旋转坐标,我们要依据旋转的角度(弧度),计算出物体旋转前后的坐标,一般有两种方法:

简单坐标旋转

灵活运用前章节的三角函数知识可以很容易解决,基本思路:

  1. 计算物体初始相对于中心点的位置;
  2. 使用atan2计算弧度angle;
  3. 使用勾股定理计算半径radius;
  4. angle+rotation后使用cos计算旋转后x轴位置,用sin计算旋转后y轴位置。

下面是示例是采用这种方法的圆周运动,其中vr为ball相对于中心点的弧度变化速度,由于旋转半径是固定的,所以没有在动画循环里每次都获取。
完整示例:简单坐标旋转演示

/**
* 简单坐标旋转演示
* */
window.onload = function () {
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const ball = new Ball();
ball.x = 300;
ball.y = 200;
// 弧度变化速度
const vr = 0.05;
// 中心点位置设定在画布中心
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// ball相对与中心点的距离
const dx = ball.x - centerX;
const dy = ball.y - centerY;
// ball相对与中心点的弧度
let angle = Math.atan2(dy, dx);
// 旋转半径
const radius = Math.sqrt(dx ** 2 + dy ** 2); (function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height); ball.x = centerX + Math.cos(angle) * radius;
ball.y = centerY + Math.sin(angle) * radius;
angle += vr;
ball.draw(context);
}());
};

坐标旋转公式

上面的方法对于单个物体来说是很合适的,特别是角度和半径只需计算一次的情况。但是在更动态的场景中,可能需要旋转多个物体,而他们相对于中心点的位置各不相同。所以每一帧都要计算每个物体的距离、角度和半径,然后把vr累加在角度上,最后计算物体新的坐标。这样显然不会是优雅的做法。
理想的做法是用数学方法推导出旋转角度与位置的关系,直接每次代入计算即可。推导过程如下图:

其实推导过程不重要,我们只需要记住如下两组公式,其中dx2和dy2是ball结束点相对于中心点的距离,所以得到物体结束点,还要分别加上中心点坐标。

// 正向选择
dx2 = (x1 - centerX) * cos(rotation) - (y1 - centerY) * sin(rotation)
dy2 = (y1 - centerY) * cos(rotation) + (x1 - centerX) * sin(rotation)
// 反向选择
dx2 = (x1 - centerX) * cos(rotation) + (y1 - centerY) * sin(rotation)
dy2 = (y1 - centerY) * cos(rotation) - (x1 - centerX) * sin(rotation)

下面是示例是采用这种方法的圆周运动,其中dx1和dy1是ball起始点相对于中心点的距离,dx2和dy2是ball结束点相对于中心点的距离。
完整示例:高级坐标旋转演示

/**
* 高级坐标旋转演示
* */
window.onload = function () {
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const ball = new Ball();
ball.x = 300;
ball.y = 200;
// 弧度变化速度
const vr = 0.05;
// 中心点位置设定在画布中心
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// 由于vr是固定的可以先计算正弦和余弦
const cos = Math.cos(vr);
const sin = Math.sin(vr); (function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height); // ball相对与中心点的距离
const dx1 = ball.x - centerX;
const dy1 = ball.y - centerY;
// 代入公式求出ball在结束相对与中心点的距离
const dx2 = dx1 * cos - dy1 * sin;
const dy2 = dy1 * cos + dx1 * sin;
// 求出x2,y2
ball.x = centerX + dx2;
ball.y = centerY + dy2;
ball.draw(context);
}());
};

斜面反弹

前面的章节中我们介绍过越界的一种处理办法是反弹,由于边界是矩形,反弹面垂直或水平,所以可以直接将对应轴的速度取反即可,但对于非垂直或水平的反弹面这种方法是不适用的。
坐标旋转常见的应用就是处理这种情况,将不规律方向的复杂问题简单化。
基本思路:(旋转前后如图)

  1. 使用旋转公式,旋转整个系统,将斜面场景转变为水平场景;
  2. 在水平场景中处理反弹;
  3. 再旋转回来。

示例是一个球掉落到一条线上,球受到重力加速度影响下落,碰到斜面就会反弹,每次反弹都会损耗速度。
完整示例:斜面反弹示例

window.onload = function () {
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const ball = new Ball();
// line类构造函数参数(开始点x轴坐标,开始点y轴坐标,结束点x轴坐标,结束点y轴坐标)
const line = new Line(0, 0, 500, 0);
// 设置重力加速度
const gravity = 0.2;
// 设置反弹系数
const bounce = -0.6; ball.x = 100;
ball.y = 100; line.x = 0;
line.y = 200;
line.rotation = 10 * Math.PI / 180; const cos = Math.cos(line.rotation);
const sin = Math.sin(line.rotation); (function drawFrame() {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height); ball.vy += gravity;
ball.x += ball.vx;
ball.y += ball.vy; // 获取ball与line的相对位置
let x1 = ball.x - line.x;
let y1 = ball.y - line.y;
// 旋转坐标系(反向)
let y2 = y1 * cos - x1 * sin; // 依据旋转值执行反弹
if (y2 > -ball.radius) {
// 旋转坐标系(反向)
const x2 = x1 * cos + y1 * sin;
// 旋转速度(反向)
const vx1 = ball.vx * cos + ball.vy * sin;
let vy1 = ball.vy * cos - ball.vx * sin; y2 = -ball.radius;
vy1 *= bounce; // 将所有东西回转(正向)
x1 = x2 * cos - y2 * sin;
y1 = y2 * cos + x2 * sin;
ball.vx = vx1 * cos - vy1 * sin;
ball.vy = vy1 * cos + vx1 * sin;
ball.x = line.x + x1;
ball.y = line.y + y1;
} ball.draw(context);
line.draw(context);
}());
};

【30分钟学完】canvas动画|游戏基础(6):坐标旋转探究的更多相关文章

  1. 【30分钟学完】canvas动画|游戏基础(2):从零开始画画

    前言 上篇主要是理论的概述,本篇会多些实践,来讲讲canvas的基础用法,并包含一些基础三角函数的应用,推荐没有canvas基础的朋友阅读,熟悉的朋友可以跳过. 本人能力有限,欢迎牛人共同讨论,批评指 ...

  2. 【30分钟学完】canvas动画|游戏基础(1):理论先行

    前言 本文虽说是基础教程,但这是相对动画/游戏领域来说,在前端领域算是中级教程了,不适合前端小白或萌新.阅读前请确保自己对前端三大件(JavaScript+CSS+HTML)的基础已经十分熟悉,而且有 ...

  3. 【30分钟学完】canvas动画|游戏基础(7):动量守恒与多物体碰撞

    前言 一路沿着本系列教程学习的朋友可能会发现,前面教程中都尽量避免提及质量的概念,很多运动概念也时刻提醒大家这不是真实的物体运动.因为真实的物体运动其实跟质量都是密不可分的,而且质量的引入自然必须提及 ...

  4. 【30分钟学完】canvas动画|游戏基础(4):边界与碰撞

    前言 本系列前几篇中常出现物体跑到画布外的情况,本篇就是为了解决这个问题. 阅读本篇前请先打好前面的基础. 本人能力有限,欢迎牛人共同讨论,批评指正. 越界检测 假定物体是个圆形,如图其圆心坐标即是物 ...

  5. 【30分钟学完】canvas动画|游戏基础(extra1):颜色那些事

    前言 本篇主要讲解关于计算机颜色系统的概念,后续结合一些canvas的应用.因为是"你不知道也没关系"的边缘知识,所以作为本系列教程的扩展,没有兴趣的同学可以跳过. 开始我们万紫千 ...

  6. 【30分钟学完】canvas动画|游戏基础(extra1-1):美图我也行

    前言 本文是接续系列教程的extra1,主要是介绍颜色系统在canvas中的应用. 本来是与extra1一起成文的,因为segmentfault莫名其妙的字数限制bug只能分割放送了. canvas操 ...

  7. 【30分钟学完】canvas动画|游戏基础(5):重力加速度与模拟摩擦力

    前言 解决运动和碰撞问题后,我们为了让运动环境更加自然,需要加入一些环境因子,比如常见的重力加速度和模拟摩擦力. 阅读本篇前请先打好前面的基础. 本人能力有限,欢迎牛人共同讨论,批评指正. 重力加速度 ...

  8. 30分钟学玩转RabbitMQ

    最近在学习RabbitMQ,在网上找了不少资料发现都特高端.动辄集群部署,分布式架构什么的,对于一个初学者实在不够友好.心想求人不如求自己,为什么不自己整理一套资料呢?于是<30分钟学玩转Rab ...

  9. 3分钟学完Python,直接从入门到精通

    作为帅气小编,我已经把python一些模块的甩在这儿了qwq,只要你拿到这些干货,包你玩转python,直接冲向"大佬"的段位,如果已经学了C或者C++或者说如果你需要你的一段关键 ...

随机推荐

  1. [LC] 452. Minimum Number of Arrows to Burst Balloons

    There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided ...

  2. 002.前端开发知识,前端基础HTML(2020-01-07)

    一.列表标签 1.无序列表 ul <ul> <li>列表项1</li> <li>列表项2</li> <li>列表项3</l ...

  3. k8s中command、args和dockerfile中的entrypoint、cmd之间的关系

    当用户同时写了command和args的时候自然是可以覆盖DockerFile中ENTRYPOINT的命令行和参数,那么对于具体情况呢,比如仅仅写了command或者args的时候呢?完整的情况分类如 ...

  4. [LC] 259. 3Sum Smaller

    Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 < ...

  5. [LC] 78. Subsets

    Given a set of distinct integers, nums, return all possible subsets (the power set). Note: The solut ...

  6. [LC] 112. Path Sum

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...

  7. 分布式消息队列Apache Pulsar

    Pulsar简介 Apache Pulsar是一个企业级的分布式消息系统,最初由Yahoo开发并在2016年开源,目前正在Apache基金会下孵化.Plusar已经在Yahoo的生产环境使用了三年多, ...

  8. Mysql存储过程简单应用

    因为很久没写过存储过程了,语法也不记得了,靠百度后,解决了当前问题,这里就简单记录一下. CREATE PROCEDURE pro1() BEGIN DECLARE i int; DECLARE db ...

  9. verilog乘法器的设计

    在verilog编程中,常数与寄存器变量的乘法综合出来的电路不同于寄存器变量乘以寄存器变量的综合电路.知乎里的解释非常好https://www.zhihu.com/question/45554104, ...

  10. FPGA基础架构总结

    1.为什么FPGA的整体逻辑的速度有限制? 与ASIC相比较,AS指的是(application specific)专用集成应用,而FPGA是可编程逻辑,在实现的过程中,AS是直接用最基础的逻辑门实现 ...