Даны 2 окружности, вложенные одна в другую: необходимо реализовать отталкивание внутренней от курсора при этом она не должна выходить за границы внешней.
class Circle {
constructor(node) {
this.DOMElement = node;
this.ctx = this.DOMElement.getContext('2d');
this.dx = 0;
this.dy = 0;
this.x = this.DOMElement.width / 2,
this.y = this.DOMElement.height / 2,
this.ballRadius = 39;
this.cirleRadius = this.DOMElement.width / 2 - 50
}
drawCircle(x, y, radius, fill) {
this.ctx.beginPath();
this.ctx[`${fill.fill}Style`] = fill.color;
this.ctx.lineWidth = 3;
this.ctx.arc(x, y, radius, 0, Math.PI*2, true);
this.ctx[fill.fill]();
this.ctx.closePath();
}
render() {
this.ctx.clearRect(0, 0, this.DOMElement.width, this.DOMElement.height);
this.drawCircle(
this.DOMElement.width / 2,
this.DOMElement.height / 2,
this.cirleRadius,
{fill: 'stroke', color: '#34648e'}
);
this.drawCircle(
this.x,
this.y,
this.ballRadius,
{fill: 'fill', color: '#0294bf'}
);
if(Math.pow(this.x - this.DOMElement.width / 2, 2) + Math.pow(this.y - this.DOMElement.height / 2, 2) >= Math.pow(this.cirleRadius - this.ballRadius, 2) ) {
// ????
}
const frame = requestAnimationFrame(this.render.bind(this))
}
init() {
let currentX = 0;
let currentY = 0;
let distance = 0;
let speed = 10;
this.DOMElement.onmousemove = (event) => {
let mouseX = event.clientX - this.DOMElement.offsetLeft - 10;
let mouseY = event.clientY - this.DOMElement.offsetTop - this.DOMElement.scrollTop + window.pageYOffset - 14;
let xResult = mouseX - currentX;
let yResult = mouseY - currentY;
distance = Math.sqrt(xResult * xResult + yResult * yResult);
if(Math.pow(mouseX - this.x, 2) + Math.pow(mouseY - this.y, 2) <= Math.pow(this.ballRadius + 5, 2) ) {
this.x += xResult / distance * speed;
this.y += yResult / distance * speed;
}
currentX = mouseX;
currentY = mouseY;
}
this.render();
}
}
const circle = new Circle(document.querySelector('#canvas'));
circle.init();
.canvas {
border: 15px solid #ebebeb;
margin: auto;
background-color: #f8f8f8;
}
<canvas id="canvas" class="canvas" width="590" height="600">
Подскажите как реализовать планый "отскок" шара от прикосновения к нему курсором на некую дистанцию и при этом такой же плавный "отскок" от границы внешней окружности при столкновении.
Чтобы движения были без рывков, необходимо положения считать исходя из скоростей и времени между кадрами.
Т.е. зная положение объекта в прошлом кадре, его направление, скорость движения и сколько прошло с прошлого кадра секунд - вычисляем новое положение.
Собрал маленький пример:
Я завел объект, описывающий состояние шарика.
Повесил слушатель, который обновляет глобальное положение мыши по mousemove
Написал цикл отрисовки (рекурсивный вызов через requestAnimationFrame
), который берет значение положения шарика из объекта-состояния и рисует его + рисует внешний круг.
Запустил независимый от отрисовки цикл(setInterval
), который меняет состояние шарика. Он делает 3 вещи:
Обрабатывает положение мыши относительно шарика, и когда произошло столкновение устанавливает шарику скорость и вектор движения.
Считает новое положения шарика исходя из его текущего положения, вектора его движения и скорости. Постепенно уменьшает скорость движения.
Обрабатывает столкновения с внешним кругом, подсчитывая новый отраженный вектор движения
Отраженный вектор равен исходному вектору минус вектор нормали умноженный на двойное скалярное произведение вектора нормали в точке соударения и исходного вектора.
r = i−2(i⋅n)n
Вот рабочий пример:
// координаты мыши относительно центра канвы.
let x = 0, y = 0;
// глобальный слушатель мышки
window.addEventListener('mousemove', e => {
let z = window.getComputedStyle(canvas).zoom || 1;
x = e.pageX/z - e.target.offsetLeft - canvas.width/2,
y = e.pageY/z - e.target.offsetTop - canvas.height/2;
});
let ball = {
r: 50, // радиус шарика
x: 0, // координата по х центра шарика
y: 0, // координата по y центра шарика
speed: 0, // скорость движения
dirx: 0, // компонент x вектора движения шарика
diry: 0, // компонент y вектора движения шарика
damp: 10, // скорость уменьшения скорости движения (сопротивление)
collision: false, // признак коллизии с внешним кругом
// функция, которая проверяет наличие коллизии шарика с внешним кругом
hitOuterCircleCheck: function() {
let dr = 195-this.r; //разница радиусов
// по теореме пифагора проверяем выход за пределы круга (коллизию)
if (this.x*this.x + this.y*this.y > dr*dr) {
// если коллизия уже была обсчитана, но шарик еще не вернулся в круг,
// чтобы он не застревал больше не надо обсчитывать коллизии, поэтому выходим
if (this.collision)
return;
// устанавливаем для шарика признак коллизии
this.collision = true;
// далее идет код расчета нового вектора движения
// найдем вектор нормали. тут он берется приближенно,
// в точке центра шарика в момент обсчета коллизии,
// при том что шарик уже проскочил границу. по идее тут
// необходимо посчитать точку соударения геометрически.
let max = Math.max(Math.abs(this.x), Math.abs(this.y));
let nx = -this.x/max;
let ny = -this.y/max;
// найдем новый вектор движения по формуле
// r = i−2(i⋅n)n , где
// i - исходный вектор
// n - нормаль
// ⋅ знак скалярного произведения
let dot2 = this.dirx * nx * 2 + this.diry * ny * 2
this.dirx = this.dirx-dot2*nx;
this.diry = this.diry-dot2*ny;
// нормализуем вектор движения
max = Math.max(Math.abs(this.dirx), Math.abs(this.diry));
this.dirx /= max;
this.diry /= max;
} else {
// сбрасываем признак коллизии когда шарик вернулся в круг.
this.collision = false;
}
},
// функция проверки коллизии шарика и мышки
hitMouseCheck: function() {
// если есть коллизия с внешним кругом игнорируем мышку
if (this.collision)
return;
// разница координат мышки и шарика
let dx = this.x - x;
let dy = this.y - y;
// проверяем по теореме Пифагора столкновение с мышкой
if (dx*dx + dy*dy < ball.r*ball.r) {
// задаем вектор движения и нормализуем его
let max = Math.max(Math.abs(dx), Math.abs(dy));
if (!max) return;
this.dirx = dx/max;
this.diry = dy/max;
// задаем скорость
this.speed = 300;
}
},
// тут осуществляется передвижение
// dt - кол-во секунд с прошлого обсчета
doMove: function(dt) {
// к текущей координате прибавляем вектор скорости помноженный
// на значение скорости помноженные на прошедшее время
this.x += this.dirx*this.speed*dt;
this.y += this.diry*this.speed*dt;
// тормозим объект, так же на значение зависящее от времени
this.speed = Math.max(0, this.speed - this.damp*dt);
}
};
// цикл прверок, не зависит от цикла отрисовки,
// все проверки запускаются дискретно, через фиксированное время 10 мс
// если считать "физику" в requestAnimationFrame все будет печально.
let t = 0;
setInterval(() => {
// считаем сколько времени прошло с прошлого обсчета
let dt = new Date().getTime() - t;
ball.hitMouseCheck();
ball.doMove(dt/1000);
ball.hitOuterCircleCheck();
t = new Date().getTime();
}, 5);
// далее нет никаких фокусов
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
requestAnimationFrame(draw);
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(canvas.width/2, canvas.height/2)
circle(0, 0, 195);
circle(ball.x, ball.y, ball.r);
ctx.translate(-canvas.width/2, -canvas.height/2)
requestAnimationFrame(draw);
}
function circle(x,y,r) {
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.stroke();
ctx.closePath();
}
<body style="margin:0"><canvas width="400" height="400"/></body>
В этом алгоритме есть неточности, точнее недоработки, но в этом примере они несущественны.
В момент обсчета столкновения шарика, когда успешно прошла проверка коллизии с внешним кругом, шарик уже находится за пределами окружности, нормаль я определяю именно в точке центра шарика, а не в точке столкновения. точку эту можно и посчитать, но это усложнит пример.
Не хватает переноса шарика обратно в область внешнего круга, с учетом изменения вектора движения в точке соударения, когда он находится уже за границей. Это выражается в том что даже иногда визуально видно как шарик слегка выходит за границы.
Подскажите, как её нарисовать не используя две фигурыТак же известен радиус, высота и расстояние между верхней точкой окружности и верхним...
Открываю на сайте всплывающее окно, токо оно открывается в левом верхнем углуwindow
У меня возникла проблема с MySQL я недавно ее поставил настроил и вот тут уже проблема с коннектором Внимание Phpmyadmin работает без проблем а коннект...