Sweep A Line
Если вы хотите переместить линию вдоль ее нормали (на 90 градусов от ее направления) и повернуть ее, при этом следующее объяснит, как это сделать, используя абсолютное позиционирование (нет * 1003).* звонки)
Некоторые служебные функции для начала работы.
// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
const length =lengthOfVec(vec);
vec.x /= length;
vec.y /= length;
return vec;
}
const rotateVec90 = vec => ([vec.y, vec.x] = [vec.x, - vec.y], vec);
Мы можем начать с линии, определяемой ее конечными точками p1
и p2
, а нормаль к линии - это вектор, который находится под углом 90 градусов слева от линии.
Мы создадим функцию, которая перемещала линию вдоль этой нормали, функция также будет вращать линию, как это происходит, и, судя по вашему примеру, линия также должна изменить длину, чтобы мы могли добавитьАргумент масштабирования.
Шаги
Функция будет sweepLine(line, distance, rotate, scale)
, где rotate
в радианах (я не буду использовать градусы), distance
в пикселях, scale > 1
увеличит линию, а scale < 1
уменьшит линию.
function sweepLine(line, dist, rot, scale){
Нам нужен центр линии и нормализованная и нормальная линия как вектор
const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
const lineNorm = normalVec(line);
const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));
Нетrotate
Нам нужна новая позиция центра, если мы вращаемся, новый центр будет в конце дуги, если не вращать, новая позиция будет в конце линии.Просто переместите центр по обычной
if(rot !== 0){
// need the dist of point from center
const ax = line.p2.x - center.x;
const ay = line.p2.y - center.y;
// move the point
center.x += norm.x * dist;
center.y += norm.y * dist;
Теперь мы можем просто масштабировать линию
line.p1.x = center.x - ax * scale
line.p1.y = center.y - ay * scale;
line.p2.x = center.x + ax * scale;
line.p2.y = center.y + ay * scale;
}
Поворачивать движения по дуге
Для повернутой линии нам нужнонайти точку на дуге, и для определения дуги нам нужен центр этой дуги.Длина дуги - это изменение угла, умноженного на радиус, у нас нет радиуса
else {
const arcRadius = dist / rot;
Центр дуги находится на расстоянии arcRadius
от центра (обратите внимание, что rot
может быть отрицательным, чтопереместим центр в правильное положение)
const arcCenter = Point(
center.x + lineNorm.x * arcRadius,
center.y + lineNorm.y * arcRadius
);
теперь у нас есть центр, нам нужен начальный угол дуги, который является направлением линии.
const startAngle = Math.atan2(lineNorm.y, lineNorm.x);
const endAngle = startAngle + rot;
Мыдобавьте вращение к startAngle
, а затем переместите arcRadius
расстояние от arcCenter
вдоль этого нового угла к новому центру.
center.x = arcCenter.x + Math.cos(endAngle) * arcRadius;
center.y = arcCenter.y + Math.sin(endAngle) * arcRadius;
С новой позицией центра мы можем изменить линииразмер и повернуть его одновременно, если мы получим длину линии.
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
line.p1.x = center.x - Math.cos(endAngle) * len * scale * 0.5;
line.p1.y = center.y - Math.sin(endAngle) * len * scale * 0.5;
line.p2.x = center.x + Math.cos(endAngle) * len * scale * 0.5;
line.p2.y = center.y + Math.sin(endAngle) * len * scale * 0.5;
И это все.Функция может возвращать.
}
}
Пример
Чтобы показать пример использования, приведенный ниже фрагмент делает то же самое, но имеет некоторые оптимизации на этом пути.
Пример создает случайныйлинии, а затем перемещает ее с помощью функции SweepLine.Он анимирован для непрерывного рисования линий.
requestAnimationFrame(update);
const ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
function update(timer){
if(w !== innerWidth || h !== innerHeight){
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
}
jiggle();
sweepLine(line, moveBy,rotateBy, scaleBy);
drawLine(line);
requestAnimationFrame(update);
}
// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
const length = lengthOfVec(vec);
vec.x /= length;
vec.y /= length;
return vec;
}
const rotateVec90 = vec => {
const t = vec.x;
vec.x = - vec.y;
vec.y = t;
return vec;
}
function sweepLine(line, dist, rot, scale){
const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
const lineNorm = normalVec(line);
const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));
if(rot === 0){
const ax = (line.p2.x - center.x) * scale;
const ay = (line.p2.y - center.y) * scale;
center.x += norm.x * dist;
center.y += norm.y * dist;
line.p1.x = center.x - ax
line.p1.y = center.y - ay;
line.p2.x = center.x + ax;
line.p2.y = center.y + ay;
} else {
const arcRadius = dist / rot;
const arcCenter = Point(
center.x - lineNorm.x * arcRadius, center.y - lineNorm.y * arcRadius
);
const endAngle = Math.atan2(lineNorm.y, lineNorm.x) + rot;
var ax = Math.cos(endAngle);
var ay = Math.sin(endAngle);
center.x = arcCenter.x + ax * arcRadius;
center.y = arcCenter.y + ay * arcRadius;
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
ax *= len * scale * 0.5;
ay *= len * scale * 0.5;
line.p1.x = center.x - ax;
line.p1.y = center.y - ay;
line.p2.x = center.x + ax;
line.p2.y = center.y + ay;
}
}
function drawLine(line){
ctx.lineWidth = 8;
ctx.lineCap = "round";
ctx.strokeStyle = col;
ctx.beginPath();
ctx.lineTo(line.p1.x, line.p1.y);
ctx.lineTo(line.p2.x, line.p2.y);
ctx.stroke();
}
function createRandomLine(){
const x = Math.random() * w * 0.3 + w * 0.35;
const y = Math.random() * h * 0.3 + h * 0.35;
const len = Math.random() * 40 + 10;
const dir = Math.random() * Math.PI * 2;
return Line(
Point(x - Math.cos(dir) * len * 0.5, y - Math.sin(dir) * len * 0.5),
Point(x + Math.cos(dir) * len * 0.5, y + Math.sin(dir) * len * 0.5)
);
}
// sweep the line randomly needs some settings
var line, rotateBy, moveBy, scaleBy, col, l = 50,s = 70,hue = 0,moveFor = 0; //
function randomize(){
rotateBy = Math.random() * 0.5 - 0.25;
moveBy = Math.random() * 5 + 5;
scaleBy = 1;
moveFor = 200;
line = createRandomLine();
}
function jiggle(){
if(moveFor === 0 ){ randomize() }
rotateBy += (Math.random() - 0.5) * 0.2;
scaleBy = Math.random() < 0.2 ? 1/1.1 : Math.random() < 0.2 ? 1.1 : 1;
moveBy += (Math.random() - 0.5) * 4;
moveFor --;
hue = (hue + 1) % 360;
s = (s + 100 + Math.random() - 0.5) % 100;
l = (l + 100 + Math.random() - 0.5) % 100;
col = "hsl("+hue+","+s+"%,"+l+"%)";
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>