<div>
      <div>
        <span>相似比常数</span>
        <input
          type="range"
          id="tension"
          min="-1"
          max="2"
          step=".1"
          value=".5"
        />
        Tension <span id="tensionvalue">(0.5)</span>
      </div>
      <div>
        <span>是否使用距离比例 d1/(d1+d2)</span>
        <label><input type="checkbox" id="useRate" /> 是否使用距离比例</label>
      </div>
    </div>
    <div>
      <canvas id="canvas"></canvas>
    </div>
  * {
        padding: 0;
        margin: 0;
      }
 let isCtrl = false;
      function $(id) {
        return document.getElementById(id);
      }
      function update() {
        $("tensionvalue").innerHTML = "(" + $("tension").value + ")";
        drawLine2();
      }
      $("useRate").onchange = $("tension").onchange = update;

      let type = "WebGL";
      if (!PIXI.utils.isWebGLSupported()) {
        type = "canvas";
      }

      PIXI.utils.sayHello(type);
      //Create a Pixi Application
      let app = new PIXI.Application({
        width: 800, // default: 800
        height: 800, // default: 600
        antialias: true, // default: false
        transparent: false, // default: false
        resolution: 1, // default: 1
        backgroundColor: 0x061639,
        view: document.getElementById("canvas"),
      });

      document.body.appendChild(app.view);

      const canvas = app.view;
      var arr = [];
      const r = 12;
      let currentPoint;

      canvas.addEventListener("click", function (event) {
        mousePosFun(canvas, event);
      });

      function drawLine(from, to) {
        drawLine2();
      }
      function getMiddle(from, to) {
        return {
          pos: {
            x: (from.pos.x + to.pos.x) / 2,
            y: (from.pos.y + to.pos.y) / 2,
          },
        };
      }
      function drawBaseLine(from, to) {
        line = new PIXI.Graphics();
        line.lineStyle(4, 0x1a7af8, 1);
        line.moveTo(from.x, from.y);
        line.lineTo(to.x, to.y);
        app.stage.addChild(line);
        ss.push(line);
      }

      // 数组的两个点的构成的直角三角形的[w,h]
      function va(arr, i, j) {
        return [arr[2 * j] - arr[2 * i], arr[2 * j + 1] - arr[2 * i + 1]];
      }
      // 计算arr数组的i和j两个点之间的距离
      function distance(arr, i, j) {
        return Math.sqrt(
          Math.pow(arr[2 * i] - arr[2 * j], 2) +
            Math.pow(arr[2 * i + 1] - arr[2 * j + 1], 2)
        );
      }
      function controlPoints(x1, y1, x2, y2, x3, y3) {
        var t = Number($("tension").value);
        // 返回 w 和 h=>[w,h]
        var v = va(arguments, 0, 2);
        var d01 = distance(arguments, 0, 1);
        var d12 = distance(arguments, 1, 2);
        // 带入距离比例
        var d012 = d01 + d12;
        let s = t;
        if ($("useRate").checked) {
          s = (t * d01) / d012;
        }
        return [x2 - v[0] * s, y2 - v[1] * s, x2 + v[0] * s, y2 + v[1] * s];
      }

      let ss = [];
      let pts = [];
      function drawLine2() {
        // 将画布的线条全部清空
        for (let ii = 0; ii < ss.length; ii++) {
          const bezier = ss[ii];
          app.stage.removeChild(bezier);
        }
        ss = [];
        pts = [];
        // 将对象的数组转换为点的一维数组形式
        arr
          .map((i) => i.pos)
          .forEach((item) => {
            pts.push(item.x);
            pts.push(item.y);
          });

        cps = []; // 控制点的位置
        // 获取控制点的位置
        for (var i = 0; i < pts.length - 2; i += 1) {
          cps = cps.concat(
            controlPoints(
              pts[2 * i],
              pts[2 * i + 1],
              pts[2 * i + 2],
              pts[2 * i + 3],
              pts[2 * i + 4],
              pts[2 * i + 5]
            )
          );
        }

        let start = arr[0];
        // 两个点直接画直线
        if (arr.length === 2) {
          drawBaseLine(start.pos, arr[1].pos);
        }
        // 多个点
        console.log("pts", pts, "cps", cps);
        let end;
        let control;
        let len = arr.length;
        if (arr.length >= 3) {
          start = arr[0];
          end = arr[1];
          // 开始是一跳二次贝赛尔曲线
          var bezier = new PIXI.Graphics();
          bezier.lineStyle(4, 0x1a7af8, 1);
          bezier.moveTo(start.pos.x, start.pos.y);
          bezier.quadraticCurveTo(cps[0], cps[1], pts[2], pts[3]);
          bezier.endFill();
          start = end;
          app.stage.addChild(bezier);
          ss.push(bezier);
          // 中间的部分是三次贝赛尔曲线
          for (var i = 2; i < len - 1; i += 1) {
            start = end;
            var bezier = new PIXI.Graphics();
            bezier.lineStyle(4, 0x1a7af8, 1);
            bezier.moveTo(start.pos.x, start.pos.y);
            bezier.bezierCurveTo(
              cps[(2 * (i - 1) - 1) * 2],
              cps[(2 * (i - 1) - 1) * 2 + 1],
              cps[2 * (i - 1) * 2],
              cps[2 * (i - 1) * 2 + 1],
              pts[i * 2],
              pts[i * 2 + 1]
            );
            bezier.endFill();
            end = arr[i];
            app.stage.addChild(bezier);
            ss.push(bezier);
          }

          start = end;
          var bezier = new PIXI.Graphics();
          bezier.lineStyle(4, 0x1a7af8, 1);
          bezier.moveTo(start.pos.x, start.pos.y);
          bezier.quadraticCurveTo(
            cps[(2 * (i - 1) - 1) * 2],
            cps[(2 * (i - 1) - 1) * 2 + 1],
            pts[i * 2],
            pts[i * 2 + 1]
          );
          bezier.endFill();
          app.stage.addChild(bezier);
          ss.push(bezier);
        }
      }

      function drawPoint(x, y, first, last) {
        let circle = new PIXI.Graphics();
        circle.beginFill(0x9966ff);
        circle.drawCircle(0, 0, r);
        circle.endFill();
        circle.x = x;
        circle.y = y;
        app.stage.addChild(circle);
        return circle;
      }
      function mousePosFun(canvas, event, manuel) {
        var rect = canvas.getBoundingClientRect();
        var x, y;
        if (!manuel) {
          // 获取坐标
          x = event.clientX - rect.left * (canvas.width / rect.width);
          y = event.clientY - rect.top * (canvas.height / rect.height);
        } else {
          x = manuel.x;
          y = manuel.y;
        }

        let circle = drawPoint(x, y, arr.length === 0);
        let line;
        const data = {
          pos: { x, y }, //点的位置
          circle, // 圆
          line, //线
          id: Math.random(),
        };
        // 看看是否存在前面一项
        arr.push(data);
        drawLine();
      }
      [
        { x: 100, y: 100 },
        { x: 400, y: 100 },
        { x: 400, y: 400 },
        { x: 100, y: 400 },
      ].forEach((item) => {
        mousePosFun(canvas, {}, item);
      });
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.bootcdn.net/ajax/libs/pixi.js/5.3.4/pixi.js