Как вы уже поняли, круг может состоять из четырех кривых Безье. Я собираюсь использовать кубический c вместо квадратичного c, хотя он предлагает две контрольные точки.
Давайте начнем с рассмотрения следующей иллюстрации:
Как мы видим, красная кривая состоит из начальной точки A , конечной точки B и двух контрольных точек c1 & c2 соответственно.
Итак, если мы хотим, чтобы у x, y был круг с радиусом r, мы можем сказать:
A х = х; A y = y - r
B x = x + r; B y = y
c1 x = x + r / 2; c1 y = y - r
c2 x = x + r; c2 y = y - r / 2
Конечно, три отсутствующие кривые могут быть построены таким же образом.
То, что мы также можем видеть из приведенного выше рисунка: что начальная точка для красного сегмента также является конечной точкой для оранжевого сегмента. Точно так же контрольная точка оранжевого сегмента c8 связана с начальной точкой красного сегмента.
Так что, если мы собираемся переместить точку A , нам нужно двигаться конечная точка оранжевого сегмента, начальная точка красного сегмента И две контрольные точки c8 и c1 .
Для этого я написал бы общее Arc
класс, который состоит из начальной точки, конечной точки, двух контрольных точек и, кроме того, к которой подключена точка c. Затем это выглядит примерно так:
- , если кто-то нажимает на точку A, B, C или D, сохраняют текущую позицию мыши
- , сохраняют позицию ar * Контрольная точка 1085 *, а также подключенная контрольная точка ar c
- Если мышь перемещается, переместите начальную точку, ее контрольную точку и конечную точку подключенного ar c и это контрольная точка относительно движения мыши
- перекрасить круг
Вот пример:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Arc {
constructor(pointA, pointB, controlPointA, controlPointB) {
this.pointA = pointA;
this.pointB = pointB;
this.controlPointA = controlPointA;
this.controlPointB = controlPointB;
this.controlPointOldA = null;
this.controlPointOldB = null;
}
update(x, y, x2, y2) {
this.pointA.x = x;
this.pointA.y = y;
this.connectedArc.pointB.x = x;
this.connectedArc.pointB.y = y;
this.controlPointA.x = this.controlPointOldA.x + x2;
this.controlPointA.y = this.controlPointOldA.y + y2;
this.connectedArc.controlPointB.x = this.controlPointOldB.x + x2;
this.connectedArc.controlPointB.y = this.controlPointOldB.y + y2;
}
connect(connectedArc) {
this.connectedArc = connectedArc;
}
saveControlPoints() {
this.controlPointOldA = new Point(this.controlPointA.x, this.controlPointA.y);
this.controlPointOldB = new Point(this.connectedArc.controlPointB.x, this.connectedArc.controlPointB.y);
}
}
class Circle {
constructor(x, y, radius) {
this.arcA = new Arc(new Point(x, y - radius), new Point(x + radius, y), new Point(x + radius / 2, y - radius), new Point(x + radius, y - radius / 2));
this.arcB = new Arc(new Point(x + radius, y), new Point(x, y + radius), new Point(x + radius, y + radius / 2), new Point(x + radius / 2, y + radius));
this.arcC = new Arc(new Point(x, y + radius), new Point(x - radius, y), new Point(x - radius / 2, y + radius), new Point(x - radius, y + radius / 2));
this.arcD = new Arc(new Point(x - radius, y), new Point(x, y - radius), new Point(x - radius, y - radius / 2), new Point(x - radius / 2, y - radius));
this.arcA.connect(this.arcD);
this.arcB.connect(this.arcA);
this.arcC.connect(this.arcB);
this.arcD.connect(this.arcC);
}
}
var circle = new Circle(150, 150, 75);
var mouseX, mouseY, selectedArc;
var width = 5;
var height = 5;
var dragging = false;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var arcs = [circle.arcA, circle.arcB, circle.arcC, circle.arcD];
var points = document.getElementsByClassName("point");
var arc;
for (var a = 0; a < points.length; a++) {
arc = arcs[a];
points[a].setAttribute('data-linkedID', a);
points[a].style.left = (arc.pointA.x - width) + "px";
points[a].style.top = (arc.pointA.y - height) + "px";
points[a].addEventListener("mousedown", dragStarted);
}
document.addEventListener("mousemove", drag);
document.addEventListener("mouseup", dragStop);
function dragStarted(e) {
mouseX = e.pageX;
mouseY = e.pageY;
selectedArc = arcs[e.target.parentElement.getAttribute("data-linkedID")];
selectedArc.saveControlPoints();
dragging = true;
}
function drag(e) {
if (dragging) {
selectedArc.update(e.pageX - width, e.pageY - height, e.pageX - mouseX, e.pageY - mouseY);
update();
var arc;
for (var a = 0; a < points.length; a++) {
arc = arcs[a];
points[a].style.left = (arc.pointA.x - width) + "px";
points[a].style.top = (arc.pointA.y - height) + "px";
}
}
}
function dragStop(e) {
dragging = false;
}
function update() {
ctx.fillStyle = "#eeeeee";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
arcs.forEach(function(arc) {
ctx.moveTo(arc.pointA.x, arc.pointA.y);
ctx.bezierCurveTo(arc.controlPointA.x, arc.controlPointA.y, arc.controlPointB.x, arc.controlPointB.y, arc.pointB.x, arc.pointB.y);
});
ctx.stroke();
}
update();
#container {
position: absolute;
}
#canvas {
position: absolute;
top: 0px;
left: 0px;
}
.point {
position: absolute;
width: 10px;
height: 10px;
}
<div id="container">
<canvas id="canvas" width=300 height=300></canvas>
<svg class="point" id="pointA">
<circle cx="5" cy="5" r="5" fill="red" />
</svg>
<svg class="point" id="pointB">
<circle cx="5" cy="5" r="5" fill="red" />
</svg>
<svg class="point" id="pointC">
<circle cx="5" cy="5" r="5" fill="red" />
</svg>
<svg class="point" id="pointD">
<circle cx="5" cy="5" r="5" fill="red" />
</svg>
</div>