理解與運用貝塞爾曲線

1.曲線介紹

貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數(shù)學曲線。在計算機圖形學中也是相當重要的參數(shù)曲線。

2.公式介紹

線性公式

給定點P0、P1,線性貝茲曲線只是一條兩點之間的直線。這條線由下式給出:


線性公式.png

當參數(shù)t變化時,其過程如下:


線性.gif

且其等同于線性插值。

二次方公式

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


二次方公式.png

當參數(shù)t變化時,其過程如下:


二次.gif

TrueType字型就運用了以貝茲樣條組成的二次貝茲曲線。

三次方公式

P0、P1、P2、P3四個點在平面或在三維空間中定義了三次方貝茲曲線。曲線起始于P0走向P1,并從P2的方向來到P3。一般不會經(jīng)過P1或P2;這兩個點只是在那里提供方向資訊。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。
曲線的參數(shù)形式為:

三次方公式.png

當參數(shù)t變化時,其過程如下:


三次.gif

一般參數(shù)公式

階貝茲曲線可如下推斷。給定點P0、P1、…、Pn,其貝茲曲線即:
如上公式可如下遞歸表達: 用表示由點P0、P1、…、Pn所決定的貝茲曲線。
用平常話來說,階的貝茲曲線,即雙階貝茲曲線之間的插值。


一般參數(shù)公式.png

3.公式運用

根據(jù)公式自己用線段代替曲線實現(xiàn)系統(tǒng)貝塞爾曲線效果

實現(xiàn)效果:

線.gif

1.首先確定控制點,以及其它兩點
2.確定切分信息,在此我把[0,1]切分成了100份
3.根據(jù)公式計算切分的每個點
4.創(chuàng)建定時器每一定時間繪制特定點數(shù)
5.利用系統(tǒng)函數(shù)創(chuàng)建貝塞爾曲線,與自己繪制的進行對比。

//
//  FormulaView.m
//  Bezier
//
//  Created by qinmin on 2017/1/16.
//  Copyright ? 2017年 qinmin. All rights reserved.
//

#import "FormulaView.h"

@interface FormulaView ()
{
    CGFloat _t;
    CGFloat _deltaT;
    
    CGPoint _p1;
    CGPoint _p2;
    CGPoint _control;
    
    CGPoint *_pointArr;
    CADisplayLink   *_displayLink;
    
    int     _currentIndex;
}
@end

@implementation FormulaView

- (void)dealloc
{
    if (_pointArr) {
        free(_pointArr);
        _pointArr = NULL;
    }
}

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setupPoint];
        [self setupSliceInfo];
        [self createBezierPoint];
        [self setupTimer];
    }
    return self;
}

- (void)setupPoint
{
    _p1 = CGPointMake(10, 100);
    _p2 = CGPointMake(400, 100);
    _control = CGPointMake(100, 800);
    
}

// 切分點信息
- (void)setupSliceInfo
{
    _t = 0.0;
    _deltaT = 0.01;
    _currentIndex = 0;
}

// 創(chuàng)建線段的切分點
- (void)createBezierPoint
{
    int count = 1.0/_deltaT;
    _pointArr = (CGPoint *)malloc(sizeof(CGPoint) * (count+1));

    // t的范圍[0,1]
    for (int i = 0; i < count+1; i++) {
        float t = i * _deltaT;
        
        // 二次方計算公式
        float cx = (1-t)*(1-t)*_p1.x + 2*t*(1-t)*_control.x + t*t*_p2.x;
        float cy = (1-t)*(1-t)*_p1.y + 2*t*(1-t)*_control.y + t*t*_p2.y;
        _pointArr[i] = CGPointMake(cx, cy);
    }
}

- (void)setupTimer
{
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerTick:)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)timerTick:(CADisplayLink *)displayLink
{
    _currentIndex += 1;
    int count = 1.0/_deltaT;
    if (_currentIndex > count+1) {
        _currentIndex = 1;
    }
    [self setNeedsDisplay];
}

// 畫圖
- (void)drawRect:(CGRect)rect
{
    if (_pointArr == NULL) {
        return;
    }
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(ctx, 4);
    
    // 系統(tǒng)貝塞爾曲線
    [[UIColor blackColor] setStroke];
    CGContextMoveToPoint(ctx, _p1.x, _p1.y);
    CGContextAddQuadCurveToPoint(ctx, _control.x, _control.y, _p2.x, _p2.y);
    CGContextDrawPath(ctx, kCGPathStroke);
    
    // 線段代替曲線
    [[UIColor redColor] setStroke];
    CGContextMoveToPoint(ctx, _pointArr[0].x, _pointArr[0].y);
    for (int i = 1; i < _currentIndex; i++) {
        CGContextAddLineToPoint(ctx, _pointArr[i].x, _pointArr[i].y);
    }
    CGContextDrawPath(ctx, kCGPathStroke);
}
@end

這里最重要的是根據(jù)公式,計算每一個點的信息


二次方公式.png

實現(xiàn)類似彈簧效果

實現(xiàn)效果:

物理.gif

1.首先確定除控制點以外的其它兩點,也就是繩子上兩球的球心
2.設置整個場景球的重力,以及繩子的彈力
3.計算貝塞爾曲線的控制點,由于控制點并不是最終和球接觸的那個點,這個點因該是t=0.5時曲線上的點
4.創(chuàng)建定時器每一定時間更新小球位置
5.檢測小球碰撞到繩子的時候,給小球施加彈力,并且讓繩子與小球共同運動。
6.小球向上運動與繩子分離的時候,撤掉彈力的作用。

//
//  MyView.h
//  UIkit
//
//  Created by qinmin on 2017/1/14.
//  Copyright ? 2017年 qinmin. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface Ball : NSObject
@property (nonatomic, assign) float speed;
@property (nonatomic, assign) float accelerate;
@property (nonatomic, assign) CGPoint position;
@property (nonatomic, assign) CGSize size;
@end

@interface Rope : NSObject
@property (nonatomic, assign) float k;
@property (nonatomic, assign) float x;
@property (nonatomic, assign) CGPoint position;
@property (nonatomic, assign) CGPoint control;
@property (nonatomic, assign) CGPoint start;
@property (nonatomic, assign) CGPoint end;
@end

@interface BallView : UIView
- (void)stop;
- (void)start;
@end

//
//  MyView.m
//  UIkit
//
//  Created by qinmin on 2017/1/14.
//  Copyright ? 2017年 qinmin. All rights reserved.
//

#import "BallView.h"

@implementation Ball

@end

@implementation Rope

@end


@interface BallView ()
{
    CADisplayLink   *_displayLink;
    Ball            *_ball;
    Rope            *_rope;
}
@end

@implementation BallView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setupTimer];
        [self setupBall];
        [self setupRope];
    }
    return self;
}

- (void)setupTimer
{
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerTick:)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)setupBall
{
    _ball = [[Ball alloc] init];
    _ball.accelerate = 300; //加速度
    _ball.speed = 0; //速度
    _ball.size = CGSizeMake(30, 30); //球大小
    _ball.position = CGPointMake(195, 100); //球的初始位置
}

- (void)setupRope
{
    _rope = [[Rope alloc] init];
    _rope.position = CGPointMake(200, 420); //繩子中間點的位置
    _rope.start = CGPointMake(150, 420); //繩子起點
    _rope.end = CGPointMake(270, 420); //繩子終點
    _rope.control = CGPointMake(210, 420); //繩子的控制點
    _rope.x = 0; //繩子偏移量
    _rope.k = 20; //勁度系數(shù)
}

- (void)timerTick:(CADisplayLink *)displayLink
{
    //NSLog(@"%f", displayLink.duration);
    CGRect ballRect = CGRectMake(_ball.position.x, _ball.position.y+1, _ball.size.width, _ball.size.height);
    
    BOOL ropeMove = NO;
    //球與繩子碰撞檢測
    if (CGRectContainsPoint(ballRect, _rope.position)) {
        ropeMove = YES;
        // delta x
        // f = kx
        _rope.x = _rope.position.y - _rope.end.y;
    }else {
        //球向上離開繩子
        _rope.x = 0;
    }
    
    _ball.speed += (_ball.accelerate - _rope.k * _rope.x) * displayLink.duration;
    float s = _ball.speed * displayLink.duration;
    _ball.position = CGPointMake(_ball.position.x, _ball.position.y + s);
    
    // 球與繩子碰撞檢測
    if (ropeMove) {
        float x = _ball.position.x + _ball.size.width/2;
        float y = _ball.position.y + _ball.size.height;
        
        // 中間點 公式的t為0.5
        float t = 0.5;
        
        // 根據(jù)公式逆推出控制點
        float cx = (x - (1-t)*(1-t)*_rope.start.x - t*t*_rope.end.x)/(2*t*(1-t));
        float cy = (y - (1-t)*(1-t)*_rope.start.y - t*t*_rope.end.y)/(2*t*(1-t));
        
        _rope.position = CGPointMake(x, y);
        _rope.control = CGPointMake(cx, cy);
        
        // fix 小球未與繩子接觸,小球的位置高于繩子的位置
        if (y <= _rope.end.y) {
            _rope.position = CGPointMake((_rope.end.x+_rope.start.x)/2, _rope.start.y);
            _rope.control = _rope.position;
        }
    }
    
    //fix position
    if (_ball.position.y < 100) {
        _ball.position = CGPointMake(_ball.position.x, 100);
    }
    
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    // 畫兩個固定點
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGMutablePathRef path = CGPathCreateMutable();

    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_rope.start.x-_ball.size.width/2, _rope.start.y-_ball.size.height/2, _ball.size.width,  _ball.size.height)].CGPath);
    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_rope.end.x-_ball.size.width/2, _rope.end.y-_ball.size.height/2, _ball.size.width, _ball.size.height)].CGPath);
    CGRect ballRect = CGRectMake(_ball.position.x, _ball.position.y, _ball.size.width, _ball.size.height);
    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:ballRect].CGPath);

    CGContextAddPath(ctx, path);
    CGContextDrawPath(ctx, kCGPathFillStroke);
    CGPathRelease(path);
    
    // 畫繩子的貝塞爾曲線
    CGContextMoveToPoint(ctx, _rope.start.x, _rope.start.y);
    CGContextAddQuadCurveToPoint(ctx, _rope.control.x, _rope.control.y, _rope.end.x, _rope.end.y);
    
    // 畫最高點標記線
    CGContextMoveToPoint(ctx, _ball.position.x-30, 100);
    CGContextAddLineToPoint(ctx, _ball.position.x +50, 100);
    
    CGContextDrawPath(ctx, kCGPathStroke);
}

- (void)stop
{
    [_displayLink invalidate];
    _displayLink = nil;
}

- (void)start
{
    [self setupTimer];
}

@end

這里最重要的是根據(jù)公式,t=0.5 計算出控制點的位置

二次方公式.png

實現(xiàn)類似小船在波浪行駛的效果

效果:

波浪.gif

1.首先用貝塞爾曲線作為波浪,由于需要實現(xiàn)連續(xù)不斷的效果。所以至少需要兩個曲線,當然可以加入更多波形,在此只弄了簡單的兩個波形。
2.設置波形的水平速度,然后連續(xù)運。當波浪離開右側屏幕上,讓其重新回到左側屏幕。
3.計算t值,主要是根據(jù)當前小球在貝塞爾曲線的比例,比例也就是我們需要的t值。
4.根據(jù)三次方公式計算出波形對應y值,也就是小球的y值。

//
//  ShipView.m
//  Bezier
//
//  Created by qinmin on 2017/1/16.
//  Copyright ? 2017年 qinmin. All rights reserved.
//

#import "ShipView.h"

@implementation Ship

@end

@implementation Wave

@end


@interface ShipView ()
{
    CADisplayLink   *_displayLink;
    Wave            *_wave1;
    Wave            *_wave2;
    Ship            *_ship;
}
@end

@implementation ShipView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setupTimer];
        
        [self setupWave];
        [self setupShip];
    }
    return self;
}

- (void)setupTimer
{
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerTick:)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)setupWave
{
    _wave1 = [[Wave alloc] init];
    _wave1.cp1 = CGPointMake(200, 150);
    _wave1.cp2 = CGPointMake(300, 280);
    _wave1.p1 = CGPointMake(0, 200);
    _wave1.p2 = CGPointMake(420, 200);
    _wave1.speed = 320;
    
    _wave2 = [[Wave alloc] init];
    _wave2.cp1 = CGPointMake(200-420+1, 150);
    _wave2.cp2 = CGPointMake(300-420+1, 250);
    _wave2.p1 = CGPointMake(0-420+1, 200);
    _wave2.p2 = CGPointMake(420-420+1, 200);
    _wave2.speed = _wave1.speed;
}

- (void)setupShip
{
    _ship = [[Ship alloc] init];
    _ship.size = CGSizeMake(30, 30);
    _ship.position = CGPointMake(210, 250);
}

- (void)addDeltaX:(CGFloat)x forWave:(Wave *)wave
{
    // fix 無線循環(huán)
    if (wave.p1.x + x >= 420) {
        x = - 420 * 2 + 8;
    }
    
    CGPoint cp1 = wave.cp1;
    cp1.x += x;
    wave.cp1 = cp1;
    
    CGPoint cp2 = wave.cp2;
    cp2.x += x;
    wave.cp2 = cp2;
    
    CGPoint p1 = wave.p1;
    p1.x += x;
    wave.p1 = p1;
    
    CGPoint p2 = wave.p2;
    p2.x += x;
    wave.p2 = p2;
}

- (void)timerTick:(CADisplayLink *)displayLink
{
    CGFloat delta = displayLink.duration * _wave1.speed;
    [self addDeltaX:delta forWave:_wave1];
    [self addDeltaX:delta forWave:_wave2];
    
    Wave *currentWave;
    if (_wave1.p1.x <= _ship.position.x && _ship.position.x <= _wave1.p2.x) {
        currentWave = _wave1;
    }else {
        currentWave = _wave2;
    }
    
    // 計算t值
    float t = (_ship.position.x - currentWave.p1.x)/(currentWave.p2.x - currentWave.p1.x);
    
    // 由t值,計算出y值
    float y = currentWave.p1.y*pow(1-t, 3) + 3*currentWave.cp1.y*t*pow(1-t, 2) +3*currentWave.cp2.y*t*t*(1-t)+currentWave.p2.y*pow(t, 3);
    CGPoint position = _ship.position;
    position.y = y-_ship.size.height-2;
    _ship.position = position;
    
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    CGMutablePathRef path = CGPathCreateMutable();
    
    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_ship.position.x, _ship.position.y, _ship.size.width,  _ship.size.height)].CGPath);
    
    CGContextAddPath(ctx, path);
    CGContextDrawPath(ctx, kCGPathFillStroke);
    CGPathRelease(path);
    
    
    CGContextMoveToPoint(ctx, _wave1.p1.x, _wave1.p1.y);
    CGContextAddCurveToPoint(ctx, _wave1.cp1.x, _wave1.cp1.y, _wave1.cp2.x, _wave1.cp2.y, _wave1.p2.x, _wave1.p2.y);
    CGContextDrawPath(ctx, kCGPathStroke);
    
    CGContextMoveToPoint(ctx, _wave2.p1.x, _wave2.p1.y);
    CGContextAddCurveToPoint(ctx, _wave2.cp1.x, _wave2.cp1.y, _wave2.cp2.x, _wave2.cp2.y, _wave2.p2.x, _wave2.p2.y);
    CGContextDrawPath(ctx, kCGPathStroke);
}

@end

實現(xiàn)類似橡皮泥效果

效果:


橡皮泥效果.gif

這里我就不過多解釋代碼,代碼還值得優(yōu)化

//
//  DotView.m
//  Bezier
//
//  Created by qinmin on 2017/1/15.
//  Copyright ? 2017年 qinmin. All rights reserved.
//

#import "DotView.h"

@implementation Dot

@end

@interface DotView ()
{
    Dot     *_startDot;
    Dot     *_endDot;
    
    CGPoint _controlPoint;
    CGFloat _lastDistance;
}
@end

@implementation DotView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setupDots];
        [self setupGesture];
        [self setupControlPoint];
    }
    return self;
}

- (void)setupDots
{
    _startDot = [[Dot alloc] init];
    _startDot.position = CGPointMake(100, 200);
    _startDot.size = CGSizeMake(50, 50);
    
    _endDot = [[Dot alloc] init];
    _endDot.position = CGPointMake(100, 290);
    _endDot.size = CGSizeMake(50, 50);
}

- (void)setupGesture
{
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    [self addGestureRecognizer:panGesture];
}

- (void)setupControlPoint
{
    CGFloat cpx = (_startDot.position.x + _endDot.position.x)/2;
    CGFloat cpy = 0.5;
    _controlPoint = CGPointMake(cpx, cpy);
}

- (void)handleGesture:(UIPanGestureRecognizer *)gesture
{
    CGPoint offset = [gesture locationInView:self];
    //CGPoint velocity = [gesture translationInView:self];
    //NSLog(@"%@", NSStringFromCGPoint(velocity));
    
    CGPoint endPos = _endDot.position;
    endPos.x = offset.x;
    endPos.y = offset.y;
    _endDot.position = endPos;
    
    float distance = sqrtf(pow(_startDot.position.x - _endDot.position.x, 2) + pow(_startDot.position.y - _endDot.position.y, 2));
    
    CGSize size;
    if (distance - _lastDistance < 0.00000) {
        size = _startDot.size;
        size.width = size.width + 0.001 * distance;
        size.height = size.width;
        NSLog(@"%@", NSStringFromCGSize(size));
        if (size.width > 50 ) {
            size.width = size.height = 30;
        }
    }else {
        size = _startDot.size;
        size.width = size.width - 0.001 * distance;
        size.height = size.width;
        NSLog(@"%@", NSStringFromCGSize(size));
        if (size.width < 15 ) {
            size.width = size.height = 15;
        }
    }
    _startDot.size = _endDot.size = size;
    _lastDistance = distance;
    
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGMutablePathRef path = CGPathCreateMutable();
    
    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_startDot.position.x-_startDot.size.width/2, _startDot.position.y-_startDot.size.height/2, _startDot.size.width,  _startDot.size.height)].CGPath);
    
    CGPathAddPath(path, NULL, [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_endDot.position.x-_endDot.size.width/2, _endDot.position.y-_endDot.size.height/2, _endDot.size.width,  _endDot.size.height)].CGPath);
    
    CGContextAddPath(ctx, path);
    CGContextDrawPath(ctx, kCGPathFillStroke);
    CGPathRelease(path);
    
    // 求動點相對于x軸的偏移角
    // a = (1,0), b = (end.x-start.x, end.y-start.y), cost=a*b/(|a||b|)
    float cost = (_endDot.position.x-_startDot.position.x)/sqrtf(pow(_endDot.position.x-_startDot.position.x, 2) + pow(_endDot.position.y-_startDot.position.y, 2));
    float t = acosf(cost);
    float sint = sin(t);
    
    // 修正動點在定點上方時候的角度問題
    int i = 1;
    if (_endDot.position.y < _startDot.position.y) {
        cost = cos(-t);
        sint = sin(-t);
        i = -1;
    }
    
    float deltax = _startDot.size.width/2 * sint;
    float deltay = _startDot.size.height/2 * cost;
    
    float cpx = (_startDot.position.x+_endDot.position.x)/2;
    float cpy = (_startDot.position.y+_endDot.position.y)/2 - _controlPoint.y*cost;
    float cpy1 = (_startDot.position.y+_endDot.position.y)/2 + _controlPoint.y*cost;
    
    // 畫四邊形
    CGContextSetLineWidth(ctx, 0);
    CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
    CGContextMoveToPoint(ctx, _startDot.position.x+deltax-1*i, _startDot.position.y-deltay);
    CGContextAddLineToPoint(ctx, _startDot.position.x-deltax+1*i, _startDot.position.y+deltay);
    CGContextAddLineToPoint(ctx, _endDot.position.x+deltax-1*i, _endDot.position.y-deltay);
    CGContextMoveToPoint(ctx, _endDot.position.x+deltax-1*i, _endDot.position.y-deltay);
    CGContextAddLineToPoint(ctx, _endDot.position.x-deltax+1*i, _endDot.position.y+deltay);
    CGContextAddLineToPoint(ctx, _startDot.position.x-deltax+1*i, _startDot.position.y+deltay);
    CGContextDrawPath(ctx, kCGPathEOFillStroke);
    
    // 畫貝塞爾曲線
    [[UIColor whiteColor] setFill];
    CGContextSetLineWidth(ctx, 0);
    CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
    CGContextMoveToPoint(ctx, _startDot.position.x+deltax+1*i, _startDot.position.y-deltay);
    CGContextAddQuadCurveToPoint(ctx, cpx, cpy, _endDot.position.x+deltax+1*i, _endDot.position.y-deltay);
    CGContextMoveToPoint(ctx, _startDot.position.x-deltax-1*i, _startDot.position.y+deltay);
    CGContextAddQuadCurveToPoint(ctx, cpx, cpy1, _endDot.position.x-deltax-1*i, _endDot.position.y+deltay);
    CGContextDrawPath(ctx, kCGPathEOFillStroke);
  
}
@end

最后

這篇文章講述了貝塞爾曲線的公式定義,以及用它來實現(xiàn)一些特殊的效果。如果能理解好貝塞爾曲線的原理,對動畫效果開發(fā)是很有幫助的。

更深入的理解方程推到過程請參照:
1.如何得到貝塞爾曲線的曲線長度和 t 的近似關系
2.貝塞爾曲線掃盲

目前代碼已經(jīng)放到github上面,傳送門:Bezier

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容