Наверное, вы помните из первой части, что я говорила о том, что забыла школьную программу физики, которая могла бы мне помочь создавать анимацию с помощью canvas. Как оказалось, я забыла еще и тригонометрию. Также оказалось, что тригонометрия крайне полезна в создании анимации и креативного кодинга в целом.
Иногда, не совсем понятно, как и зачем использовать этот вид математики для создания анимации. Сегодня я хочу показать вам пример, как мы можем использовать тригонометрию в сочетании с нашей банальной физикой для создания забавной анимации.
Мы сделаем рычаг или катапульту, которую пользователь сможет оттянуть мышкой и запустить в воздух маленького пингвинчика.
Как когда-то сказала Тина Тернер, на что способна любовь способен треугольник?
Первый элемент, требуемый для нашей анимации – это рычаг. Базовая часть довольно проста, мы можем нарисовать полукруг, чтобы создать следующее.
function drawBase() { ctx.beginPath(); ctx.arc(leverPosition.x, leverPosition.y, 40, Math.PI, 0, false); ctx.closePath(); ctx.lineWidth = 5; ctx.fillStyle = '#c1c0c8'; ctx.fill(); }
Теперь нам нужно нарисовать сам рычаг. Мы знаем, что нам нужен рычаг длиной 200 пикселей, и нам нужно его направить под углом 70° от «основания». Чтобы нарисовать линию, вам нужно знать начальную (x1,y1) и конечную точку (x2,y2). На данном этапе мы знаем нашу начальную точку (основание), но не знаем конечную точку в пространстве x/y.
![img](https://habrastorage.org/files/1d7/2c1/f86/1d72c1f86f2e469eb473c559954dff77.png)
Глядя на схему выше, мы видим, что наш рычаг, расстояние x и расстояние y до нашей второй точки составляют стороны треугольника. Мы знаем расстояние рычага и угол, но нам нужно вычислить стороны x и y. Пришло время тригонометрии!
Вот опять наш рычаг, разбитый на три угла: A, B и C и три стороны: a, b и c:
![img](https://habrastorage.org/files/80c/7a5/0f6/80c7a50f6dd1415393c6ed3bba203ef5.png)
Первый тригонометрический факт, который нам нужно знать для решения нашего треугольника, это то, что три угла в треугольнике всегда в сумме дают 180°
A + B + C = 180
В данном случае нам известен угол C (70°) и угол B (правый угол — 90°), поэтому мы можем найти угол A:
A = 180 — B — C
Теперь у нас есть все три угла, и мы можем попробовать найти наши стороны a (x) и c (y). Для этого мы можем воспользоваться теоремой синусов:
a / sin(A) = b / sin(B) = c / sin©
Нам известны значения угла C, угла B и стороны b, поэтому мы можем найти сторону c:
c / sin© = b / sin(B) c = b / sin(B) * sin©
Наконец, мы можем найти сторону a:
a / sin(A) = c / sin© a = c / sin© * sin(A)
Вот функция JavaScript, которая может найти стороны x/y треугольника нашего рычага при известном угле С. Не забывайте, что для тригонометрических функций JavaScript, таких как Math.sin, углы должны быть указаны в радианах, а мы их считали в градусах. Я люблю использовать простую функцию помощника, которая помогает отслеживать конвертацию.
function calculateTriangleXY(C) { var B = 90; // solve for A var A = 180 - C - B; // solve c and a var c = (leverLength * Math.sin(toRadians(C))) / Math.sin(toRadians(B)); var a = (c * Math.sin(toRadians(A))) / Math.sin(toRadians(C)); // return x/y sides return {x: a, y: c} } function toRadians(deg) { return deg / 180 * Math.PI; }
Теперь у нас есть функция, способная найти стороны x/y треугольника, и мы можем нарисовать наш рычаг за счет вычитания этих значений из нашей базовой точки, чтобы получить конечную точку линии.
function drawLever() { var xy = calculateTriangleXY(currentAngle); ctx.beginPath(); ctx.moveTo(leverPosition.x, leverPosition.y); ctx.lineTo(leverPosition.x - xy.x, leverPosition.y - xy.y); ctx.lineWidth = 4; ctx.strokeStyle = '#7b4b3d'; ctx.stroke(); }
Рычаг у нас уже есть, теперь нужно сделать так, чтобы пользователь мог с ним взаимодействовать. Мы можем это сделать, записав движение мышки, и проверив, что мышь привязывается к «области запуска» рычага. При этом «область запуска» представляет собой длинный тонкий треугольник под углом, а потому это не так просто, как отметить положение мышки внутри нормальной рамки x/y.
![img](https://habrastorage.org/files/43e/8d6/ed1/43e8d6ed1bc84152bbb002e4db1cea24.png)
Мы можем использовать тригонометрию для решения и этой проблемы! Когда пользователь держит мышку в области запуска рычага, он создает еще один треугольник с углом 70°. Так как мы знаем положение y мышки и угол рычага, мы можем проверить, каким должно быть положение x, если мышь находится в области запуска. Если x нашей мыши находится рядом с x треугольника, тогда мы можем предположить, что она находится в области запуска. Давайте обновим нашу функцию calculateTriangleXY, чтобы мы могли сообщить ей положение y, а также угол, чтобы получить положение х.
function calculateTriangleXY(C, y) { var B = 90; // solve for A var A = 180 - C - B; // if we only have angle C we need to solve edge c first var c = y || (leverLength * Math.sin(toRadians(C))) / Math.sin(toRadians(B)); var a = (c * Math.sin(toRadians(A))) / Math.sin(toRadians(C)); return {x: a, y: c} }
Теперь мы можем проверить положение мыши, и если она находится в области запуска, мы изменим курсор на указатель, чтобы пользователь понимал, что объект кликабелен, и установим переменную hitting в значение «истина».
// get x given current angle & y pos var xy = calculateTriangleXY(currentAngle, leverPosition.y - mousePos.y); // if mouse x is close to the triangle x, we're in the hit area if ((mousePos.x > leverPosition.x - xy.x - 20) && (mousePos.x < leverPosition.x - xy.x + 20)) { canvas.style.cursor = 'pointer'; hitting = true; } else { canvas.style.cursor = 'auto'; hitting = false; }
Теперь мы знаем, когда пользователь наводит мышь на область запуска! Чудесно! Это значит, что когда пользователь кликнет мышью, мы сможем начать «движение» рычага. Когда пользователь кликнет и потянет, нам нужно сделать так, чтобы рычаг соответствовал положению мыши. Чтобы это сделать, нам нужно определить угол рычага при имеющемся положении x/y мыши. Найти этот угол мы можем с помощью, как вы уже догадались – тригонометрии!
В этом примере мы знаем стороны a и c благодаря позиции x/y мыши, и угол B (90°).
![img](https://habrastorage.org/files/911/a56/611/911a566119ef408aa9248521f26608fb.png)
Чтобы найти угол C, нам сначала нужно найти сторону b. Для этого мы можем использовать теорию косинусов.
b2 = a2 + c2 − (2 * a * c * cos(B))
Как только мы найдем сторону b, мы можем найти угол С.
unction calculateTriangleAngle(x,y) { var A, C; var B = 90; var c = y; var a = x; var b = Math.sqrt(Math.pow(a, 2) + Math.pow(c, 2) - 2 * a * c * Math.cos(toRadians(B))); C = Math.asin(Math.sin(toRadians(B)) / b * c); return C; }
После этого мы можем установить соответствующий угол рычага, что приведет к движению с помощью мыши!
var angle = toDegrees(calculateTriangleAngle(leverPosition.x - mousePos.x, leverPosition.y - mousePos.y)); if (angle < 10) { release(); return; } if (angle > 80) angle = 80; currentAngle = angle;
Наконец, мы можем добавить функцию для «высвобождения» рычага, либо когда пользователь отпускает, либо когда дотягивает рычаг до самого конца. Помните, в 1 части этого блога я объясняла, что вы можете задать скорость в направлении x и y? Вы также можете задать скорость вращения, и именно это мы здесь и сделаем. Мы зададим скорость vr относительно того, насколько далеко был оттянут рычаг, так чтобы он отправлялся обратно до нашего целевого угла в 70°.
function release() { pulling = false; vr = (70 - currentAngle) * 0.06; }
Собрав все это вместе, мы получим рычаг, который можно натягивать и отпускать!
Давайте заставим пингвинов летать
Сначала нам нужно создать объект «пингвин», который мы будем использовать для генерирования пингвинов.
function Penguin(x, y, r) { var _this = this; var img; this.x = x; this.y = y; this.r = targetAngle; (function() { img = new Image(); img.src = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/penguin.png'; })(); this.update = function() { // rotating the penguin ctx.save(); ctx.translate(_this.x, _this.y); if(_this.latched) ctx.rotate(toRadians(currentAngle)); else ctx.rotate(toRadians(_this.r)); // translating a little so it sits further down the ledge ctx.translate(24, -16); ctx.drawImage(img, -20, -20, 40, 40); ctx.restore(); } }
Теперь мы можем создать пингвина и разместить его на рычаге.
function addPenguin() { var penguin = new Penguin(140,120); penguin.latched = true; penguins.push(penguin); loadedPenguin = penguin; }
Вы наверняка заметили, что мы установили свойство penguin.latched в значение «true». Это для того, чтобы функция обновления обновляла вращение пингвина по мере вращения рычага, создавая ощущение, что пингвин «привязан» к рычагу. Нам нужно опустить пингвина, когда рычаг вернется в наш изначальный угол после запуска. Мы можем поставить отметку для этого момента в нашу функцию updateRotation и запустить пингвина.
if (released && currentAngle > targetAngle) { released = false; throwPenguin(); }
«Запуск» пингвина подразумевает присвоение пингвину значений vx, vy и vr, что запустит его в воздух, как только мы обновим функцию обновления Penguin с помощью банальной физики.
function throwPenguin() { loadedPenguin.vx = vr*4; loadedPenguin.vy = vr*-1.86; loadedPenguin.vr = 0.5; loadedPenguin.latched = false; loadedPenguin = null; setTimeout(addPenguin, 500); } // added to Penguin.update -- // penguin is launched! if(!_this.latched && _this.vx) { _this.vy += gravity; _this.x += _this.vx; _this.y += _this.vy; _this.r += _this.vr; _this.vx *= 0.98; _this.vy *= 0.98; // check for hitting edges if (_this.x + 28 > canvasWidth) { _this.x = canvasWidth - 28; _this.vx *= bounce; _this.vr *= bounce; } if (_this.y + 38 > canvasHeight) { _this.y = canvasHeight - 38; _this.vy *= bounce; _this.vr *= bounce; } if (Math.abs(_this.vx) < 0.001) _this.vx = 0; if (Math.abs(_this.vy) < 0.001) _this.vy = 0; }
→ Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
→ Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
→ Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.
ссылка на оригинал статьи http://habrahabr.ru/post/262209/
Добавить комментарий