как сделать анимацию движения таракана
Нужно было сделать анимацию ползающих жуков, я сделал как:
function initialBugs(bugs) {
var containerRect = document.getElementById("bug-container").getBoundingClientRect();
for (let bug of bugs ) {
var initialX = Math.random() < 0.6 ? - bug.offsetWidth : containerRect.width;
var initialY = Math.floor(Math.random() * containerRect.height);
bug.style.left = initialX + "px";
bug.style.top = initialY + "px";
}
}
function moveRoach() {
var bugs = document.getElementsByClassName("bug-icon");
var container = document.getElementById("bug-container");
initialBugs(bugs);
for (let bug of bugs) {
var targetX = Math.floor(Math.random() * container.offsetWidth);
var targetY = Math.floor(Math.random() * container.offsetHeight);
var targetAngle = Math.atan2(targetY - parseInt(bug.style.top), targetX - parseInt(bug.style.left)) * (180 / Math.PI);
var rotationAngle = targetAngle;
bug.style.transform = "rotate(" + rotationAngle + "deg)";
bug.style.left = targetX + "px";
bug.style.top = targetY + "px";
}
}
document.addEventListener("DOMContentLoaded", function() {
moveRoach();
setInterval(moveRoach, 9000);
});
#bug-container {
position: relative;
height: 200px;
}
.bug-icon {
height: 15px;
width: 15px;
position: absolute;
top: 0;
left: 0;
z-index: 0;
transition: left 5s linear, top 5s linear;
}
<header class="header" id="bug-container">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
</header>
Для каждого жука определяю случайные координаты x и y внутри контейнера с использованием Math.random().
Далее вычисляю угол между текущим положением жука и новым (x, y), сделал это с помощью функции Math.atan2(), которая возвращает арктангенс от отношения разности координат y - parseInt(bug.style.top) к разности координат x - parseInt(bug.style.left), затем этот угол перевожу из радиан в градусы
Далее вычисляю угол поворота жука, вычитая из targetAngle фиксированный угол -45°(так как на иконке он так повёрнут), разница углов rotationAngle определяет насколько нужно повернуть элемент, чтобы он был направлен в сторону новых координат.
Но в процессе решения столкнулся с рядом проблем:
Я никак не могу заставить жука поворачиваться в сторону перемещения последующей позиции.Он поворачивается в сторону следующей позиции, но спустя некоторые время снова начинает, делать кривые повороты.- Движения жука не получились близко к реальным движениям, и не знаю можно ли такое сделать.
- Если позиция оказывается слишком далеко от нынешнего положения, то жук "бежит" слишком быстро и наоборот если слишком близко то медленно
И есть ли другой способ сделать такое? Может есть какая-то библиотека с которой можно такое реализовать, но я нашел максимально близкое только в animate.js
Ответы (2 шт):
- Я никак не могу заставить жука проворачиваться в сторону перемещения последующей позиции.
Решено. Нужно было ставить дельту Y первой, и умножать сами новые X и Y на -1, чтобы жуки не шли задом на перёд.
- Движения жука не получились близко к реальным движениям, и не знаю можно ли такое сделать.
Можно что-то нахимичить с offset-path и с Motion Path-ами в принципе, но мне это кажется излишним.
- Если позиция оказывается слишком далеко от нынешнего положения, то жук "бежит" слишком быстро
Так происходит потому, что жуки должны прийти к одному месту в одно и то же время.
Решение
class MoveBugs {
static init(container) {
const bugs = container.getElementsByClassName("bug-icon");
const firstBug = bugs[0]
const maxWidth = container.offsetWidth - firstBug.offsetWidth
const maxHeight = container.offsetHeight - firstBug.offsetHeight
const initialAngle = -45
const moveBugs = () => this.moveBugs(bugs, maxWidth, maxHeight, initialAngle)
moveBugs()
setInterval(moveBugs, 2e3)
}
static moveBugs(bugs, maxWidth, maxHeight, initialAngle) {
for (const bug of bugs) {
const newX = getRandom(0, maxWidth)
const newY = getRandom(0, maxHeight)
const angle = this.getAngle(bug, newX, newY) - initialAngle
bug.style.transform = `rotate(${angle}deg)`
this.moveBug(bug, newX, newY)
}
}
static getAngle(bug, newX, newY) {
const currentX = bug.offsetLeft
const currentY = bug.offsetTop
const dx = newX - currentX
const dy = newY - currentY
return Math.atan2(dy * -1, dx * -1) * 180 / Math.PI
}
static moveBug(bug, x, y) {
bug.style.left = x + 'px'
bug.style.top = y + 'px'
}
}
const container = document.getElementById('bug-container')
MoveBugs
.init(container)
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
#bug-container {
position: relative;
height: 200px;
}
.bug-icon {
height: 15px;
width: 15px;
position: absolute;
top: 0;
left: 0;
z-index: 0;
transition: left 5s linear, top 5s linear;
}
<header class="header" id="bug-container">
<img class="bug-icon" id="bug1" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug2" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug3" src="https://svgshare.com/i/t12.svg">
</header>
P.S. На лучшее решение конечно же не претендую, но надеюсь мой ответ поможет другим участникам написать более совершенное решение.
tx, ty, lastTargetTime отвечают за цель к которой таракан бежит. Цель меняется случайно с интервалом около двух секунд.
t, lastT измеряют время между последовательными вызовами update.
aspeed, vspeed - угловая и линейная скорость таракана.
a, x, y - направление движения и координаты таракана.
На каждом вызове update таракан доворачивается на цель и бежит с постоянной скоростью. Если таракану удаётся подойти к цели достаточно близко, он двигается кругами. Это не программировалось специально, просто такая модель движения.
window.requestAnimationFrame - вызывает update для всех тараканов.
const container = document.getElementById("bug-container");
const makeCockroach = icon => {
const vspeed = 100;
const aspeed = 2 * Math.PI;
let lastT = 0;
let lastTargetTime = -100;
let a = 0;
let x = 0;
let y = 0;
let tx = 0;
let ty = 0;
const update = t => {
if (t >= lastTargetTime + 1.5 + Math.random()) {
const rect = container.getBoundingClientRect();
tx = Math.random() * rect.width ;
ty = Math.random() * rect.height;
lastTargetTime = t;
}
const dt = t - lastT;
const ta = Math.atan2(ty - y, tx - x);
for (; a < ta; a += 2 * Math.PI) {
}
for (; a > ta; a -= 2 * Math.PI) {
}
const a2 = a + 2 * Math.PI;
if (ta - a < a2 - ta) {
a = Math.min(ta, a + dt * aspeed);
} else {
a = Math.max(ta, a2 - dt * aspeed);
}
x += dt * vspeed * Math.cos(a);
y += dt * vspeed * Math.sin(a);
icon.style.left = x + "px";
icon.style.top = y + "px";
icon.style.transform = "rotate(" + (180 * a / Math.PI - 135) + "deg)";
lastT = t;
};
return {'update': update};
};
const cockroaches = Array.prototype.map.call(
document.getElementsByClassName("bug-icon"),
makeCockroach
);
const step = () => {
const t = performance.now() / 1000;
for (const cockroach of cockroaches) {
cockroach.update(t);
}
window.requestAnimationFrame(step);
};
window.requestAnimationFrame(step);
#bug-container {
position: relative;
height: 200px;
}
.bug-icon {
height: 15px;
width: 15px;
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
<header class="header" id="bug-container">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
<img class="bug-icon" id="bug" src="https://svgshare.com/i/t12.svg">
</header>