QQ粘性效果

  • 效果:拖動(dòng)信息提示數(shù)目按鈕,感覺像是在拉伸按鈕,當(dāng)拖動(dòng)到一定范圍,按鈕(小圓被抽出),松開手小圓會(huì)爆炸。如果抽出小圓后,將小圓移回之前的位置,小圓會(huì)被恢復(fù)
  • 核心計(jì)算公式:(見圖中A、B、C、D、O、P點(diǎn)公式)
  • 步驟:
    1. 生成大圓view并設(shè)置屬性(示例是在storyboard中創(chuàng)建,所以下面代碼沒有這步)
    • 根據(jù)手勢(shì)拖動(dòng)設(shè)置按鈕移動(dòng)
    • 在按鈕原來的位置生成一個(gè)與按鈕一模一樣的小圓view
    • 封裝兩圓中心距離計(jì)算方法(勾股定理)
    • 根據(jù)兩圓中心距離及一定比例,設(shè)置小圓大小形變
    • 利用核心計(jì)算公式,計(jì)算并繪制直線和曲線路徑,通過形狀圖層生成不規(guī)則矩形(計(jì)算方法封裝,需注意兩圓中心距離為0,直接return)
    • 當(dāng)兩圓中心距離達(dá)到一定距離后,實(shí)現(xiàn)抽走效果(小圓同時(shí)隱藏)
    • 如果松手位置在爆炸范圍圈外,松開手通過核心動(dòng)畫或者imageView播放爆炸幀動(dòng)畫,并將大圓從父控件中移除
    • 如果松手位置在爆炸范圍圈內(nèi),通過彈簧效果使大圓重新回到原點(diǎn),并顯示小圓
    • VC.m中,要取消autoMask轉(zhuǎn)化為自動(dòng)布局,否則位移會(huì)有問題
      [圖片上傳失敗...(image-220148-1511405997537)]
##在BageView.m中
@interface BageView ()

//形狀圖層
@property (nonatomic, weak)  CAShapeLayer *shapeLayer;
//小圓控件
@property (nonatomic, weak) UIView *smallCircleView;

@end

@implementation BageView
//懶加載形狀圖層
- (CAShapeLayer *)shapeLayer
{
    if (_shapeLayer == nil) {
        // 可以根據(jù)路徑生成圖層
        CAShapeLayer *layer = [CAShapeLayer layer];

        layer.fillColor = [UIColor redColor].CGColor;

        [self.superview.layer insertSublayer:layer atIndex:0];//為什么要這樣插入?

        _shapeLayer = layer;
    }

    return _shapeLayer;
}
//對(duì)象初始化
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {

        [self setUp];
    }
    return self;
}
//對(duì)象初始化
- (void)awakeFromNib
{
    // 初始化
    [self setUp];
}

// 初始化操作
- (void)setUp
{
##生成大圓view并設(shè)置屬性(示例是在storyboard中創(chuàng)建,所以下面代碼沒有這步)
    // 默認(rèn)背景顏色:紅色
    self.backgroundColor = [UIColor redColor];

    // 默認(rèn)圓角半徑
    self.layer.cornerRadius = self.bounds.size.width * 0.5;

    // 設(shè)置字體
    self.titleLabel.font = [UIFont systemFontOfSize:12];

    // 設(shè)置文字的顏色
    [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
##根據(jù)手勢(shì)拖動(dòng)設(shè)置按鈕移動(dòng)
    // 添加pan手勢(shì)
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

    [self addGestureRecognizer:pan];
##在按鈕原來的位置生成一個(gè)與按鈕一模一樣的小圓view
    // 添加一個(gè)小圓
    UIView *smallCircleView = [[UIView alloc] init];

    // 尺寸
    smallCircleView.frame = self.frame;

    // 顏色
    smallCircleView.backgroundColor = self.backgroundColor;

    // 設(shè)置圓角半徑
    smallCircleView.layer.cornerRadius = self.layer.cornerRadius;

    // 添加到父控件
    [self.superview insertSubview:smallCircleView belowSubview:self];

    _smallCircleView = smallCircleView;
}
##根據(jù)手勢(shì)拖動(dòng)設(shè)置按鈕移動(dòng)
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 獲取手指的偏移量
    CGPoint transP = [pan translationInView:self];

    // 修改形變,移動(dòng)控件的位置
    // 修改transform并不會(huì)修改center
//    self.transform = CGAffineTransformTranslate(self.transform, transP.x, transP.y);
    CGPoint center = self.center;
    center.x += transP.x;
    center.y += transP.y;
    self.center = center;

    // 復(fù)位
    [pan setTranslation:CGPointZero inView:self];
##封裝兩圓中心距離計(jì)算方法(勾股定理)
    // 計(jì)算下兩個(gè)圓心的距離
    CGFloat distance = [self distanceWithSmallView:_smallCircleView bigView:self];


##根據(jù)兩圓中心距離及一定比例,設(shè)置小圓大小形變
    // 修改小圓的半徑,根據(jù)圓心距離產(chǎn)生一個(gè)比例
    CGFloat smallR = self.bounds.size.width * 0.5 - distance / 10.0;

    _smallCircleView.bounds = CGRectMake(0, 0, smallR * 2 , smallR * 2);

    // 設(shè)置圓角半徑
    _smallCircleView.layer.cornerRadius = smallR;

利用核心計(jì)算公式,計(jì)算并繪制直線和曲線路徑,通過形狀圖層生成不規(guī)則矩形(計(jì)算方法封裝,需注意兩圓中心距離為0,直接return)
    // 計(jì)算不規(guī)則的矩形路徑
    UIBezierPath *path = [self pathWithSmallView:_smallCircleView bigView:self];

    if (self.smallCircleView.hidden == NO) { // 當(dāng)小圓沒有顯示的時(shí)候,就不需要描述不規(guī)則的矩形

        // 設(shè)置形狀圖層的路徑
        self.shapeLayer.path = path.CGPath;
    }

##當(dāng)兩圓中心距離達(dá)到一定距離后,實(shí)現(xiàn)抽走效果(小圓同時(shí)隱藏)
    // 當(dāng)圓心距離大于60的時(shí)候,吸附效果
    if (distance > 60) {
        // 隱藏小圓
        _smallCircleView.hidden = YES;

        // 不規(guī)則的矩形移除父控件
//        self.shapeLayer.hidden = YES;
        [self.shapeLayer removeFromSuperlayer];
    }

##如果松手位置在爆炸范圍圈外,松開手通過核心動(dòng)畫或者imageView播放爆炸幀動(dòng)畫,并將大圓從父控件中移除

    if (pan.state == UIGestureRecognizerStateEnded) { // 手指抬起的時(shí)候,需要做判斷
        if (distance > 60) {

            NSMutableArray *images = [NSMutableArray array];
            // 加載gif圖片
            for (int i = 1; i <= 8; i++) {

                NSString *imageName = [NSString stringWithFormat:@"%d",i];

                UIImage *image = [UIImage imageNamed:imageName];
                [images addObject:image];
            }

            // 播放gif圖片

            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];

            // 設(shè)置動(dòng)畫數(shù)組
            imageView.animationImages = images;

            imageView.animationDuration = 1;

            [self addSubview:imageView];

            // 主動(dòng)播放
            [imageView startAnimating];

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self removeFromSuperview];
            });
##如果松手位置在爆炸范圍圈內(nèi),通過彈簧效果使大圓重新回到原點(diǎn),并顯示小圓
        }else{ // 還原


            // 位置
            [UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                self.center = self.smallCircleView.center;

            } completion:^(BOOL finished) {

                // 小圓顯示
                self.smallCircleView.hidden = NO;
            }];
        }
    }
}

利用核心計(jì)算公式,計(jì)算并繪制直線和曲線路徑,通過形狀圖層生成不規(guī)則矩形(計(jì)算方法封裝,需注意兩圓中心距離為0,直接return))
// 計(jì)算不規(guī)則的矩形路徑
- (UIBezierPath *)pathWithSmallView:(UIView *)smallView bigView:(UIView *)bigView
{
    // 計(jì)算圓心
    CGFloat d = [self distanceWithSmallView:smallView bigView:bigView];

    CGFloat y1 = smallView.center.y;
    CGFloat x1 = smallView.center.x;
    CGFloat r1 = smallView.layer.cornerRadius;

    CGFloat y2 = bigView.center.y;
    CGFloat x2 = bigView.center.x;
    CGFloat r2 = bigView.layer.cornerRadius;

    // 如果間距為0,不計(jì)算矩形路徑
    if (d == 0) return nil;

    // cosθ
    CGFloat cosθ = (y2 - y1) / d;
    // sinθ
    CGFloat sinθ = (x2 - x1) / d;

    // A:
    CGPoint pointA = CGPointMake(x1 - r1 * cosθ, y1 + r1 * sinθ);
    // B:
    CGPoint pointB = CGPointMake(x1 + r1 * cosθ, y1 - r1 * sinθ);

    // C:
    CGPoint pointC = CGPointMake(x2 + r2 * cosθ, y2 - r2 * sinθ);

    // D:
    CGPoint pointD = CGPointMake(x2 - r2 * cosθ, y2 + r2 * sinθ);

    // O:
    CGPoint pointO = CGPointMake(pointA.x + d * 0.5 * sinθ, pointA.y + d * 0.5 * cosθ);

    // P:
    CGPoint pointP = CGPointMake(pointB.x + d * 0.5 * sinθ, pointB.y + d * 0.5 * cosθ);

    // 描述路徑
    UIBezierPath *path = [UIBezierPath bezierPath];
    //  nan == null

    [path moveToPoint:pointA];

    // AB
    [path addLineToPoint:pointB];

    // BC
    // 繪制曲線
    [path addQuadCurveToPoint:pointC controlPoint:pointP];

    // CD
    [path addLineToPoint:pointD];

    // DA
    [path addQuadCurveToPoint:pointA controlPoint:pointO];

    return path;
}
##封裝兩圓中心距離計(jì)算方法(勾股定理)
// 計(jì)算兩個(gè)控件的圓心距離
- (CGFloat)distanceWithSmallView:(UIView *)smallView bigView:(UIView *)bigView
{
    CGFloat offsetX = bigView.center.x - smallView.center.x;
    CGFloat offsetY = bigView.center.y - smallView.center.y;

    return sqrt(offsetX * offsetX + offsetY * offsetY);
}

- (void)setHighlighted:(BOOL)highlighted{}

@end


##VC.m中,要取消autoMask轉(zhuǎn)化為自動(dòng)布局,否則位移會(huì)有問題

- (void)viewDidLoad {
    [super viewDidLoad];

    // 取消autoMask轉(zhuǎn)化為自動(dòng)布局
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • QQ粘性效果 實(shí)現(xiàn)思路: 1.自定義大圓控件(UIButton)可以顯示背景圖片,和文字 2.讓大圓控件隨著手指移...
    SoManyDumb閱讀 278評(píng)論 0 0
  • 制作步驟 1.自定義按鈕控件 設(shè)置背景顏色,設(shè)置layer的cornerRadius屬性,添加手勢(shì),重寫setHi...
    沖破繭縛閱讀 1,404評(píng)論 1 7
  • 動(dòng)畫分析 當(dāng)前控件既可以顯示圖片,有可以顯示文字,那么我們就可以通過按鈕來最為當(dāng)前的控件. 當(dāng)拖動(dòng)控件,當(dāng)前控件尺...
    亡靈詛咒閱讀 552評(píng)論 0 2
  • 整體思路: 手指移動(dòng),按鈕跟著移動(dòng).按鈕跟著手指移動(dòng).移動(dòng)時(shí)底部有一個(gè)圓, 根據(jù)上面的大圓按鈕拖動(dòng)的距離,小圓的半...
    翻滾的企鵝閱讀 754評(píng)論 0 0
  • 圖片折疊效果 1.如何制作圖片折疊效果? `把一張圖片分成兩部分顯示,上面一部分,下面一部分,折疊上面部分的內(nèi)容。...
    Hevin_Chen閱讀 568評(píng)論 0 1

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