Skip to main content

如何使用锚点修改曲线点?

本演示展示了如何创建可以通过拖动锚点进行修改的交互式曲线(抛物线和贝塞尔曲线)。这种技术通常用于矢量图形编辑器,使用户能够创建和调整自定义曲线。

说明: 使用鼠标或手指拖动锚点,以修改抛物线(红色)和贝塞尔曲线(蓝色)的曲率。

import Konva from 'konva';

const width = window.innerWidth;
const height = window.innerHeight;

// 创建舞台和图层
const stage = new Konva.Stage({
  container: 'container',
  width: width,
  height: height,
});

const layer = new Konva.Layer();
stage.add(layer);

// 构建锚点的函数
function buildAnchor(x, y) {
  const anchor = new Konva.Circle({
    x: x,
    y: y,
    radius: 20,
    stroke: '#666',
    fill: '#ddd',
    strokeWidth: 2,
    draggable: true,
  });
  layer.add(anchor);

  // 添加悬停样式
  anchor.on('mouseover', function () {
    document.body.style.cursor = 'pointer';
    this.strokeWidth(4);
  });
  
  anchor.on('mouseout', function () {
    document.body.style.cursor = 'default';
    this.strokeWidth(2);
  });

  // 当锚点移动时更新曲线
  anchor.on('dragmove', function () {
    updateDottedLines();
  });

  return anchor;
}

// 更新虚线点(显示控制点)的函数
function updateDottedLines() {
  const q = quad;
  const b = bezier;

  const quadLinePath = layer.findOne('#quadLinePath');
  const bezierLinePath = layer.findOne('#bezierLinePath');

  // 更新抛物线的控制点线
  quadLinePath.points([
    q.start.x(),
    q.start.y(),
    q.control.x(),
    q.control.y(),
    q.end.x(),
    q.end.y(),
  ]);

  // 更新贝塞尔曲线的控制点线
  bezierLinePath.points([
    b.start.x(),
    b.start.y(),
    b.control1.x(),
    b.control1.y(),
    b.control2.x(),
    b.control2.y(),
    b.end.x(),
    b.end.y(),
  ]);
}

// 创建具有自定义形状的抛物线
const quadraticLine = new Konva.Shape({
  stroke: 'red',
  strokeWidth: 4,
  sceneFunc: (ctx, shape) => {
    ctx.beginPath();
    ctx.moveTo(quad.start.x(), quad.start.y());
    ctx.quadraticCurveTo(
      quad.control.x(),
      quad.control.y(),
      quad.end.x(),
      quad.end.y()
    );
    ctx.fillStrokeShape(shape);
  },
});
layer.add(quadraticLine);

// 创建具有自定义形状的贝塞尔曲线
const bezierLine = new Konva.Shape({
  stroke: 'blue',
  strokeWidth: 5,
  sceneFunc: (ctx, shape) => {
    ctx.beginPath();
    ctx.moveTo(bezier.start.x(), bezier.start.y());
    ctx.bezierCurveTo(
      bezier.control1.x(),
      bezier.control1.y(),
      bezier.control2.x(),
      bezier.control2.y(),
      bezier.end.x(),
      bezier.end.y()
    );
    ctx.fillStrokeShape(shape);
  },
});
layer.add(bezierLine);

// 创建虚线以显示抛物线的控制点
const quadLinePath = new Konva.Line({
  dash: [10, 10, 0, 10],
  strokeWidth: 3,
  stroke: 'black',
  lineCap: 'round',
  id: 'quadLinePath',
  opacity: 0.3,
  points: [0, 0],
});
layer.add(quadLinePath);

// 创建虚线以展示贝塞尔曲线的控制点
const bezierLinePath = new Konva.Line({
  dash: [10, 10, 0, 10],
  strokeWidth: 3,
  stroke: 'black',
  lineCap: 'round',
  id: 'bezierLinePath',
  opacity: 0.3,
  points: [0, 0],
});
layer.add(bezierLinePath);

// 为抛物线创建锚点
const quad = {
  start: buildAnchor(60, 30),
  control: buildAnchor(240, 110),
  end: buildAnchor(80, 160),
};

// 为贝塞尔曲线创建锚点
const bezier = {
  start: buildAnchor(280, 20),
  control1: buildAnchor(530, 40),
  control2: buildAnchor(480, 150),
  end: buildAnchor(300, 150),
};

// 更新控制点线
updateDottedLines();