0

My problem is how to make a UI where the user can modify a complex path by dragging anchor points that are on the path. Note on - for example the control points for Bezier curves in this example in the Konvajs documentation are off the path itself, whereas I prefer anchor points on a complex path.

I will be trying to modify a curve created as in this question.

Here is a snippet that draws a sample path. Can anyone help me please? Scroll the snippet window down slightly then click on the rectangle to see the path animation.

    var stage = new Konva.Stage({
        container: 'container',
        width: 500,
        height: 500
    });

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
        x: stage.getWidth() / 2,
        y: stage.getHeight() / 2,
        stroke: '#555',
        strokeWidth: 3,
        fill: '#ddd',
        width: 50,
        height: 50,
        shadowColor: 'black',
        shadowBlur: 10,
        shadowOffset: [10, 10],
        shadowOpacity: 0.2,
        cornerRadius: 10
    });

    var path = new Konva.Path({
        x: stage.getWidth() / 2,
        y: stage.getHeight() / 2,
        data: 'M30 10 C-20 -50 -20 -70 50 -80 C150 -100 50 -150 -100 -80',
        stroke: '#00D2FF',
        strokeWidth: 5
    });

    var pathLen = path.getLength() + 10;
    path.dashOffset(pathLen);
    path.dash([pathLen]);

    var anim = new Konva.Animation(function (frame) {
        var dashLen = pathLen - frame.time / 5;
        if (dashLen < 0) {
            var tween = new Konva.Tween({
                node: path,
                duration: 1,
                strokeWidth: 10,
                easing: Konva.Easings.ElasticEaseOut
            });
            tween.play();
            anim.stop();
        } else {
            path.dashOffset(dashLen);
        }
    }, layer);

    rect.on('mousedown', function() {
        anim.start();
    })


    layer.add(path);
    layer.add(rect);

    stage.add(layer);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 500px, height: 500px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>

Also, can anyone suggest why part of the curve is drawn before the animation runs?

ramory-l
  • 119
  • 2
  • 9
  • Well done with the snippet. The initial stroke that you see is going to be a bug in your maths - the initial dashOffset is not correct. Regarding the control points: for bezier curves the control points are naturally outside of the curve. You could possibly try what you require by putting draggable tags on the curve then computing the offset of the control point based on the movement the user applies to the tag. I would first tackle the issue of how to put a tag on the path at appropriate places. – Vanquished Wombat Nov 10 '18 at 17:09
  • @Vanquished Wombat, I edited my code a bit. Now there is no part of the curve before animation and the curve now does not disappear when you repeatedly click on the rectangle. – ramory-l Nov 11 '18 at 09:16
  • There is a very similar question [here](https://stackoverflow.com/questions/25446539/control-points-in-canvas-shape), with a link to a working example that may help you. – Vanquished Wombat Nov 12 '18 at 08:29
  • So, I shouldn't use Path object. I must draw my curve like [here](https://stackoverflow.com/questions/25442709/draw-html5-javascript-canvas-path-in-time) and make anchors like [here](https://stackoverflow.com/questions/25446539/control-points-in-canvas-shape). – ramory-l Nov 12 '18 at 09:52
  • I don't think its about how you define the path - single text string v's multiple drawing steps because you can draw the same path with either - it is how you manipulate the points that feels like the issue to me. What I can't 'see' is how to position the control points for a complex path in the UI to give a meaningful picture for the user, and how you manipulate the path when dragged. The [second link](https://stackoverflow.com/questions/25446539/control-points-in-canvas-shape) gives a great example of dragging a simple curve, but what if there were also a line segment in the path? – Vanquished Wombat Nov 12 '18 at 17:41

1 Answers1

0

My solution without anchor on control point:

  var stage = new Konva.Stage({
    container: "container",
    width: 500,
    height: 500,
    draggable: true
  });

  var layer = new Konva.Layer();
  var curveLayer = new Konva.Layer();
  var k = 0;

  var rect = new Konva.Rect({
    x: stage.getWidth() / 2,
    y: stage.getHeight() / 2,
    stroke: "#555",
    strokeWidth: 3,
    fill: "#ddd",
    width: 50,
    height: 50,
    shadowColor: "black",
    shadowBlur: 10,
    shadowOffset: [10, 10],
    shadowOpacity: 0.2,
    cornerRadius: 10
  });

  var path = buildPath("M25 25 L-75 -200 C-75 -200 -120 -300 -200 -200");

  var pathLen = path.getLength() + 1000;
  path.dashOffset(pathLen);
  path.dash([pathLen]);

  var anim = new Konva.Animation(function(frame) {
    var dashLen = pathLen - frame.time * 10;
    if (dashLen < 0) {
      var tween = new Konva.Tween({
        node: path,
        duration: 1,
        strokeWidth: 10,
        easing: Konva.Easings.ElasticEaseOut
      });
      var circle = buildAnchorPoint(-75, -200);
      var circle1 = buildAnchorPoint(-200, -200);
      //var circle2 = buildAnchorPoint(-120, -242.5);
      layer.draw();
      circle.on("dragmove", function() {
        var point = stage.getPointerPosition();
        var pointx = point.x - stage.x();
        var pointy = point.y - stage.y();
        console.log(
          "X: " +
            (pointx - stage.getWidth() / 2) +
            " Y: " +
            (pointy - stage.getHeight() / 2)
        );
        path.data(
          "M25 25 L" +
            (pointx - stage.getWidth() / 2) +
            " " +
            (pointy - stage.getHeight() / 2) +
            " C" +
            (pointx - stage.getWidth() / 2) +
            " " +
            (pointy - stage.getHeight() / 2) +
            "-120 -300" +
            (circle1.getX() - stage.getWidth() / 2) +
            " " +
            (circle1.getY() - stage.getHeight() / 2)
        );
        curveLayer.draw();
        layer.draw();
      });
      circle1.on("dragmove", function() {
        var point = stage.getPointerPosition();
        var pointx = point.x - stage.x();
        var pointy = point.y - stage.y();
        console.log(
          "X: " +
            (point.x - stage.getWidth() / 2) +
            " Y: " +
            (point.y - stage.getHeight() / 2)
        );
        path.data(
          "M25 25 L" +
            (circle.getX() - stage.getWidth() / 2) +
            " " +
            (circle.getY() - stage.getHeight() / 2) +
            "C" +
            (circle.getX() - stage.getWidth() / 2) +
            " " +
            (circle.getY() - stage.getHeight() / 2) +
            " -120 -300 " +
            (pointx - stage.getWidth() / 2) +
            " " +
            (pointy - stage.getHeight() / 2)
        );
        curveLayer.draw();
        layer.draw();
      });
      tween.play();
      anim.stop();
    } else {
      path.dashOffset(dashLen);
    }
  }, curveLayer);

  rect.on("mousedown", function() {
    if (k == 0) {
      anim.start();
      k++;
    }
  });

  layer.add(rect);

  stage.add(curveLayer);
  stage.add(layer);

  function buildPath(data) {
    var path = new Konva.Path({
      x: stage.getWidth() / 2,
      y: stage.getHeight() / 2,
      data: data,
      stroke: "#00D2FF",
      strokeWidth: 5,
      lineJoin: "round",
      lineCap: "round"
    });
    curveLayer.add(path);
    return path;
  }

  function buildAnchorPoint(x, y) {
    var circle = new Konva.Circle({
      x: stage.getWidth() / 2 + x,
      y: stage.getHeight() / 2 + y,
      stroke: "#c1fbff",
      strokeWidth: 1,
      fill: "#00D2FF",
      radius: 5,
      cornerRadius: 10,
      draggable: true
    });
    layer.add(circle);
    return circle;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 500px, height: 500px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>

Can someone help with anchor on control point?

Also, I'd like to clean code a bit, because I will have many paths witch anchors, that's why I need to make handlers not like in my code, help please!

ramory-l
  • 119
  • 2
  • 9