iOS仿抖音短視頻
iOS仿抖音—左右滑動(dòng)切換效果
iOS仿抖音—上下滑動(dòng)播放視頻
iOS仿抖音—評(píng)論視圖滑動(dòng)消失
iOS仿抖音—加載點(diǎn)贊動(dòng)畫(huà)效果
iOS仿抖音—播放視圖滑動(dòng)隱藏
前言
前段時(shí)間比較忙淹冰,最近終于有時(shí)間就繼續(xù)對(duì)仿抖音的demo進(jìn)行更新埃元,本次更新的主要是抖音上的幾種動(dòng)畫(huà)侯养,下面先來(lái)看下效果圖:
說(shuō)明
經(jīng)過(guò)觀(guān)察發(fā)現(xiàn)抖音主要要以下幾種動(dòng)畫(huà)效果:
1、數(shù)據(jù)加載動(dòng)畫(huà)(兩個(gè)小球來(lái)回切換)
2、視頻加載動(dòng)畫(huà)(直線(xiàn)向兩邊擴(kuò)散)
3命咐、紅心點(diǎn)贊動(dòng)畫(huà)(紅心由小變大并向四周擴(kuò)散)
4壹堰、雙擊點(diǎn)贊動(dòng)畫(huà)(多個(gè)紅心由小變大并逐漸消失)
于是在經(jīng)過(guò)各種資料查找及自我實(shí)踐中完成了這四種動(dòng)畫(huà),下面就這幾種動(dòng)畫(huà)做一下簡(jiǎn)單說(shuō)明
1爬骤、數(shù)據(jù)加載動(dòng)畫(huà)
這個(gè)動(dòng)畫(huà)大致觀(guān)察發(fā)現(xiàn)是一個(gè)紅球一個(gè)綠球左右來(lái)回切換實(shí)現(xiàn)的充石,但仔細(xì)觀(guān)察你會(huì)發(fā)現(xiàn),在左右切換的過(guò)程中有個(gè)黑色小球在不斷變大縮小霞玄,跟隨最上面的球運(yùn)動(dòng)骤铃。
因此我們需要添加三個(gè)小球拉岁,綠球、紅球惰爬、黑球喊暖,默認(rèn)綠球在左紅球在右,黑球在綠球上
self.containerView = [[UIView alloc] init];
self.containerView.center = self.center;
self.containerView.bounds = CGRectMake(0, 0, 2.1f * kBallWidth, 2.0f * kBallWidth);
[self addSubview:self.containerView];
// 綠球
self.greenBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
self.greenBall.center = CGPointMake(kBallWidth * 0.5f, self.containerView.bounds.size.height * 0.5f);
self.greenBall.layer.cornerRadius = kBallWidth * 0.5f;
self.greenBall.layer.masksToBounds = YES;
self.greenBall.backgroundColor = GKColorRGB(35, 246, 235);
[self.containerView addSubview:self.greenBall];
// 紅球
self.redBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
self.redBall.center = CGPointMake(self.containerView.bounds.size.width - kBallWidth * 0.5f, self.containerView.bounds.size.height * 0.5f);
self.redBall.layer.cornerRadius = kBallWidth * 0.5f;
self.redBall.layer.masksToBounds = YES;
self.redBall.backgroundColor = GKColorRGB(255, 46, 86);
[self.containerView addSubview:self.redBall];
// 黑球
// 第一次動(dòng)畫(huà)是正向撕瞧,綠球在上陵叽,紅球在下,陰影顯示在綠球上
self.blackBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
self.blackBall.backgroundColor = GKColorRGB(12, 11, 17);
self.blackBall.layer.cornerRadius = kBallWidth * 0.5f;
self.blackBall.layer.masksToBounds = YES;
[self.greenBall addSubview:self.blackBall];
開(kāi)始動(dòng)畫(huà)丛版,綠球向右放大運(yùn)動(dòng)巩掺,紅球向左縮小運(yùn)動(dòng),綠球到最右邊后页畦,紅球向右放大運(yùn)動(dòng)胖替,綠球向左縮小運(yùn)動(dòng),完成一次循環(huán)豫缨,黑球是綠球和紅球的重合部分刊殉,主要代碼如下:
- (void)updateBallAnimations {
if (self.moveDirection == GKBallMoveDirectionPositive) { // 正向
CGPoint center = self.greenBall.center;
center.x += kBallSpeed;
self.greenBall.center = center;
center = self.redBall.center;
center.x -= kBallSpeed;
self.redBall.center = center;
// 縮放動(dòng)畫(huà),綠球放大州胳,紅球縮小
self.greenBall.transform = [self ballLargerTransformOfCenterX:center.x];
self.redBall.transform = [self ballSmallerTransformOfCenterX:center.x];
// 更新黑球位置
CGRect blackBallFrame = [self.redBall convertRect:self.redBall.bounds toCoordinateSpace:self.greenBall];
self.blackBall.frame = blackBallFrame;
self.blackBall.layer.cornerRadius = self.blackBall.bounds.size.width * 0.5f;
// 更新方向 改變?nèi)齻€(gè)球的相對(duì)位置
if (CGRectGetMaxX(self.greenBall.frame) >= self.containerView.bounds.size.width || CGRectGetMinX(self.redBall.frame) <= 0) {
// 切換為反向
self.moveDirection = GKBallMoveDirectionNegative;
// 反向運(yùn)動(dòng)時(shí)记焊,紅球在上,綠球在下
[self.containerView bringSubviewToFront:self.redBall];
// 黑球放在紅球上面
[self.redBall addSubview:self.blackBall];
// 重置動(dòng)畫(huà)
[self resetAnimation];
}
}else if (self.moveDirection == GKBallMoveDirectionNegative) { // 反向
// 更新綠球位置
CGPoint center = self.greenBall.center;
center.x -= kBallSpeed;
self.greenBall.center = center;
// 更新紅球位置
center = self.redBall.center;
center.x += kBallSpeed;
self.redBall.center = center;
// 縮放動(dòng)畫(huà) 紅球放大 綠球縮小
self.redBall.transform = [self ballLargerTransformOfCenterX:center.x];
self.greenBall.transform = [self ballSmallerTransformOfCenterX:center.x];
// 更新黑球位置
CGRect blackBallFrame = [self.greenBall convertRect:self.greenBall.bounds toCoordinateSpace:self.redBall];
self.blackBall.frame = blackBallFrame;
self.blackBall.layer.cornerRadius = self.blackBall.bounds.size.width * 0.5f;
// 更新方向 改變?nèi)齻€(gè)球的相對(duì)位置
if (CGRectGetMinX(self.greenBall.frame) <= 0 || CGRectGetMaxX(self.redBall.frame) >= self.containerView.bounds.size.width) {
// 切換為正向
self.moveDirection = GKBallMoveDirectionPositive;
// 正向運(yùn)動(dòng) 綠球在上 紅球在下
[self.containerView bringSubviewToFront:self.greenBall];
// 黑球放在綠球上面
[self.greenBall addSubview:self.blackBall];
// 重置動(dòng)畫(huà)
[self resetAnimation];
}
}
}
具體代碼可在demo中的GKBallLoadingView中查看栓撞。
2遍膜、視頻加載動(dòng)畫(huà)
這個(gè)動(dòng)畫(huà)比較簡(jiǎn)單,首先以x軸為中心進(jìn)行縮放動(dòng)畫(huà)瓤湘,然后再修改的透明度即可瓢颅,主要代碼如下:
// 創(chuàng)建動(dòng)畫(huà)組
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.duration = GKLineLoadingDuration;
animationGroup.beginTime = CACurrentMediaTime();
animationGroup.repeatCount = MAXFLOAT;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// x軸縮放動(dòng)畫(huà)(transform.scale是以view的中心點(diǎn)為中心開(kāi)始縮放的)
CABasicAnimation *scaleAnimation = [CABasicAnimation animation];
scaleAnimation.keyPath = @"transform.scale.x";
scaleAnimation.fromValue = @(1.0f);
scaleAnimation.toValue = @(1.0f * self.superview.frame.size.width);
// 透明度漸變動(dòng)畫(huà)
CABasicAnimation *alphaAnimation = [CABasicAnimation animation];
alphaAnimation.keyPath = @"opacity";
alphaAnimation.fromValue = @(1.0f);
alphaAnimation.toValue = @(0.5f);
animationGroup.animations = @[scaleAnimation, alphaAnimation];
// 添加動(dòng)畫(huà)
[self.layer addAnimation:animationGroup forKey:nil];
具體代碼可在demo中的GKLineLoadingView中查看。
3弛说、紅心點(diǎn)贊動(dòng)畫(huà)
這個(gè)動(dòng)畫(huà)的主要是通過(guò)CAShapeLayer和貝賽爾曲線(xiàn)繪制三角形挽懦,循環(huán)創(chuàng)建6次在6個(gè)方向繪制三角形,并加入動(dòng)畫(huà)木人,然后進(jìn)行紅心的縮放動(dòng)畫(huà)信柿,主要代碼如下:
if (isLike) {
CGFloat length = 30;
CGFloat duration = 0.5f;
for (NSInteger i = 0; i < 6; i++) {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.position = self.likeBeforeImgView.center;
layer.fillColor = GKColorRGB(232, 50, 85).CGColor;
UIBezierPath *startPath = [UIBezierPath bezierPath];
[startPath moveToPoint:CGPointMake(-2, -length)];
[startPath addLineToPoint:CGPointMake(2, -length)];
[startPath addLineToPoint:CGPointMake(0, 0)];
layer.path = startPath.CGPath;
layer.transform = CATransform3DMakeRotation(M_PI / 3.0f * i, 0, 0, 1.0);
[self.layer addSublayer:layer];
CAAnimationGroup *group = [CAAnimationGroup animation];
group.removedOnCompletion = NO;
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.fillMode = kCAFillModeForwards;
group.duration = duration;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.fromValue = @(0.0f);
scaleAnim.toValue = @(1.0f);
scaleAnim.duration = duration * 0.2f;
UIBezierPath *endPath = [UIBezierPath bezierPath];
[endPath moveToPoint:CGPointMake(-2, -length)];
[endPath addLineToPoint:CGPointMake(2, -length)];
[endPath addLineToPoint:CGPointMake(0, -length)];
CABasicAnimation *pathAnim = [CABasicAnimation animationWithKeyPath:@"path"];
pathAnim.fromValue = (__bridge id)layer.path;
pathAnim.toValue = (__bridge id)endPath.CGPath;
pathAnim.beginTime = duration * 0.2f;
pathAnim.duration = duration * 0.8f;
[group setAnimations:@[scaleAnim, pathAnim]];
[layer addAnimation:group forKey:nil];
}
self.likeAfterImgView.hidden = NO;
self.likeAfterImgView.alpha = 0.0f;
self.likeAfterImgView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[UIView animateWithDuration:0.15 animations:^{
self.likeAfterImgView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
self.likeAfterImgView.alpha = 1.0f;
self.likeBeforeImgView.alpha = 0.0f;
} completion:^(BOOL finished) {
self.likeAfterImgView.transform = CGAffineTransformIdentity;
self.likeBeforeImgView.alpha = 1.0f;
}];
}else {
self.likeAfterImgView.alpha = 1.0f;
self.likeAfterImgView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
[UIView animateWithDuration:0.15 animations:^{
self.likeAfterImgView.transform = CGAffineTransformMakeScale(0.3f, 0.3f);
} completion:^(BOOL finished) {
self.likeAfterImgView.transform = CGAffineTransformIdentity;
self.likeAfterImgView.hidden = YES;
}];
}
具體代碼可在demo中的GKLikeView中查看
4、雙擊點(diǎn)贊動(dòng)畫(huà)
這個(gè)動(dòng)畫(huà)主要是通過(guò)在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法中根據(jù)觸摸的位置不停創(chuàng)建紅心醒第,并放大渔嚷、透明,然后銷(xiāo)毀稠曼,主要代碼如下:
- (void)createAnimationWithTouch:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.tapCount <= 1.0f) return;
CGPoint point = [touch locationInView:touch.view];
UIImage *image = [UIImage imageNamed:@"likeHeart"];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ADAPTATIONRATIO * 160.0f, ADAPTATIONRATIO * 160.0f)];
imgView.image = image;
imgView.contentMode = UIViewContentModeScaleAspectFill;
imgView.center = point;
// 隨機(jī)左右顯示
int leftOrRight = arc4random() % 2;
leftOrRight = leftOrRight ? leftOrRight : -1;
imgView.transform = CGAffineTransformRotate(imgView.transform, M_PI / 9.0f * leftOrRight);
[touch.view addSubview:imgView];
// 出現(xiàn)的時(shí)候回彈一下
__block UIImageView *blockImgV = imgView;
__block UIImage *blockImage = image;
[UIView animateWithDuration:0.1 animations:^{
blockImgV.transform = CGAffineTransformScale(blockImgV.transform, 1.2f, 1.2f);
} completion:^(BOOL finished) {
blockImgV.transform = CGAffineTransformScale(blockImgV.transform, 0.8f, 0.8f);
// 向上飄形病,放大,透明
[self performSelector:@selector(animationToTop:) withObject:@[blockImgV, blockImage] afterDelay:0.3f];
}];
}
具體代碼可在demo中的GKDoubleLikeView中查看。
最后
上面提到的所有動(dòng)畫(huà)都可以在github上的demoGKDYVideo中查看漠吻,如果覺(jué)得不錯(cuò)量瓜,還請(qǐng)來(lái)個(gè)star!
贊賞
您的贊賞是對(duì)我最大的支持
微信贊賞
|
支付寶贊賞
|
---|
參考
抖音點(diǎn)贊動(dòng)畫(huà)實(shí)現(xiàn)—iOS
iOS開(kāi)發(fā)_仿抖音點(diǎn)贊動(dòng)畫(huà)功能的實(shí)現(xiàn)