Cubic Bezier curve is not updated

43 Views Asked by At

We are using fabricjs and creating a simple cubic bezier curve example. We are unable to make the curve update when controlobjectpoint1 is dragged or moved.

Any help will be much appreicated.

// Set up Fabric.js canvas
var canvas = new fabric.Canvas('canvas');

// Control points for the Bezier curve
var startPoint = new fabric.Point(100, 300);
var controlPoint1 = new fabric.Point(200, 100);
var controlPoint2 = new fabric.Point(400, 500);
var endPoint = new fabric.Point(500, 300);

var bezierPath = new fabric.Path('M ' + startPoint.x + ' ' + startPoint.y + ' C ' + controlPoint1.x + ' ' + controlPoint1.y + ', ' + controlPoint2.x + ' ' + controlPoint2.y + ', ' + endPoint.x + ' ' + endPoint.y, {
    fill: '',
    stroke: 'black',
    strokeWidth: 2,
    selectable: false
  });

  console.log(bezierPath.path);
// Display control points
var controlPointRadius = 5;
var controlPointOptions = {
  fill: 'red',
  selectable: true,
  hasControls: false,
  hasBorders: false
};

var startControlPoint = new fabric.Circle({
  left: startPoint.x - controlPointRadius / 2,
  top: startPoint.y - controlPointRadius / 2,
  radius: controlPointRadius,
  ...controlPointOptions
});

var controlPoint1Object = new fabric.Circle({
  left: controlPoint1.x - controlPointRadius / 2,
  top: controlPoint1.y - controlPointRadius / 2,
  radius: controlPointRadius,
  ...controlPointOptions
});

var controlPoint2Object = new fabric.Circle({
  left: controlPoint2.x - controlPointRadius / 2,
  top: controlPoint2.y - controlPointRadius / 2,
  radius: controlPointRadius,
  ...controlPointOptions
});

var endControlPoint = new fabric.Circle({
  left: endPoint.x - controlPointRadius / 2,
  top: endPoint.y - controlPointRadius / 2,
  radius: controlPointRadius,
  ...controlPointOptions
});

document.getElementById("updatepath")
  .addEventListener("click", function(e) {
    updateBezierCurve();
    canvas.renderAll();
  });

// Update Bezier curve when control points are dragged
function updateBezierCurve() {
        
**    bezierPath.path[1] = ['C',50,50,400,500,500,300 ];
    canvas.renderAll();
    console.log(bezierPath.path);**
      
}

// Event listeners for control points
controlPoint1Object.on('moving', updateBezierCurve);
controlPoint2Object.on('moving', updateBezierCurve);
console.log(bezierPath.path);
// Add objects to canvas
canvas.add(bezierPath, startControlPoint, controlPoint1Object, controlPoint2Object, endControlPoint);

option1 updateBezierCurve() is function which is mapped to controlPoint1/2 'moving' event on standard Fabricjs.

beizerPath is path array and inside we have two entries. We changed the values in updateBezierCurve. But it never updates the curve.

We tried string and array option both, but it is not working. We already used set and setcorrds functions also.

Option2: thinking that moving is not working, so we created a button in html and click event should trigger updateVeizerCurve function.

In both options, we can see the function updateBezierCurve is called , but the renderAll is not updating the curve path. It is updating the path inside the array.

1

There are 1 best solutions below

3
Helder Sepulveda On

How about we remove and add a new bezierPath ?

Here is the code

var canvas = new fabric.Canvas('canvas');
const controlPathOptions = {
  fill: '', stroke: 'black',
  strokeWidth: 2,
  selectable: false
}
const controlPointOptions = {
  fill: 'red', stroke: 'black',
  selectable: true,
  hasControls: false,
  hasBorders: false
};

var bezierPath = new fabric.Path(
  'M 20 60 C 100 100, 200 50, 300 100', 
  controlPathOptions
 );
var startControlPoint = new fabric.Circle({
  left: 10, top: 50, radius: 5,
  ...controlPointOptions
});

function startControlPointMove(event) {
  canvas.remove(bezierPath);
  bezierPath = new fabric.Path(
    'M ' + event.e.x + ' ' + event.e.y + ' C 100 100, 200 50, 300 100',
    controlPathOptions
  );
  canvas.add(bezierPath);
}

startControlPoint.on('moving', startControlPointMove);
canvas.add(bezierPath, startControlPoint);
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<canvas id="canvas" width="400" height="200"></canvas>

This is quick and dirty sample code just to show you the curve moves...
There are offsets to account for when we recreate the new path, that I'm just not considering, but once you can do this right on one event you can add code for the others.


performance

If you are concerned with performance then consider not using FabricJS and writing the code to just use what you need, like bezierCurveTo simple enough

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

let points = [{x:30, y:60}, {x:100, y:100}, {x:200, y:50}, {x:300, y:100}]
let selected = -1;

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    ctx.bezierCurveTo(points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y);
    ctx.stroke();

    for (const point of points) {
        ctx.beginPath();
        ctx.arc(point.x, point.y, 6, 0, Math.PI * 2);
        ctx.fillStyle = "#ff0000";
        ctx.fill();
    }
}

canvas.addEventListener('mousedown', function(event) {
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    for (let i = 0; i < points.length; i++) {
        const dist = Math.hypot(points[i].x - mouseX, points[i].y - mouseY);
        if (dist < 6) { // radius for control point
            selected = i;
            canvas.addEventListener('mousemove', onMouseMove);
            canvas.addEventListener('mouseup', onMouseUp);
            break;
        }
    }
});

function onMouseMove(event) {
    const rect = canvas.getBoundingClientRect();
    points[selected].x = event.clientX - rect.left;
    points[selected].y = event.clientY - rect.top;
    draw();
}

function onMouseUp() {
    canvas.removeEventListener('mousemove', onMouseMove);
    canvas.removeEventListener('mouseup', onMouseUp);
    selected = -1;
}


draw();
<canvas id="canvas" width="400" height="180"></canvas>