前端 貝塞爾曲線 相關(guān)知識(shí)

前端 BezierCurves 相關(guān)知識(shí)

什么是貝塞爾曲線

只需要很少的控制點(diǎn)就能夠生成復(fù)雜平滑曲線(更加具體的解釋可以搜索一下)

Photoshop的鋼筆工具

感知一下

一般不會(huì)用到的高深專業(yè)參考書

掛鉤的前端技術(shù)

canvas

canvas繪制二次貝塞爾曲線 -- quadraticCurveTo(demo2_1)

    var myCanvas = document.getElementById('myCanvas');
    var myCtx = myCanvas.getContext('2d');

    myCtx.beginPath();
    myCtx.moveTo(P0.x, P0.y);

    //利用quadraticCurveTo 繪制canvas 二次貝塞爾曲線
    // 填入控制點(diǎn)(P1) 和 終點(diǎn)(p2)
    myCtx.quadraticCurveTo(P1.x, P1.y, P2.x, P2.y);

    myCtx.stroke();
    myCtx.closePath();
topic_c5_5.png

canvas繪制三次貝塞爾曲線 -- bezierCurveTo(demo2_2)

    var myCanvas = document.getElementById('myCanvas');
    var myCtx = myCanvas.getContext('2d');

    myCtx.beginPath();
    myCtx.moveTo(P0.x, P0.y);

    //利用bezierCurveTo 繪制canvas 三次貝塞爾曲線
    // 填入控制點(diǎn)(P1,P2) 和 終點(diǎn)(p3)
    myCtx.bezierCurveTo(P1.x, P1.y, P2.x, P2.y, P3.x, P3.y);

    myCtx.stroke();
    myCtx.closePath();
topic_c5_6.png

svg(demo2_3)

svg -- path 可以繪制貝塞爾曲線

C 后面參數(shù)為 控制點(diǎn)P1,控制點(diǎn)P2,以及終點(diǎn)P3
S 后面參數(shù)為 控制點(diǎn)P1 和終點(diǎn)P3 (省略了P2)
Q 后面為控制點(diǎn)P1 和終點(diǎn)P2
T 后面為終點(diǎn)P2(省略P1)
L 直線

<svg width="190px" height="860px">
    <!--M moveTo  C 三次貝塞爾曲線  S 簡(jiǎn)化的三次貝塞爾曲線  Q 二次貝塞爾曲線 T 簡(jiǎn)化的二次貝塞爾曲線  L  直線-->

    <path d="M10 10 C 20 20, 40 20, 50 10" style="fill:none;stroke:red;"/>

    <path d="M10 50 S 150 150, 200 50" style="fill:none;stroke:red;"/>

    <path d="M10 150 Q 150 250, 200 150" style="fill:none;stroke:red;"/>

    <path d="M10 250 T 50 350 T 200 200" style="fill:none;stroke:red;"/>

    <path d="M10 350 L 50 450 L 200 300" style="fill:none;stroke:red;"/>
</svg>
topic_c5_8.png

動(dòng)畫 緩動(dòng)效果(demo2_4)

  • css ease 目前chrome瀏覽器 有內(nèi)置的工具

特殊的緩懂可以實(shí)現(xiàn)一些特殊的效果,例如(Out · Back)

![Uploading topic_c5_1_669418.png . . .]
  • canvas 動(dòng)畫也需要 ease效果,各個(gè)canvas 動(dòng)畫庫(kù)都有支持,也涉及一系列動(dòng)畫的算法,不在此展開。

貝塞爾曲線繪制原理(demo1_1)

  • 初始化二次貝塞爾曲線的起始點(diǎn),控制點(diǎn)和終點(diǎn)
    var myCanvas = document.getElementById('myCanvas');
    var myCtx = myCanvas.getContext('2d');

    //初始化3個(gè)點(diǎn)
    var P0 = {
        x: 30,
        y: 30,
        name: 'P0'
    };
    var P1 = {
        x: 300,
        y: 300,
        name: 'P1'
    };
    var P2 = {
        x: 500,
        y: 30,
        name: 'P2'
    };


    var PointList = [P0, P1, P2];
    //繪制三個(gè)點(diǎn)
    for (var i = 0; i < PointList.length; i++) {
        var point = PointList[i];
        fillPoint(myCtx, point);
    }

    //連線P0,P1,P2
    linePoint(myCtx, PointList);

     function fillPoint(ctx, point) {
            if (!ctx || !point) {
                return false;
            }
            ctx.beginPath();
            ctx.arc(point.x, point.y, 2, 0, 2 * Math.PI, true);
            ctx.fill();
            ctx.font = "36px";
            ctx.fillText(point.name + '(' + point.x + ',' + point.y + ')', point.x + 10, point.y);
            ctx.closePath();
        }

     function linePoint(ctx, PointList) {
              if (!ctx || !PointList.length) {
                  return false;
              }
              ctx.beginPath();
              for (var i = 0; i < PointList.length; i++) {
                  var point = PointList[i];

                  if (i == 0) {
                      ctx.moveTo(point.x, point.y);
                  } else {
                      ctx.lineTo(point.x, point.y);
                  }
              }
              ctx.stroke();
              ctx.closePath();
          }
topic_c5_1.png
  • 確定貝塞爾曲線上的某一點(diǎn)

              // 隨意設(shè)置一個(gè)比例
              var t = 1 / 5;
    
              // 在線段P0P1 上找到t比例的點(diǎn)P3, 即 P0P3:P0P1=t;
              var P3 = {
                  name: 'P3',
                  x: P0.x + (P1.x - P0.x) * t,
                  y: P0.y + (P1.y - P0.y) * t,
              }
              fillPoint(myCtx, P3);
    
              //在線段P1P2 上找到t距離的點(diǎn) P4, 即P1P4:P1P2=t;
              var P4 = {
                  name: 'P4',
                  x: P1.x + (P2.x - P1.x) * t,
                  y: P1.y + (P2.y - P1.y) * t,
              }
              fillPoint(myCtx, P4);
    
              //連線P3P4
              linePoint(myCtx, [P3, P4]);
    
              //在線段P3P4 上找到t距離的點(diǎn)P5  即P3P5:P3P4=t;
              var P5 = {
                  name: 'P5',
                  x: P3.x + (P4.x - P3.x) * t,
                  y: P3.y + (P4.y - P3.y) * t,
              }
              fillPoint(myCtx, P5);
              console.log('P5 在P0P2 為起點(diǎn)終點(diǎn),P1為控制點(diǎn)的 二次貝塞爾曲線上');
    
topic_c5_2.png
  • 將一個(gè)點(diǎn) 循環(huán)成曲線

          var precision = 500;
          for (var i = 0; i < precision; i++) {
              var t = i / precision;
              getBezierPoint(t);
          }
    
          // 根據(jù)t獲得貝塞爾曲線上面的點(diǎn)
          function getBezierPoint(t) {
              // 在線段P0P1 上找到t比例的點(diǎn)P3, 即 P0P3:P0P1=t;
              var P3 = getTPoint(myCtx, P0, P1, t, {needfill: false})
    
              //在線段P1P2 上找到t距離的點(diǎn) P4, 即P1P4:P1P2=t;
              var P4 = getTPoint(myCtx, P1, P2, t, {needfill: false});
    
              //在線段P3P4 上找到t距離的點(diǎn)P5  即P3P5:P3P4=t;
              var P5 = getTPoint(myCtx, P3, P4, t);
    
              return P5;
          }
    
          /*
           *  在線段P0P1 上找到點(diǎn)TP, 使P0TP:P0P1=t, 并繪制出來(lái)然后返回點(diǎn)
           * */
    
          function getTPoint(myCtx, P0, P1, t, option) {
              var needfill = true, name = '';
    
              if (option) {
                  needfill = typeof option.needfill == 'boolean' ? option.needfill : true;
                  name = option.name || '';
              }
    
              var TP = {
                  name: name,
                  x: P0.x + (P1.x - P0.x) * t,
                  y: P0.y + (P1.y - P0.y) * t,
              }
              if (needfill) {
                  fillPoint(myCtx, TP);
              }
    
              return TP;
          }
    
topic_c5_3.png
  • 依次類推到三次貝塞爾曲線

由二次貝塞爾曲線變成多次貝塞爾曲線,原理是一樣的,一層套一層,一個(gè)迭代的關(guān)系

將 getBezierPoint 重寫,plist將大于等于3 (demo1_4)

// 根據(jù)t獲得貝塞爾曲線上面的點(diǎn)
function getBezierPoint(plist, t) {
    var newlist = [];
    for (var i = 0; i < plist.length - 1; i++) {
        var p = getTPoint(myCtx, plist[i], plist[i + 1], t, {needFill: false});
        newlist.push(p);
    }

    if (newlist.length > 1) {
        return getBezierPoint(newlist, t);
    } else {
        return newlist[0];
    }
}
topic_c5_4.png

代碼倉(cāng)庫(kù)(https://github.com/Lotuslwb/BezierCurves)

使用公式繪制貝塞爾曲線

topic_c5_9.png
topic_c5_10.png
topic_c5_11.png

(n k) 是什么呢

Binomial_coefficient

topic_c5_12.png
  • n=5:第一個(gè)系數(shù)(5 0)=5!/(0!5?。?1。第二個(gè)系數(shù)(5 1)=5!/(1!4!)=5。第三個(gè)系數(shù)(5 2)=5!/(2!3!)=10。接下來(lái)是對(duì)稱的
    function binomial(n, k) {
         if ((typeof n !== 'number') || (typeof k !== 'number'))
      return false;
        var coeff = 1;
        for (var x = n-k+1; x <= n; x++) coeff *= x;
        for (x = 1; x <= k; x++) coeff /= x;
        return coeff;
    }

多次貝塞爾曲線的公式j(luò)s版本

        function BezierFunction(plist, t) {

            if (t > 1 || t < 0) {
                return false;
            }


            // B(t)=P0*(1-t)^5 + 5*P1*t*(1-t)^4+10*P2*t^2(1-t)^3+10*P3*t^3(1-t)^2+ 5*P4*t^4*(1-t)^1+P5*t^5
            // 從 0 開始 到 P(n-1) ~~  3個(gè)點(diǎn) 為 2次貝塞爾曲線
            var n = plist.length - 1;
            var bt = 0;

            for (var i = 0; i <= n; i++) {
                bt += getBinomial(n, i) * plist[i] * Math.pow((1 - t), (n - i)) * Math.pow(t, i);
                return bt;
            }
        }

繪制貝塞爾曲線動(dòng)畫(demo4_1)

只需要將曲線的實(shí)現(xiàn) 簡(jiǎn)化為點(diǎn)的運(yùn)動(dòng)就可以了

    drawKeyframe()

    function drawKeyframe() {
        currentPrecision++;
        if (currentPrecision <= maxPrecision) {
            myCtx.clearRect(0, 0, myCanvas.width, myCanvas.height);

            //繪制三個(gè)點(diǎn)
            for (var i = 0; i < PointList.length; i++) {
                var point = PointList[i];
                fillPoint(myCtx, point);
            }
            //連線P0,P1,P2
            linePoint(myCtx, PointList);
            var p = getBezierPoint(currentPrecision/maxPrecision);

            drawBall(myCtx, p, 10);


            window.requestAnimationFrame(drawKeyframe);
        }
    }

    function drawBall(ctx, point, r) {
        ctx.beginPath();
        ctx.arc(point.x, point.y, r, 0, 2 * Math.PI, true);
        ctx.fill();
        ctx.closePath();
    }


    // 根據(jù)t獲得貝塞爾曲線上面的點(diǎn)
    function getBezierPoint(t) {
        var p = {
            x: quadraticBezierFunction(P0.x, P1.x, P2.x, t),
            y: quadraticBezierFunction(P0.y, P1.y, P2.y, t)
        };
        return p;
    }

如何繪制平滑的貝塞爾曲線(獲取合理的控制點(diǎn))

百度地圖 計(jì)算獲取控制點(diǎn)(demo4_2)

        function getControlPoint() {
            var p0 = PointList[0]; //起始點(diǎn)
            var p2 = PointList[1]; //終止點(diǎn)
            var curveness = 0.3; //邊的曲度
            var inv = 1;
            var p1 = {
                'name': 'P1',
                'x': (p0.x + p2.x) / 2 - inv * (p0.y - p2.y) * curveness,
                'y': (p0.y + p2.y) / 2 - inv * (p0.x - p2.x) * curveness
            };
            p2.name = 'P2';

            PointList.splice(1, 0, p1);
            for (var i = 0; i < PointList.length; i++) {
                var p = PointList[i];
                PointListX.push(p.x);
                PointListY.push(p.y);
            }
        }

貝塞爾曲線的一些應(yīng)用

canvas 大波浪動(dòng)效

用大波浪做loading, qq的例子 如下, 文章鏈接

topic_c5_4.gif
  • 先用canvas + quadraticCurveTo 畫出波浪曲線(demo5_1)
        var myCanvas = document.getElementById('myCanvas');
        var myCtx = myCanvas.getContext('2d');
        var canvasHeight = myCanvas.height, canvasWidth = myCanvas.width;
        var animationFrame;
        //半波長(zhǎng)
        var waveLen = 100, waveHeight = 30;
        //水位線初始點(diǎn)
        var p0 = {
            x: 0,
            y: canvasHeight / 2
        };

        drawKeyframe();

        function drawKeyframe() {
            //記錄當(dāng)前位置
            var currentX = p0.x, currentY = p0.y;
            myCtx.clearRect(0, 0, canvasWidth, canvasHeight);
            myCtx.beginPath();
            myCtx.moveTo(p0.x, p0.y);
            for (var i = 0; currentX <= canvasWidth + waveLen; i++) {
                if (i % 2 == 0) {
                    //上半部波
                    myCtx.quadraticCurveTo(currentX + waveLen, currentY - waveHeight, currentX + waveLen * 2, currentY);
                } else {
                    //下半部波
                    myCtx.quadraticCurveTo(currentX + waveLen, currentY + waveHeight, currentX + waveLen * 2, currentY);
                }
                currentX += waveLen * 2;
                myCtx.moveTo(currentX, currentY)
            }
            myCtx.lineWidth = 5;
            myCtx.fillStyle = "red";
            myCtx.lineTo(canvasWidth, canvasHeight);
            myCtx.lineTo(0, canvasHeight);
            myCtx.lineTo(0, canvasHeight / 2);
            myCtx.fill();
            myCtx.closePath();
            p0.x -= 5;
            animationFrame = window.requestAnimationFrame(drawKeyframe);
        }
  • 增加動(dòng)畫 效果 (demo5_2)
topic_c5_13.gif

貝塞爾曲線擬合計(jì)算

貝塞爾曲線有一個(gè)非常常用的動(dòng)畫效果——MetaBall算法。
相信很多開發(fā)者都見過(guò)類似的動(dòng)畫,例如QQ的小紅點(diǎn)消除,下拉刷新loading等等。
要做好這個(gè)動(dòng)畫,實(shí)際上最重要的就是通過(guò)貝塞爾曲線來(lái)擬合兩個(gè)圖形。
topic_c5_14.png
控制點(diǎn)為兩圓圓心連線的中點(diǎn),
連接線為圖中的這樣一個(gè)矩形,
當(dāng)圓比較小時(shí),這種通過(guò)矩形來(lái)擬合的方式幾乎是沒有問(wèn)題的。
我們把圓放大,就會(huì)不擬合。


    // 圓R0 圓點(diǎn)
    var p0 = {
        x: 120,
        y: 120,
        r: 20
    };
    // 圓R1 圓點(diǎn)
    var p1 = {
        x: 400,
        y: 400,
        r: 20
    };
    //獲得  R0 和 R1 的中點(diǎn)
    var p2 = {
        x: (p0.x + p1.x) / 2,
        y: (p0.y + p1.y) / 2,
        name: 'p2'
    };

    myCtx.beginPath();
    //畫出2個(gè)圓
    myCtx.arc(p0.x, p0.y, p0.r, 0, 2 * Math.PI, true);
    myCtx.stroke();
    myCtx.closePath();

    myCtx.beginPath();
    myCtx.arc(p1.x, p1.y, p1.r, 0, 2 * Math.PI, true);
    myCtx.stroke();
    myCtx.closePath();


    //畫出中點(diǎn)和圓心連線
    linePoint(myCtx, [p0, p1]);
    fillPoint(myCtx, p2);

    // p0 的2個(gè)端點(diǎn)
    var p0_1 = {
        x: p0.x - p0.r / Math.sqrt(2),
        y: p0.y + p0.r / Math.sqrt(2),
        name: 'p0_1'
    }, p0_2 = {
        x: p0.x + p0.r / Math.sqrt(2),
        y: p0.y - p0.r / Math.sqrt(2),
        name: 'p0_2'
    };
    linePoint(myCtx, [p0_1, p0_2]);
    fillPoint(myCtx, p0_1);
    fillPoint(myCtx, p0_2);

    //p1 的2個(gè)端點(diǎn)
    var p1_1 = {
        x: p1.x - p1.r / Math.sqrt(2),
        y: p1.y + p1.r / Math.sqrt(2),
        name: 'p1_1'
    }, p1_2 = {
        x: p1.x + p1.r / Math.sqrt(2),
        y: p1.y - p1.r / Math.sqrt(2),
        name: 'p1_2'
    };
    linePoint(myCtx, [p1_1, p1_2]);
    fillPoint(myCtx, p1_1);
    fillPoint(myCtx, p1_2);

    // 連接2個(gè)圓的端點(diǎn)
    linePoint(myCtx, [p0_1, p1_1]);
    linePoint(myCtx, [p0_2, p1_2]);


    //繪制曲線
    myCtx.beginPath();
    myCtx.strokeStyle = 'red';
    myCtx.moveTo(p0_1.x, p0_1.y);
    myCtx.quadraticCurveTo(p2.x, p2.y, p1_1.x, p1_1.y);
    myCtx.stroke();
    myCtx.closePath();

    myCtx.beginPath();
    myCtx.strokeStyle = 'red';
    myCtx.moveTo(p0_2.x, p0_2.y);
    myCtx.quadraticCurveTo(p2.x, p2.y, p1_2.x, p1_2.y);
    myCtx.stroke();
    myCtx.closePath();
如前面所說(shuō),矩形擬合在半徑較小的情況下,是可以實(shí)現(xiàn)完美擬合的,而當(dāng)半徑變大后,就會(huì)出現(xiàn)貝塞爾曲線與圓相交的情況,導(dǎo)致擬合失敗。

那么如何來(lái)實(shí)現(xiàn)完美的擬合呢?實(shí)際上,也就是說(shuō)貝塞爾曲線與圓的連接點(diǎn)到貝塞爾曲線的控制點(diǎn)的連線,一定是圓的切線,這樣的話,無(wú)論圓的半徑如何變化,貝塞爾曲線一定是與圓擬合的,具體效果如圖所示:
topic_c5_15.jpeg
    // 獲取切點(diǎn)坐標(biāo) 數(shù)組,經(jīng)過(guò)圓外一點(diǎn)有2個(gè)切點(diǎn)
    //p0 為圓心1, p1圓心1, p2為2圓中點(diǎn),r為圓的半徑
    function getTangencyPoint(p0, p1, p2, r, sencond) {
        //獲取小角 角度
        var x = Math.abs(p0.x - p1.x);
        var y = Math.abs(p0.y - p1.y);
        var angles1 = Math.atan(y / x);

        if (sencond) {
            angles1 = Math.PI - angles1;
        }

        //獲取大角 角度
        //中點(diǎn)到圓點(diǎn)的距離
        var len = Math.sqrt((p0.x - p2.x) * (p0.x - p2.x) + (p0.y - p2.y) * (p0.y - p2.y));
        var angles2 = Math.acos(r / len);

        //獲得需要的角度
        var angles3 = Math.abs(angles2 - angles1);
        //獲取距離圓心的距離
        var diffx = Math.cos(angles3) * r;
        var diffy = Math.sin(angles3) * r;


        return [{
            x: p0.x - diffy,
            y: p0.y + diffx
        }, {
            x: p0.x + diffx,
            y: p0.y - diffy
        }]

    }
topic_c5_15.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,896評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容