美文网首页
QQ粘性效果

QQ粘性效果

作者: 翻这个墙 | 来源:发表于2017-11-23 11:00 被阅读15次
  • 效果:拖动信息提示数目按钮,感觉像是在拉伸按钮,当拖动到一定范围,按钮(小圆被抽出),松开手小圆会爆炸。如果抽出小圆后,将小圆移回之前的位置,小圆会被恢复
  • 核心计算公式:(见图中A、B、C、D、O、P点公式)
  • 步骤:
    1. 生成大圆view并设置属性(示例是在storyboard中创建,所以下面代码没有这步)
    • 根据手势拖动设置按钮移动
    • 在按钮原来的位置生成一个与按钮一模一样的小圆view
    • 封装两圆中心距离计算方法(勾股定理)
    • 根据两圆中心距离及一定比例,设置小圆大小形变
    • 利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return)
    • 当两圆中心距离达到一定距离后,实现抽走效果(小圆同时隐藏)
    • 如果松手位置在爆炸范围圈外,松开手通过核心动画或者imageView播放爆炸帧动画,并将大圆从父控件中移除
    • 如果松手位置在爆炸范围圈内,通过弹簧效果使大圆重新回到原点,并显示小圆
    • VC.m中,要取消autoMask转化为自动布局,否则位移会有问题
      [图片上传失败...(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) {
        // 可以根据路径生成图层
        CAShapeLayer *layer = [CAShapeLayer layer];

        layer.fillColor = [UIColor redColor].CGColor;

        [self.superview.layer insertSublayer:layer atIndex:0];//为什么要这样插入?

        _shapeLayer = layer;
    }

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

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

// 初始化操作
- (void)setUp
{
##生成大圆view并设置属性(示例是在storyboard中创建,所以下面代码没有这步)
    // 默认背景颜色:红色
    self.backgroundColor = [UIColor redColor];

    // 默认圆角半径
    self.layer.cornerRadius = self.bounds.size.width * 0.5;

    // 设置字体
    self.titleLabel.font = [UIFont systemFontOfSize:12];

    // 设置文字的颜色
    [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
##根据手势拖动设置按钮移动
    // 添加pan手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

    [self addGestureRecognizer:pan];
##在按钮原来的位置生成一个与按钮一模一样的小圆view
    // 添加一个小圆
    UIView *smallCircleView = [[UIView alloc] init];

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

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

    // 设置圆角半径
    smallCircleView.layer.cornerRadius = self.layer.cornerRadius;

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

    _smallCircleView = smallCircleView;
}
##根据手势拖动设置按钮移动
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 获取手指的偏移量
    CGPoint transP = [pan translationInView:self];

    // 修改形变,移动控件的位置
    // 修改transform并不会修改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;

    // 复位
    [pan setTranslation:CGPointZero inView:self];
##封装两圆中心距离计算方法(勾股定理)
    // 计算下两个圆心的距离
    CGFloat distance = [self distanceWithSmallView:_smallCircleView bigView:self];


##根据两圆中心距离及一定比例,设置小圆大小形变
    // 修改小圆的半径,根据圆心距离产生一个比例
    CGFloat smallR = self.bounds.size.width * 0.5 - distance / 10.0;

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

    // 设置圆角半径
    _smallCircleView.layer.cornerRadius = smallR;

利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return)
    // 计算不规则的矩形路径
    UIBezierPath *path = [self pathWithSmallView:_smallCircleView bigView:self];

    if (self.smallCircleView.hidden == NO) { // 当小圆没有显示的时候,就不需要描述不规则的矩形

        // 设置形状图层的路径
        self.shapeLayer.path = path.CGPath;
    }

##当两圆中心距离达到一定距离后,实现抽走效果(小圆同时隐藏)
    // 当圆心距离大于60的时候,吸附效果
    if (distance > 60) {
        // 隐藏小圆
        _smallCircleView.hidden = YES;

        // 不规则的矩形移除父控件
//        self.shapeLayer.hidden = YES;
        [self.shapeLayer removeFromSuperlayer];
    }

##如果松手位置在爆炸范围圈外,松开手通过核心动画或者imageView播放爆炸帧动画,并将大圆从父控件中移除

    if (pan.state == UIGestureRecognizerStateEnded) { // 手指抬起的时候,需要做判断
        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];

            // 设置动画数组
            imageView.animationImages = images;

            imageView.animationDuration = 1;

            [self addSubview:imageView];

            // 主动播放
            [imageView startAnimating];

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self removeFromSuperview];
            });
##如果松手位置在爆炸范围圈内,通过弹簧效果使大圆重新回到原点,并显示小圆
        }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;
            }];
        }
    }
}

利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return))
// 计算不规则的矩形路径
- (UIBezierPath *)pathWithSmallView:(UIView *)smallView bigView:(UIView *)bigView
{
    // 计算圆心
    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,不计算矩形路径
    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;
}
##封装两圆中心距离计算方法(勾股定理)
// 计算两个控件的圆心距离
- (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转化为自动布局,否则位移会有问题

- (void)viewDidLoad {
    [super viewDidLoad];

    // 取消autoMask转化为自动布局
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
}

相关文章

  • QQ粘性效果

    效果:拖动信息提示数目按钮,感觉像是在拉伸按钮,当拖动到一定范围,按钮(小圆被抽出),松开手小圆会爆炸。如果抽出小...

  • QQ粘性效果

  • QQ粘性效果

    QQ粘性效果 实现思路: 1.自定义大圆控件(UIButton)可以显示背景图片,和文字 2.让大圆控件随着手指移...

  • QQ粘性效果

    动画分析 当前控件既可以显示图片,有可以显示文字,那么我们就可以通过按钮来最为当前的控件. 当拖动控件,当前控件尺...

  • QQ粘性效果的实现

    制作步骤 1.自定义按钮控件 设置背景颜色,设置layer的cornerRadius属性,添加手势,重写setHi...

  • QQ粘性布局

    在一个storyBoard里面拖一个UIButton进去,然后新建一个自定义UIBUtton类

  • QQ粘性布局

    按钮button自定义button设置圆角半径cornerRadius取消高亮状态重写setHighlighted...

  • QQ粘性布局

    整体思路: 手指移动,按钮跟着移动.按钮跟着手指移动.移动时底部有一个圆, 根据上面的大圆按钮拖动的距离,小圆的半...

  • QQ粘性布局

    #import @interfaceBageValueButton :UIButton @end #import ...

  • 【原创】也仿QQ未读消息气泡拖拽黏连效果

    一、先看效果 二、分析 昨天写了贝塞尔的动画效果,非常好玩。今天突然想到qq的消息气泡,点击拖拽有粘性,也能用贝塞...

网友评论

      本文标题:QQ粘性效果

      本文链接:https://www.haomeiwen.com/subject/ketzvxtx.html