изменить круг в холсте - PullRequest
       5

изменить круг в холсте

0 голосов
/ 20 февраля 2020

Привет, у меня проблема с холстом.

Я пытаюсь сделать круг, который можно изменить, как this . В демоверсии круг можно изменить

. Проблема состоит в том, чтобы перетащить точку круга, чтобы изменить ее.

Я знаю, как перетаскивать точку на холсте javascript, но как изменить форму линии круга, чтобы следовать точке.

const DEBUG = true;

const WIDTH = window.innerWidth;
const HEIGHT = window.innerHeight;

const MIN_DIMENSION = WIDTH < HEIGHT ? WIDTH : HEIGHT;
const DEFAULT_RADIUS = MIN_DIMENSION * 0.45;
let canvas, ctx;

let cos = Math.cos;
let sin = Math.sin;
let pi = Math.PI;
let pi2 = pi * 2;

class Point {
  constructor(x,y) {
    this.x = x;
    this.y = y;
  }
}


function block(c, cb) {
  c.save();
  c.beginPath();
  cb(c);
  c.closePath();
  c.restore();
}

function circle(c,r) {
  c.arc(0, 0, r, 0, pi2);
}

function debugPoints(c, points) {
  points.forEach((p,i) => {
    if(i % 2 === 0) {
      c.fillStyle = 'red';
    } else {
      c.fillStyle = 'black';
    }
    c.beginPath();
    c.arc(p.x, p.y, 2, 0, pi2);
    c.fill();
    c.closePath();
  })
}

function bezierCirclePoints(r, n) {
  let a = pi2/(2*n);
  let R = r/cos(a);
  
  let points = new Array(2 * n);
  
  
  console.log('n:', n);
  console.log('a:', a);
  console.log('r:', r);
  console.log('R:', R);
  
  // calculate even bezier points
  for(let i = 0; i < n; i++) {
    let i2 = 2*i;
    let x = r * sin(i2 * a);
    let y = -r * cos(i2 * a);
    points[i2] =  new Point(x, y);
  }
  
  // calculate odd bezier points
  for(let i = 0; i < n; i++) {
    let i2 = 2*i + 1;
    let x = R * sin(i2 * a);
    let y = -R * cos(i2 * a);
    points[i2] =  new Point(x, y);
  }
  
  points.push(points[0]);
  return points;
}

function bezierCircle(c, r = DEFAULT_RADIUS, n = 7) {
  let points = bezierCirclePoints(r,n);
  
  c.translate(WIDTH * 0.5,HEIGHT * 0.5);
  
  if(DEBUG) {
    debugPoints(c, points);
  }
  
  c.fillStyle = 'red';
  c.strokeStyle = 'red';

  // draw circle
  c.beginPath();
  let p = points[0];
  c.moveTo(p.x, p.y);
  
  for(let i = 1; i < points.length; i+=2){
      let p1 = points[i];
      let i2 = i + 1;
      if(i2 >= points.length) {
        i2 = 0;
      }  
      let p2 = points[i2];
      c.quadraticCurveTo(p1.x, p1.y, p2.x, p2.y);
  } 
  
  c.stroke();
  c.closePath();
}

function redCircle(c) {
  c.fillStyle = 'red';
  c.translate(200,200);
  circle(c, 100);
  c.fill();
}


canvas = document.getElementById('circle');
canvas.width = WIDTH;
canvas.height = HEIGHT;
ctx = canvas.getContext('2d');

block(ctx, bezierCircle)
<canvas id="circle"></canvas>

1 Ответ

1 голос
/ 20 февраля 2020

Как вы уже поняли, круг может состоять из четырех кривых Безье. Я собираюсь использовать кубический c вместо квадратичного c, хотя он предлагает две контрольные точки.

Давайте начнем с рассмотрения следующей иллюстрации:

circle illustration

Как мы видим, красная кривая состоит из начальной точки 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...