中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Javascript中怎么生成平滑曲線

發布時間:2021-07-13 10:24:17 來源:億速云 閱讀:138 作者:Leah 欄目:開發技術

這篇文章給大家介紹Javascript中怎么生成平滑曲線,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

    前言

    Javascript中怎么生成平滑曲線

    平滑曲線生成是一個很實用的技術

    很多時候,我們都需要通過繪制一些折線,然后讓計算機平滑的連接起來,

    先來看下最終效果(紅色為我們輸入的直線,藍色為擬合過后的曲線) 首尾可以特殊處理讓圖形看起來更好:)

    Javascript中怎么生成平滑曲線

    實現思路是利用貝塞爾曲線進行擬合

    貝塞爾曲線簡介

    貝塞爾曲線(英語:Bézier curve)是計算機圖形學中相當重要的參數曲線。

    二次貝塞爾曲線

    Javascript中怎么生成平滑曲線

    二次方貝塞爾曲線的路徑由給定點P0、P1、P2的函數B(t)追蹤:

    Javascript中怎么生成平滑曲線

    三次貝塞爾曲線

    Javascript中怎么生成平滑曲線

    對于三次曲線,可由線性貝塞爾曲線描述的中介點Q0、Q1、Q2,和由二次曲線描述的點R0、R1所建構

    Javascript中怎么生成平滑曲線

    貝塞爾曲線計算函數

    根據上面的公式我們可有得到計算函數

    二階

      /**
       *
       *
       * @param {number} p0
       * @param {number} p1
       * @param {number} p2
       * @param {number} t
       * @return {*}
       * @memberof Path
       */
      bezier2P(p0: number, p1: number, p2: number, t: number) {
        const P0 = p0 * Math.pow(1 - t, 2);
        const P1 = p1 * 2 * t * (1 - t);
        const P2 = p2 * t * t;
        return P0 + P1 + P2;
      }
      
        /**
       *
       *
       * @param {Point} p0
       * @param {Point} p1
       * @param {Point} p2
       * @param {number} num
       * @param {number} tick
       * @return {*}  {Point}
       * @memberof Path
       */
      getBezierNowPoint2P(
          p0: Point,
          p1: Point,
          p2: Point,
          num: number,
          tick: number,
      ): Point {
        return {
          x: this.bezier2P(p0.x, p1.x, p2.x, num * tick),
          y: this.bezier2P(p0.y, p1.y, p2.y, num * tick),
        };
      }
      
        /**
       * 生成二次方貝塞爾曲線頂點數據
       *
       * @param {Point} p0
       * @param {Point} p1
       * @param {Point} p2
       * @param {number} [num=100]
       * @param {number} [tick=1]
       * @return {*}
       * @memberof Path
       */
      create2PBezier(
          p0: Point,
          p1: Point,
          p2: Point,
          num: number = 100,
          tick: number = 1,
      ) {
        const t = tick / (num - 1);
        const points = [];
        for (let i = 0; i < num; i++) {
          const point = this.getBezierNowPoint2P(p0, p1, p2, i, t);
          points.push({x: point.x, y: point.y});
        }
        return points;
      }

    三階

    /**
       * 三次方塞爾曲線公式
       *
       * @param {number} p0
       * @param {number} p1
       * @param {number} p2
       * @param {number} p3
       * @param {number} t
       * @return {*}
       * @memberof Path
       */
      bezier3P(p0: number, p1: number, p2: number, p3: number, t: number) {
        const P0 = p0 * Math.pow(1 - t, 3);
        const P1 = 3 * p1 * t * Math.pow(1 - t, 2);
        const P2 = 3 * p2 * Math.pow(t, 2) * (1 - t);
        const P3 = p3 * Math.pow(t, 3);
        return P0 + P1 + P2 + P3;
      }
      
        /**
       * 獲取坐標
       *
       * @param {Point} p0
       * @param {Point} p1
       * @param {Point} p2
       * @param {Point} p3
       * @param {number} num
       * @param {number} tick
       * @return {*}
       * @memberof Path
       */
      getBezierNowPoint3P(
          p0: Point,
          p1: Point,
          p2: Point,
          p3: Point,
          num: number,
          tick: number,
      ) {
        return {
          x: this.bezier3P(p0.x, p1.x, p2.x, p3.x, num * tick),
          y: this.bezier3P(p0.y, p1.y, p2.y, p3.y, num * tick),
        };
      }
      
        /**
       * 生成三次方貝塞爾曲線頂點數據
       *
       * @param {Point} p0 起始點  { x : number, y : number}
       * @param {Point} p1 控制點1 { x : number, y : number}
       * @param {Point} p2 控制點2 { x : number, y : number}
       * @param {Point} p3 終止點  { x : number, y : number}
       * @param {number} [num=100]
       * @param {number} [tick=1]
       * @return {Point []}
       * @memberof Path
       */
      create3PBezier(
          p0: Point,
          p1: Point,
          p2: Point,
          p3: Point,
          num: number = 100,
          tick: number = 1,
      ) {
        const pointMum = num;
        const _tick = tick;
        const t = _tick / (pointMum - 1);
        const points = [];
        for (let i = 0; i < pointMum; i++) {
          const point = this.getBezierNowPoint3P(p0, p1, p2, p3, i, t);
          points.push({x: point.x, y: point.y});
        }
        return points;
      }

    擬合算法

    Javascript中怎么生成平滑曲線

    問題在于如何得到控制點,我們以比較簡單的方法

    取 p1-pt-p2的角平分線 c1c2垂直于該條角平分線 c2為p2的投影點取短邊作為c1-pt c2-pt的長度對該長度進行縮放 這個長度可以大概理解為曲線的彎曲程度

    Javascript中怎么生成平滑曲線

    ab線段 這里簡單處理 只使用了二階的曲線生成 -> ? 這里可以按照個人想法處理

    bc線段使用abc計算出來的控制點c2和bcd計算出來的控制點c3 以此類推

      /**
       * 生成平滑曲線所需的控制點
       *
       * @param {Vector2D} p1
       * @param {Vector2D} pt
       * @param {Vector2D} p2
       * @param {number} [ratio=0.3]
       * @return {*}
       * @memberof Path
       */
      createSmoothLineControlPoint(
          p1: Vector2D,
          pt: Vector2D,
          p2: Vector2D,
          ratio: number = 0.3,
      ) {
        const vec1T: Vector2D = vector2dMinus(p1, pt);
        const vecT2: Vector2D = vector2dMinus(p1, pt);
        const len1: number = vec1T.length;
        const len2: number = vecT2.length;
        const v: number = len1 / len2;
        let delta;
        if (v > 1) {
          delta = vector2dMinus(
              p1,
              vector2dPlus(pt, vector2dMinus(p2, pt).scale(1 / v)),
          );
        } else {
          delta = vector2dMinus(
              vector2dPlus(pt, vector2dMinus(p1, pt).scale(v)),
              p2,
          );
        }
        delta = delta.scale(ratio);
        const control1: Point = {
          x: vector2dPlus(pt, delta).x,
          y: vector2dPlus(pt, delta).y,
        };
        const control2: Point = {
          x: vector2dMinus(pt, delta).x,
          y: vector2dMinus(pt, delta).y,
        };
        return {control1, control2};
      }
      
        /**
       * 平滑曲線生成
       *
       * @param {Point []} points
       * @param {number} ratio
       * @return {*}
       * @memberof Path
       */
      createSmoothLine(points: Point[], ratio: number = 0.3) {
        const len = points.length;
        let resultPoints = [];
        const controlPoints = [];
        if (len < 3) return;
        for (let i = 0; i < len - 2; i++) {
          const {control1, control2} = this.createSmoothLineControlPoint(
              new Vector2D(points[i].x, points[i].y),
              new Vector2D(points[i + 1].x, points[i + 1].y),
              new Vector2D(points[i + 2].x, points[i + 2].y),
              ratio,
          );
          controlPoints.push(control1);
          controlPoints.push(control2);
          let points1;
          let points2;
    
          // 首端控制點只用一個
          if (i === 0) {
            points1 = this.create2PBezier(points[i], control1, points[i + 1], 50);
          } else {
            console.log(controlPoints);
            points1 = this.create3PBezier(
                points[i],
                controlPoints[2 * i - 1],
                control1,
                points[i + 1],
                50,
            );
          }
          // 尾端部分
          if (i + 2 === len - 1) {
            points2 = this.create2PBezier(
                points[i + 1],
                control2,
                points[i + 2],
                50,
            );
          }
    
          if (i + 2 === len - 1) {
            resultPoints = [...resultPoints, ...points1, ...points2];
          } else {
            resultPoints = [...resultPoints, ...points1];
          }
        }
        return resultPoints;
      }

    案例代碼

    const input = [
            { x: 0, y: 0 },
            { x: 150, y: 150 },
            { x: 300, y: 0 },
            { x: 400, y: 150 },
            { x: 500, y: 0 },
            { x: 650, y: 150 },
        ]
        const s = path.createSmoothLine(input);
        let ctx = document.getElementById('cv').getContext('2d');
        ctx.strokeStyle = 'blue';
        ctx.beginPath();
        ctx.moveTo(0, 0);
        for (let i = 0; i < s.length; i++) {
            ctx.lineTo(s[i].x, s[i].y);
        }
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(0, 0);
        for (let i = 0; i < input.length; i++) {
            ctx.lineTo(input[i].x, input[i].y);
        }
        ctx.strokeStyle = 'red';
        ctx.stroke();
        document.getElementById('btn').addEventListener('click', () => {
            let app = document.getElementById('app');
            let index = 0;
            let move = () => {
                if (index < s.length) {
                    app.style.left = s[index].x - 10 + 'px';
                    app.style.top = s[index].y - 10 + 'px';
                    index++;
                    requestAnimationFrame(move)
                }
            }
            move()
        })

    附錄:Vector2D相關的代碼

    /**
     *
     *
     * @class Vector2D
     * @extends {Array}
     */
    class Vector2D extends Array {
      /**
       * Creates an instance of Vector2D.
       * @param {number} [x=1]
       * @param {number} [y=0]
       * @memberof Vector2D
       * */
      constructor(x: number = 1, y: number = 0) {
        super();
        this.x = x;
        this.y = y;
      }
    
      /**
       *
       * @param {number} v
       * @memberof Vector2D
       */
      set x(v) {
        this[0] = v;
      }
    
      /**
       *
       * @param {number} v
       * @memberof Vector2D
       */
      set y(v) {
        this[1] = v;
      }
    
      /**
       *
       *
       * @readonly
       * @memberof Vector2D
       */
      get x() {
        return this[0];
      }
    
      /**
       *
       *
       * @readonly
       * @memberof Vector2D
       */
      get y() {
        return this[1];
      }
    
      /**
       *
       *
       * @readonly
       * @memberof Vector2D
       */
      get length() {
        return Math.hypot(this.x, this.y);
      }
    
      /**
       *
       *
       * @readonly
       * @memberof Vector2D
       */
      get dir() {
        return Math.atan2(this.y, this.x);
      }
    
      /**
       *
       *
       * @return {*}
       * @memberof Vector2D
       */
      copy() {
        return new Vector2D(this.x, this.y);
      }
    
      /**
       *
       *
       * @param {*} v
       * @return {*}
       * @memberof Vector2D
       */
      add(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
      }
    
      /**
       *
       *
       * @param {*} v
       * @return {*}
       * @memberof Vector2D
       */
      sub(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
      }
    
      /**
       *
       *
       * @param {*} a
       * @return {Vector2D}
       * @memberof Vector2D
       */
      scale(a) {
        this.x *= a;
        this.y *= a;
        return this;
      }
    
      /**
       *
       *
       * @param {*} rad
       * @return {*}
       * @memberof Vector2D
       */
      rotate(rad) {
        const c = Math.cos(rad);
        const s = Math.sin(rad);
        const [x, y] = this;
    
        this.x = x * c + y * -s;
        this.y = x * s + y * c;
    
        return this;
      }
    
      /**
       *
       *
       * @param {*} v
       * @return {*}
       * @memberof Vector2D
       */
      cross(v) {
        return this.x * v.y - v.x * this.y;
      }
    
      /**
       *
       *
       * @param {*} v
       * @return {*}
       * @memberof Vector2D
       */
      dot(v) {
        return this.x * v.x + v.y * this.y;
      }
    
      /**
       * 歸一
       *
       * @return {*}
       * @memberof Vector2D
       */
      normalize() {
        return this.scale(1 / this.length);
      }
    }
    
    /**
     * 向量的加法
     *
     * @param {*} vec1
     * @param {*} vec2
     * @return {Vector2D}
     */
    function vector2dPlus(vec1, vec2) {
      return new Vector2D(vec1.x + vec2.x, vec1.y + vec2.y);
    }
    
    /**
     * 向量的減法
     *
     * @param {*} vec1
     * @param {*} vec2
     * @return {Vector2D}
     */
    function vector2dMinus(vec1, vec2) {
      return new Vector2D(vec1.x - vec2.x, vec1.y - vec2.y);
    }
    
    export {Vector2D, vector2dPlus, vector2dMinus};

    關于Javascript中怎么生成平滑曲線就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    繁昌县| 搜索| 杭州市| 凤山县| 潮安县| 林芝县| 保山市| 唐河县| 沙河市| 宜宾市| 泌阳县| 宁强县| 社旗县| 佛冈县| 永定县| 名山县| 阿勒泰市| 封丘县| 景泰县| 隆子县| 兰坪| 宜兴市| 界首市| 弋阳县| 鄂州市| 都匀市| 辰溪县| 唐河县| 磐安县| 永丰县| 武邑县| 米泉市| 沈丘县| 岢岚县| 鄂伦春自治旗| 博爱县| 广平县| 惠水县| 福州市| 湟源县| 利川市|