如何将曲线添加到konva中的文本路径线?

问题描述 投票:0回答:1

我正在尝试使用 Konva 的文本路径对象在组中绘制一条弯曲的自由线。该组由带有锚点的自由线、后箭头和前箭头组成。

这是我的演示 - as codepen

function getSqDist(p1, p2) {

  var dx = p1.x - p2.x,
    dy = p1.y - p2.y;

  return dx * dx + dy * dy;
}

function getSqSegDist(p, p1, p2) {

  var x = p1.x,
    y = p1.y,
    dx = p2.x - x,
    dy = p2.y - y;

  if (dx !== 0 || dy !== 0) {

    var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);

    if (t > 4) {
      x = p2.x;
      y = p2.y;

    } else if (t > 0) {
      x += dx * t;
      y += dy * t;
    }
  }

  dx = p.x - x;
  dy = p.y - y;

  return dx * dx + dy * dy;
}

function simplifyRadialDist(points, sqTolerance) {

  var prevPoint = points[0],
    newPoints = [prevPoint],
    point;

  for (var i = 1, len = points.length; i < len; i++) {
    point = points[i];

    if (getSqDist(point, prevPoint) > sqTolerance) {
      newPoints.push(point);
      prevPoint = point;
    }
  }

  if (prevPoint !== point) newPoints.push(point);

  return newPoints;
}

function simplifyDPStep(points, first, last, sqTolerance, simplified) {
  var maxSqDist = sqTolerance,
    index;

  for (var i = first + 1; i < last; i++) {
    var sqDist = getSqSegDist(points[i], points[first], points[last]);

    if (sqDist > maxSqDist) {
      index = i;
      maxSqDist = sqDist;
    }
  }

  if (maxSqDist > sqTolerance) {
    if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
    simplified.push(points[index]);
    if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
  }
}

function simplifyDouglasPeucker(points, sqTolerance) {
  var last = points.length - 1;

  var simplified = [points[0]];
  simplifyDPStep(points, 0, last, sqTolerance, simplified);
  simplified.push(points[last]);

  return simplified;
}

function simplify(points, tolerance, highestQuality) {

  if (points.length <= 2) return points;

  var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;

  points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
  points = simplifyDouglasPeucker(points, sqTolerance);

  return points;
}


/*
 * From here onwards we set up the stage and its contents.
 */
const stage = new Konva.Stage({
    container: 'container',
    width: window.innerWidth,
    height: window.innerHeight
  }),

  layer = new Konva.Layer();

var group = new Konva.Group({
  x: 0,
  y: 0,
  draggable: true,
  id: 'freeline-group'
});

var anchor = new Konva.Circle({
  radius: 8,
  fill: 'red',
  visible: true,
  draggable: true,
})

var front = new Konva.Text({
  text: '▶',
  fill: 'black',
  fontSize: 30,
  offsetY: 25 / 2,
  offsetX: 15 / 2,
})

// var group;

anchor.on('mouseenter', function() {
  group.draggable(false);
});

anchor.on('mouseout', function() {
  group.draggable(true);
});

stage.add(layer);

let
  info = document.querySelector("#info"), // het the handle to the html info element for showing point stats
  lineCnt = -1, // global used to could lines and generate anchor names
  points = [], // list of raw points
  simplifiedPts = [], // simplified list of points
  drawingState = '', // we will need to know when we are drawing and when dragging
  selectedObject = "",
  moving_anchor, // global to hold the current anchor when dragging
  mouseOffset = {
    x: 0,
    y: 0
  }, // from corner of dragging anchor
  lines = [],
  currentLine = 0;

/*
 * This function draws a path based on a list of points.
 */
function drawPoints() {
  let myStage = stage;
  // If we drew this path before then remove the current point markers ready to redraw
  // Note we have to do this because the simplification process could move the points
  const kids = layer.find('.anchor' + currentLine);
  for (let i = 0; i < kids.length; i++) {
    kids[i].remove();
  }
  console.log(lines[currentLine].points);

  let pathData = "";
  for (let i = 0; i < lines[currentLine].points.length; i++) {

    let pt = lines[currentLine].points[i];


    switch (i) {
      case 0:
        pathData += "M " + pt.x + " " + pt.y;
        break;
      case 1:
        pathData += "C " + pt.x + " " + pt.y;
        break;
      default:
        pathData += " " + pt.x + " " + pt.y;
        break;
    }



    const newAnchor = anchor.clone({
      x: pt.x,
      y: pt.y,
      name: 'anchor' + currentLine,
      dragBoundFunc: function(pos) {
        var newX = 0;
        var newY = 0;
        var maxWidth = stage.width() - 10;
        var maxHeight = stage.height() - 10;

        if (pos.x < 10 || pos.y < 10) {
          newX = pos.x < 10 ? 10 : pos.x;
          newY = pos.y < 10 ? 10 : pos.y;
        } else if (pos.x > maxWidth || pos.y > maxHeight) {
          newX = pos.x > maxWidth ? maxWidth : pos.x;
          newY = pos.y > maxHeight ? maxHeight : pos.y;
        } else {
          newX = pos.x;
          newY = pos.y;
        }

        return {
          x: newX,
          y: newY
        }
      }
    });
    const newArrow = front.setAttrs({
      x: pt.x - 0,
      y: pt.y - 0,
      name: 'anchor' + currentLine
    });
    newAnchor.setAttrs({
      lineNo: currentLine,
      anchorNo: i
    }); // store the point sequence in the shape so we can get it on mousedown

    group.add(newAnchor, newArrow); // add anchor circle to the layer
  }

  // set the new path data into the paths objects.
  lines[currentLine].path.data(pathData);
  lines[currentLine].backA.data(pathData);
}

/*
 * This function is called each time the mousemove event is fired.
 * Note that before we draw the points we will simplify them, removing any unneeded points.
 */
function addPoint(pt) {

  // store the new raw point
  points.push(pt);

  // simplify the raw points to make an equiv line with fewer points.
  lines[currentLine].points = simplify(points);

  drawPoints();

}

stage.on('mousedown', function(evt) {
  // console.log("mousedown started")
  let myStage = stage;

  let shape = evt.target;
  const pt = stage.getPointerPosition();

  if (shape.name().length === 0) {
    drawLine();
  }

  if (drawingState === "beforeDraw") {
    // console.log("if")
    drawingState = 'drawing';
  } else {
    // console.log("else")
    if (shape.name().length > 0) {
      drawingState = 'moving';
      moving_anchor = shape;
      let anchor_no = moving_anchor.getAttr('anchorNo');
      let pt = lines[currentLine].points[anchor_no];
      mouseOffset = {
        x: shape.x() - pt.x,
        y: shape.y() - pt.y
      };

    }
  }
})

// user draws with mouse held down and moving
stage.on('mousemove', function(e) {
  let shape = stage;
  const pt = stage.getPointerPosition();

  if (drawingState === 'drawing') {
    addPoint(pt)
    // console.log('points adding')
  }
  if (drawingState === 'moving') {
    // console.log('points moving')
    currentLine = moving_anchor.getAttr('lineNo')
    let anchor_no = moving_anchor.getAttr('anchorNo');
    // moving_anchor.draggable(true)
    moving_anchor.position({
      x: pt.x,
      y: pt.y
    });
    lines[currentLine].points[anchor_no] = {
      x: pt.x - group.x(),
      y: pt.y - group.y()
    };
    drawPoints();
  }

})

// user ends drawing with mouse up  
stage.on('mouseup', function(e) {
  drawingState = "";
  reset(false)
})

// reset the stage and points lists
function reset(clear) {
  if (clear) {
    group.removeChildren();
  }
  points = [];
}
reset(true);


let backA = null;

function drawLine() {


  // console.log(1 + " runs drawLine function")
  drawingState = 'beforeDraw';
  lineCnt++;

  let dData = '';
  for (let i = 0; i < 500; i++) {
    dData += "-"
  }
  path = new Konva.TextPath({
    text: dData,
    fill: 'black',
    bezier: true,
    tension: 0.5
  })

  backA = new Konva.TextPath({
    text: '<',
    fill: 'black',
    fontSize: 45,
  });


  group.add(backA, path);
  layer.add(group);

  currentLine = lineCnt;
  lines[lineCnt] = {
    path: path,
    backA: backA,
    points: []
  };

  path.on('mouseenter', function() {
    document.body.style.cursor = "pointer"
  })

  path.on('mouseout', function() {
    document.body.style.cursor = "default"
  })
}

document.getElementById('changeArrow').addEventListener('click', function() {
  if (backA === null) {
    return
  }
  backA.text("‹")
})

document.getElementById('changeArrow2').addEventListener('click', function() {
  if (backA === null) {
    return
  }
  backA.text("«")
})

document.getElementById('changeNoarrow').addEventListener('click', function() {
  if (backA === null) {
    return
  }
  backA.text(" ")
})
body {
  margin: 20px;
  padding: 0;
  overflow: hidden;
  background-color: #f0f0f0;
}

#container {
  border: 1px solid red;
  width: 1000px;
  height: 400px;
}
<script src="https://unpkg.com/konva@8/konva.min.js"></script>
<div>
  <button id='reset' onclick="reset(true)"> Remove </button>
</div>
<div id="container"></div>

您能指导我如何给这条线画曲线吗?谢谢!

konvajs konva
1个回答
0
投票

Konva.TextPath 使用 data 属性来定义路径。这使用一组精简的 SVG 命令来定义路径。目前支持的 SVG 数据命令有 M、m、L、l、H、h、V、v、Q、q、T、t、C、c、S、s、A、a、Z、z。

请参阅官方文档此处

使用计算出的曲线点创建 SVG 命令,以使路径与您的曲线匹配。

© www.soinside.com 2019 - 2024. All rights reserved.