不均勻的或者不規(guī)則的雙層波浪如何實現(xiàn)

有一天ui 姐姐想畫一個雙層的波浪用于項目叔遂,大概是這樣的效果
大概是這樣的效果.gif

網(wǎng)絡圖片已艰,如有侵權(quán)立即更換哩掺。

分析:

看起來有點炫酷疮丛,第一次做還有點分不清楚門道。連續(xù)幾天,我都在思考它的原理呢蔫,有空閑就兩只手來回片吊,模擬蕩漾得出幾點特征:

  • 1.雙層波浪协屡,相反的方向運行肤晓,中間是靜態(tài)的不考慮。
  • 2.看著像一層層波浪卷员,上下移動腾务,向x軸方向移動,偶爾還變個速導致忽快忽慢岩瘦,都是錯覺未巫。
  • 3.波浪是不均勻的,非得貝塞爾曲線來做不可启昧。

解釋第2點叙凡,上下移動,向左移動箫津,忽快忽慢的誤導性很大狭姨,這是個巨坑苏遥。注意最下面那層波浪饼拍,把它的波浪想像成固定一個整體,不上下移動田炭,眼睛一直跟隨一個波段师抄,盯住它,你會發(fā)現(xiàn)教硫,它就是一個整體在勻速向左移動叨吮,曲線只是在x軸平移,不斷重復瞬矩,第2點的錯覺是波段的波峰茶鉴,波谷,波長不一致導致的變速感景用。沒有上下移動涵叮。

如何實現(xiàn):
1.path畫一條固定的貝塞爾曲線,曲線的數(shù)據(jù)找 ui小姐姐要大概是這樣子的:


image.png

為什么超出屏幕還有一部分呢伞插?因為它要連接起點割粮,像這樣:


image.png

2.一直向左平移到線的終點:
image.png

運動到曲線的最右點到快到達屏幕邊界內(nèi)時,下一幀復原最開始波浪,一個無限循環(huán)的不規(guī)則波浪就完成了媚污。

我把原型圖里面最下面的波浪拼起來:


all.png

這樣總明白了吧舀瓢!他就是一條完整的曲線。

上手

第一個問題:如何畫一條曲線耗美,可以首尾拼接起來保持平滑京髓?

  1. 畫一個多點相連平滑曲線航缀,android 上解決不了(最多兩個控制點,不能再多了)朵锣,但是數(shù)學可以谬盐。
    安排:http://www.reibang.com/p/98088ff77607
  1. 結(jié)尾處,單獨處理诚些,用一條一個控制點的一階貝賽爾連接飞傀。
    上圖:


    無標題ss.png

最后效果:

效果.gif

因為ui 姐姐需要兩層不透明的,效果以最下面的曲線的為例實現(xiàn)了90%吧诬烹!但是為什么這么丑砸烦,我也不知道,哈哈绞吁。如果給一個好點的曲線數(shù)據(jù)幢痘,也許就不一樣了!比如我換一條均勻的貝賽爾曲線就好看多了:


均勻效果.gif
但是為什么要畫不均勻的波浪呢家破?

上代碼:

wave 類

package com.example.a14143.wave;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.Log;

/**
 * @author DrChen
 * @Date 2019/4/21.
 * qq:1414355045
 * 保存波浪的所有信息
 * 和繪制
 */
public class Wave {
 
    /**
     * 單個波浪寬
     */
    private float waveWith;
    /**
     * 繪制的方向
     */
    boolean isLeft = true;
    /**
     * 單個循環(huán)一半長度占屏幕寬度的百分比(浪的長度等于一個循環(huán)的1/2)
     */
    private float waveWithF;
    /**
     * 繪制的區(qū)域
     */
    private RectF rectF;

    /**
     * 當前移動位置計次
     */
    private int currentMoveX;

    /**
     * 每次移動的距離
     */
    private int moveX;
    /**
     * 移動前的點位
     */
    private float[][] wavePoints;

    private boolean isDebug = false;
    /**
     * 切率
     */
    private float lineSmoothness = 0.12f;
    
    public Path path = new Path();



    private int mHeight;


    /**
     *
     * @param isLeft 向左向右
     * @param rectF  繪制區(qū)域
     * @param points
     */
    public Wave(  boolean isLeft, RectF rectF,float lineSmoothness,int mHeight,int moveX,float[]... points) {

        this.isLeft = isLeft;
        this.mHeight = mHeight;
        if(lineSmoothness>0) this.lineSmoothness = lineSmoothness;
        setRectF(rectF);
        setPoints(points);
        //每次移動的距離
        if (isLeft) {
            this.moveX = - moveX;

        } else {
           this. moveX = moveX;
        }


        measurePath();
    }


    private void setRectF(RectF rectF) {
        this.rectF = rectF;
    }

    /**
     * 傳入數(shù)據(jù)只有控制點颜说,沒有貝賽爾的起點,通過控制點計算各個點位
     */
    private void setPoints(float[]... points) {


        if (points.length < 4 ) {
            throw new RuntimeException("所有點不能小于4個");  //直接手動拋出異常
        }
        if (rectF == null) {
            throw new RuntimeException("沒有繪制區(qū)域就沒有繪制");
        }


        if (wavePoints == null) {
            wavePoints = new float[points.length][6];
        }
    
        waveWithF = points[points.length-1][0];
        waveWith = rectF.width()* waveWithF;
        // 下面是利用各個點連成一條平滑的曲線
        float prePreviousPointX = Float.NaN;
        float prePreviousPointY = Float.NaN;
        float previousPointX = Float.NaN;
        float previousPointY = Float.NaN;
        float currentPointX = Float.NaN;
        float currentPointY = Float.NaN;
        float nextPointX;
        float nextPointY;

        int lineSize = points.length;
        for (int i = 0; i < points.length; i++) {
            if (Float.isNaN(currentPointX)) {

                currentPointX = points[i][0];
                currentPointY = points[i][1];
            }
            if (Float.isNaN(previousPointX)) {
                //是否是第一個點
                if (i > 0) {
                    previousPointX = points[i - 1][0];
                    previousPointY = points[i - 1][1];
                } else {
                    //是的話就用當前點表示上一個點
                    previousPointX = currentPointX;
                    previousPointY = currentPointY;
                }
            }

            if (Float.isNaN(prePreviousPointX)) {
                //是否是前兩個點
                if (i > 1) {

                    prePreviousPointX = points[i][0];
                    prePreviousPointY = points[i][1];
                } else {
                    //是的話就用當前點表示上上個點
                    prePreviousPointX = previousPointX;
                    prePreviousPointY = previousPointY;
                }
            }

            // 判斷是不是最后一個點了
            if (i < lineSize - 1) {

                nextPointX = points[i + 1][0];
                nextPointY = points[i + 1][1];
            } else {
                //是的話就用當前點表示下一個點
                nextPointX = currentPointX;
                nextPointY = currentPointY;
            }

            if (i == 0||i>=points.length-2) {//起點和最后面接頭的地方汰聋,為了保證平滑單獨處理
                // 將Path移動到開始點
                wavePoints[i][0] = currentPointX*rectF.width();
                wavePoints[i][1] = currentPointY*rectF.height()+rectF.top;

                 
            } else {
                // 求出控制點坐標
                final float firstDiffX = (currentPointX - prePreviousPointX);
                final float firstDiffY = (currentPointY - prePreviousPointY);
                final float secondDiffX = (nextPointX - previousPointX);
                final float secondDiffY = (nextPointY - previousPointY);
                final float firstControlPointX = previousPointX + (lineSmoothness * firstDiffX);
                final float firstControlPointY = previousPointY + (lineSmoothness * firstDiffY);
                final float secondControlPointX = currentPointX - (lineSmoothness * secondDiffX);
                final float secondControlPointY = currentPointY - (lineSmoothness * secondDiffY);

                wavePoints[i][0] = firstControlPointX*rectF.width();
                wavePoints[i][1] = firstControlPointY*rectF.height()+rectF.top;
                wavePoints[i][2] = secondControlPointX*rectF.width();
                wavePoints[i][3] = secondControlPointY*rectF.height()+rectF.top;
                wavePoints[i][4] = currentPointX*rectF.width();
                wavePoints[i][5] = currentPointY*rectF.height()+rectF.top;
 
            }

            // 更新值,
            prePreviousPointX = previousPointX;
            prePreviousPointY = previousPointY;
            previousPointX = currentPointX;
            previousPointY = currentPointY;
            currentPointX = nextPointX;
            currentPointY = nextPointY;
        }


    }

    /**
     * 測試時需要
     */
    public void testMoveTo(float moveX) {

        for (int i = 0; i < wavePoints.length; i++) {
            wavePoints[i][0] += moveX * waveWith;
            wavePoints[i][2] += moveX * waveWith;
            wavePoints[i][4] += moveX * waveWith;
        }
    }

    /**
     * 刷新每次移動的點位
     */
    public void measureMoveTo() {
       if(isLeft){
           if(waveWith + currentMoveX < Math.abs(moveX)){
               currentMoveX = 0;
           }else {
               currentMoveX+=moveX;
           }
       }else {
           if(waveWith - currentMoveX< moveX){
               currentMoveX = 0;
           }else {
               currentMoveX+= moveX;
           }

       }

    }



    /**
     *
     *  繪制path
     */
    private void measurePath() {

        path.reset();
        if(isLeft){
            path.moveTo(wavePoints[0][0], mHeight);
            path.lineTo(wavePoints[0][0], wavePoints[0][1]);

            for (int i = 1; i < wavePoints.length-2  ; i ++) {

                path.cubicTo(wavePoints[i][0], wavePoints[i][1],
                        wavePoints[i][2], wavePoints[i][3],
                        wavePoints[i][4], wavePoints[i ][5]);
            }
            //最后面接頭處理
            path.quadTo(wavePoints[wavePoints.length-2][0],wavePoints[wavePoints.length-2][1],
                    wavePoints[wavePoints.length-1][0],wavePoints[wavePoints.length-1][1]
            );
            for (int i = 1; i < wavePoints.length-2  ; i ++) {

                path.cubicTo(wavePoints[i][0]+waveWith, wavePoints[i][1],
                        wavePoints[i][2]+waveWith, wavePoints[i][3],
                        wavePoints[i][4]+waveWith, wavePoints[i ][5]);
            }
            path.lineTo(waveWith + wavePoints[wavePoints.length-1][0], mHeight);
            path.close();

        }else {
            path.moveTo(wavePoints[0][0]-waveWith, mHeight);
            path.lineTo(wavePoints[0][0]-waveWith, wavePoints[0][1]);

            for (int i = 1; i < wavePoints.length-2  ; i ++) {

                path.cubicTo(wavePoints[i][0]-waveWith, wavePoints[i][1],
                        wavePoints[i][2]-waveWith, wavePoints[i][3],
                        wavePoints[i][4]-waveWith, wavePoints[i ][5]);
            }
            //最后面接頭處理
            path.quadTo(wavePoints[wavePoints.length-2][0]-waveWith,wavePoints[wavePoints.length-2][1],
                    wavePoints[wavePoints.length-1][0]-waveWith,wavePoints[wavePoints.length-1][1]
            );
            for (int i = 1; i < wavePoints.length-2  ; i ++) {

                path.cubicTo(wavePoints[i][0], wavePoints[i][1],
                        wavePoints[i][2], wavePoints[i][3],
                        wavePoints[i][4], wavePoints[i ][5]);
            }
            path.lineTo( wavePoints[wavePoints.length-1][0], mHeight);
            path.close();
            }

    }

    public void draw(Canvas canvas, Paint paint) {
        canvas.save();
        canvas.translate(currentMoveX,0);
        canvas.drawPath(path,paint);
        measureMoveTo();
        canvas.restore();

    }
}

View 類

package com.example.a14143.wave;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * @author DrChen
 * @Date 2019/4/16 0016.
 * qq:1414355045
 * 三段漸變波紋
 */
public class WaveView extends View {
    /**
     * 三段漸變的中點
     */
    private final float gradient_height_mid = 0.28f;

    private final float top_wave_top = 0.28f;
    private final float top_wave_bottom = top_wave_top + 0.06f;
    private final float bottom_wave_bottom = top_wave_bottom ;
    private final float bottom_wave_top = top_wave_bottom - 0.05f;


    /**
     * 這個是背景的漸變色和波浪的顏色
     */
    private int topColor, midColor, bottomColor, waveColor;
    /**
     * 控件的寬高
     */
    private int mHeight, mWidth;
    /**
     * 漸變畫筆
     */
    private Paint gradientPaint;
    /**
     * 波浪的畫筆
     */
    private Paint wavePaint;
    /**
     * 繪制波浪式輔助的畫筆(可以去掉)
     */
    private Paint testPaint;
    /**
     * 是否開啟輔助的畫筆
     */
    private boolean isDebug = false;
    /**
     * view 是否被刪除
     */
    private boolean onPause = false;

    /**
     * 整個視圖的背景是三種顏色的漸變门粪,藍到紫的區(qū)域
     */
    private RectF gradient_bottom;
    /**
     * 紫到白的區(qū)域
     */
    private RectF gradient_top;

    /**
     * 波浪繪制區(qū)域
     */
    private RectF bottom_wave_RectF;
    private RectF top_wave_RectF;


    private LinearGradient oneGradient;
    private LinearGradient twoGradient;

    private Wave bottomWave;
    private Path bottomPath = new Path();
    private Wave topWave;
    private Path topPath = new Path();


    public WaveView(Context context) {
        this(context, null);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        topColor = Color.parseColor("#3851DF");
        midColor = Color.parseColor("#937EF7");
        bottomColor = Color.parseColor("#FFFFFF");
        waveColor = Color.parseColor("#80FFFFFF");
        if (gradientPaint == null) {
            gradientPaint = new Paint();
        }
        gradientPaint.setStyle(Paint.Style.FILL);
        if (gradient_bottom == null) gradient_bottom = new RectF();
        if (gradient_top == null) gradient_top = new RectF();
        if (bottom_wave_RectF == null) bottom_wave_RectF = new RectF();
        if (top_wave_RectF == null) top_wave_RectF = new RectF();

        if (wavePaint == null) wavePaint = new Paint();
        wavePaint.setColor(waveColor);
        wavePaint.setStyle(Paint.Style.FILL);
        wavePaint.setAntiAlias(true);


        if (testPaint == null) {
            testPaint = new Paint();
        }
        testPaint.setColor(Color.BLACK);
        testPaint.setStrokeWidth(2);
        testPaint.setStyle(Paint.Style.STROKE);


    }

    public void onResume() {
        onPause = false;
        invalidate();
    }

    public void onPause() {
        onPause = true;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        gradient_bottom.right = mWidth;
        gradient_bottom.bottom = mHeight * gradient_height_mid;
        gradient_top.top = mHeight * gradient_height_mid;
        gradient_top.bottom = mHeight;
        gradient_top.right = mWidth;

        bottom_wave_RectF.top = mHeight * bottom_wave_top;
        bottom_wave_RectF.right = mWidth;
        bottom_wave_RectF.bottom = mHeight * bottom_wave_bottom;
        top_wave_RectF.top = mHeight * top_wave_top;
        top_wave_RectF.right = mWidth;
        top_wave_RectF.bottom = mHeight * top_wave_bottom;


        if (bottomWave == null) {
            //我的數(shù)據(jù)屏幕寬度是750,浪高50, 每個點位后面要加f 不然算出來的數(shù)據(jù)會全錯烹困,因為會丟失小數(shù)點
            bottomWave = new Wave( true, bottom_wave_RectF,0.12f,mHeight,dip2px(5,getContext()),
                    new float[]{0 / 750f, 35 / 50f},
                    new float[]{214 / 750f, 6 / 50f},
                    new float[]{362 / 750f, 28 / 50f},
                    new float[]{582 / 750f, 6 / 50f},
                    new float[]{903 / 750f, 41 / 50f},
                    new float[]{1149 / 750f, 25 / 50f},
                    new float[]{1262/750f,35/50f},
                    new float[]{1390/750f,60/50f},//最后這個點可以適當調(diào)整玄妈,保證平滑
            new float[]{1522/750f,35/50f}
            );

        }

        if(topWave==null){
            //我的數(shù)據(jù)屏幕寬度是750,浪高50, 每個點位后面要加f 不然算出來的數(shù)據(jù)會全錯髓梅,因為會丟失小數(shù)點
            topWave = new Wave(false, top_wave_RectF,0.1f,mHeight,dip2px(2,getContext()),
                    new float[]{0 / 750f, 28 / 78f},
                    new float[]{262 / 750f, 0 / 78f},
                    new float[]{657 / 750f, 37 / 78f},
                    new float[]{1052 / 750f, 0 / 78f},
                    new float[]{1314 / 750f, 28 / 78f},
                    new float[]{1520 / 750f, 60 / 78f},
                    new float[]{1709/750f,28/78f}

            );

        }



    }
    /**
     * dp轉(zhuǎn)px
     * @param dip       dp
     * @param context   上下文
     * @return
     */
    public static int dip2px(float dip, Context context) { float density = context.getResources().getDisplayMetrics().density;
        int px = (int) (dip * density + 0.5f);// 4.9->4, 4.1->4, 四舍五入
        return px;
    }

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);


        //繪制背景三段漸變
        drawBackdrop(canvas);
        if (isDebug) {
            canvas.drawRect(bottom_wave_RectF, testPaint);
        }
//        topWave.testMoveTo(0.4f);
//        bottomWave.testMoveTo(0.6f);



        topWave.draw(canvas,wavePaint);
        bottomWave.draw(canvas,wavePaint);







        if (onPause) {
            return;
        }
      postInvalidateDelayed(10);


    }



    @Override
    protected void onDetachedFromWindow() {
        onPause = true;
        super.onDetachedFromWindow();

    }

    /**
     * 繪制三段漸變背景
     *
     * @param canvas
     */
    private void drawBackdrop(Canvas canvas) {
        if (oneGradient == null)
            oneGradient = new LinearGradient(gradient_bottom.centerX(), gradient_bottom.top, gradient_bottom.centerX(), gradient_bottom.bottom, topColor, midColor, Shader.TileMode.MIRROR);
        if (twoGradient == null)
            twoGradient = new LinearGradient(gradient_top.centerX(), gradient_top.top, gradient_top.centerX(), gradient_top.bottom, midColor, bottomColor, Shader.TileMode.MIRROR);
        gradientPaint.setShader(oneGradient);
        canvas.drawRect(gradient_bottom, gradientPaint);
        gradientPaint.setShader(twoGradient);
        canvas.drawRect(gradient_top, gradientPaint);
    }






}


最后調(diào)用:

package com.example.a14143.wave;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
WaveView waveView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    waveView = findViewById(R.id.waveView);


    }

    @Override
    protected void onResume() {
        super.onResume();
    waveView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
    waveView.onPause();
    }
}

github的鏈接: https://github.com/drchengit/WaveView
我是drchen拟蜻,一個溫潤的男子,版權(quán)所有枯饿,未經(jīng)允許不得抄襲酝锅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奢方,隨后出現(xiàn)的幾起案子屈张,更是在濱河造成了極大的恐慌,老刑警劉巖袱巨,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碳抄,居然都是意外死亡愉老,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門剖效,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫉入,“玉大人焰盗,你說我怎么就攤上這事≈淞郑” “怎么了熬拒?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垫竞。 經(jīng)常有香客問我澎粟,道長啥辨,這世上最難降的妖魔是什么柔逼? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮船万,結(jié)果婚禮上遣鼓,老公的妹妹穿的比我還像新娘啸盏。我一直安慰自己,他們只是感情好骑祟,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布回懦。 她就那樣靜靜地躺著,像睡著了一般次企。 火紅的嫁衣襯著肌膚如雪怯晕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天抒巢,我揣著相機與錄音贫贝,去河邊找鬼。 笑死蛉谜,一個胖子當著我的面吹牛稚晚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播型诚,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼客燕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狰贯?” 一聲冷哼從身側(cè)響起也搓,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涵紊,沒想到半個月后傍妒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡摸柄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年颤练,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驱负。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗦玖,死狀恐怖患雇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宇挫,我是刑警寧澤苛吱,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站器瘪,受9級特大地震影響翠储,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娱局,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一彰亥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衰齐,春花似錦任斋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抹缕,卻和暖如春澈蟆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卓研。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工趴俘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奏赘。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓寥闪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親磨淌。 傳聞我的和親對象是個殘疾皇子疲憋,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354