iOS实现抖音点赞动画效果

本文实例为大家分享了iOS实现抖音点赞动画的具体代码,供大家参考,具体内容如下

1. 概述

最近看到抖音点赞爱心的动画效果比较好,出于好奇,自己也研究仿照动画效果写了一个,不喜欢的朋友可不要喷我噢!!!

话不多说,先来看一下执行效果。

2. 动画分析

上面的示例效果有点快,现在来看一个慢的,然后在分析动画组成。

这回看清楚了吧,哈哈。

2.1 动画过程分析

咱们就以10秒的点赞动画来分析一下:

点赞的时候:

1、点击的时候,白色爱心逐渐变小到一定程度,然后变成红色爱心。(3秒)
2、红色爱心慢慢变大,最终有个缓冲动画,然后恢复原尺寸。(7秒)
3、在红色爱心变大的时候,有一个红色的圆环逐渐变大,圆环宽度由小变大,再变小消失。(5秒)
4、在红色爱心变大的时候,还有6个环绕爱心的三角形,三角形由小变大,再变小消失。(7秒)
5、注意,2、3、4的动画是在1动画结束后同时执行的,即延迟3秒再执行。

取消点赞的时候:

1、点击后红色爱心逐渐变小。
2、变小后,设置不可见,并恢复原尺寸。

2.2 代码实现原理分析

1、自定义一个UIView,并添加两个UIImageView,分别显示红色爱心和白色爱心,红色爱心在白色爱心上面,并设置红色爱心不可见。
2、给UIView添加单击手势。
3、点击时判断是点赞还是取消点赞,如果是点赞:
4、用两个UIView自带的动画,将白色ImageView的transform变小,变小后不可见,然后设置红色ImageView的transform变大,变大后白色ImageView的transform变回原尺寸。
5、通过贝塞尔曲线和CAShapeLayer绘制圆环,并给圆环添加动画组CAAnimationGroup,动画组中添加了一个基础动画CABasicAnimation(将圆环从小变大)和一个关键帧动画CAKeyframeAnimation(将圆环宽度由小变大再变小消失)
6、通过贝塞尔曲线和CAShapeLayer循环绘制6个三角形,并通过CATransform3DMakeRotation旋转6个三角形,使其环绕爱心一周。
7、给每个三角形添加一个关键帧动画CAKeyframeAnimation(将三角形由小变大再变小消失)
8、如果是取消点赞,比较简单,逐渐将红色爱心变小,然后设置不可见,白色爱心自然就显示出来了。
9、在动画执行过程中,关闭用户交互,待动画结束,再打开用户交互。

分析的有些简单,只是提供一种思路,没有什么比看代码更直接的了,来吧!

3. 全部代码

代码中添加了很多的注释,方便理解。

import UIKit

public class LikeView: UIView {

 // 红色爱心视图
 fileprivate var likeImageView = UIImageView()
 // 白色爱心视图
 fileprivate var unLikeImageView = UIImageView()
 // true: 点赞, false:取消点赞
 fileprivate var isLike: Bool = false
 // 动画时长,可设置
 public var duration: CFTimeInterval = 0.5

 override init(frame: CGRect) {
 super.init(frame: frame)
 setupUI()
 }

 required init?(coder: NSCoder) {
 super.init(coder: coder)
 setupUI()
 }

 fileprivate func setupUI() {
 // 添加白色爱心视图
 unLikeImageView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
 unLikeImageView.image = UIImage(named: "icon_like_before")
 addSubview(unLikeImageView)

 // 添加红色爱心视图,并设置不可看。切记红色爱心在在白色爱心的上面。
 likeImageView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
 likeImageView.image = UIImage(named: "icon_like_after")
 likeImageView.alpha = 0
 addSubview(likeImageView)

 // 添加单击手势
 let tap = UITapGestureRecognizer(target: self, action: #selector(tapLikeAction))
 self.addGestureRecognizer(tap)
 }

 // 点击事件
 @objc fileprivate func tapLikeAction() {
 // 点击的时候停止交互,以免反复点击。
 self.isUserInteractionEnabled = false
 isLike = !isLike

 // 点赞
 if isLike {
 // 设置红色爱心不可见
 likeImageView.alpha = 0

 // 将红色爱心缩小至原来0.2倍。
 self.likeImageView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)

 /* 添加动画, 使白色爱心变小,红色爱心变大,此过程占用全部动画时长。*/

 UIView.animate(withDuration: duration * 0.3, delay: 0, options: .curveEaseInOut) { [weak self] in
 // 将白色爱心逐渐变小至0.2倍,
 self?.unLikeImageView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
 } completion: { [weak self] (finished) in
 // 设置红色爱心可见,此时是0.2倍大小。
 self?.likeImageView.alpha = 1
 let duration = self?.duration ?? 0.5
 // 白色爱心变小后,继续操作红色爱心
 UIView.animate(withDuration: duration * 0.7, delay: 0.1, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.8, options: .curveEaseInOut) {
  // 将红色爱心恢复原大小
  self?.likeImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
 } completion: { (finished) in
  // 红色爱心变大后,恢复白色爱心的尺寸,开启用户交互。
  self?.unLikeImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
  self?.isUserInteractionEnabled = true
 }
 }

 //***************** 以下是圆环动画,在红色爱心变大的时候执行。******************//

 // 小圆环路径
 let circleStartPath = UIBezierPath(arcCenter: likeImageView.layer.position, radius: self.bounds.size.width / 6, startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: true)

 // 大圆环路径
 let radius = sqrt(powf(Float(self.bounds.size.width), 2) + powf(Float(self.bounds.size.height), 2))/2
 let circleEndPath = UIBezierPath(arcCenter: likeImageView.layer.position, radius: CGFloat(radius), startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: true)

 // 创建圆环图层,用于显示圆环。
 let circleLayer = CAShapeLayer()
 circleLayer.strokeColor = UIColor.red.cgColor
 circleLayer.fillColor = UIColor.clear.cgColor
 self.layer.insertSublayer(circleLayer, below: self.likeImageView.layer)

 // 计算圆环图层的偏移时间
 var currentTimeInSuper = self.layer.convertTime(CACurrentMediaTime(), from: nil)
 var currentTimeLocal = circleLayer.convertTime(currentTimeInSuper, from: self.layer)

 // 设置圆环动画组执行时间
 let circleGroupDuration = duration * 0.5

 // 圆环动画组
 let circleGroup = CAAnimationGroup()
 circleGroup.duration = circleGroupDuration
 // 圆环动画组开始时间,此开始时间正好是白色爱心变小后,红色爱心开始变大时。
 circleGroup.beginTime = currentTimeLocal + duration * 0.3

 // 设置圆环路径变化动画
 let circlePathAnimation = CABasicAnimation(keyPath: "path")
 circlePathAnimation.fromValue = circleStartPath.cgPath
 circlePathAnimation.toValue = circleEndPath.cgPath

 // 设置圆环宽度变化动画,先变大,再变小。
 let circleLineWidthAnimation = CAKeyframeAnimation(keyPath: "lineWidth")
 circleLineWidthAnimation.values = [1.0, 4.0, 0.3]
 circleLineWidthAnimation.keyTimes = [0.0, 0.7, 0.9]

 // 将圆环的两个动画添加到动画组。
 circleGroup.animations = [circlePathAnimation, circleLineWidthAnimation]

 // 将动画添加到圆环图层。
 circleLayer.add(circleGroup, forKey: nil)
 //**********************************************************************//

 //***************** 以下是周围6个三角形放射动画,在红色爱心变大的时候执行。******************//
 // 循环创建三角形图层,并添加动画效果
 for i in 0..<6 {
 // 三角形的高
 let height = self.bounds.size.height / 2 + 12
 // 三角形底边长
 let width = self.bounds.size.width / 10

 // 绘制一个起始三角形路径
 let triangleStartPath = UIBezierPath()
 triangleStartPath.move(to: .zero)
 triangleStartPath.addLine(to: CGPoint(x: -1, y: -1))
 triangleStartPath.addLine(to: CGPoint(x: 1, y: -1))
 triangleStartPath.close()

 // 绘制一个完全展开的三角形路径
 let triangleMiddlePath = UIBezierPath()
 triangleMiddlePath.move(to: .zero)
 triangleMiddlePath.addLine(to: CGPoint(x: -width/2, y: -height))
 triangleMiddlePath.addLine(to: CGPoint(x: width/2, y: -height))
 triangleMiddlePath.close()

 // 绘制一个终了三角形路径
 let triangleEndPath = UIBezierPath()
 triangleEndPath.move(to: CGPoint(x: 0, y: -height))
 triangleEndPath.addLine(to: CGPoint(x: -width/2, y: -height))
 triangleEndPath.addLine(to: CGPoint(x: width/2, y: -height))
 triangleEndPath.close()

 // 绘制三角形图层
 let shapeLayer = CAShapeLayer()
 // 设置图层中心位置,很重要。
 shapeLayer.position = self.likeImageView.layer.position
 shapeLayer.fillColor = UIColor.red.cgColor
 // 将图层进行旋转。
 shapeLayer.transform = CATransform3DMakeRotation(CGFloat(Double.pi/3) * CGFloat(i), 0, 0, 1)

 self.layer.insertSublayer(shapeLayer, below: circleLayer)

 // 计算三角形图层的偏移时间
 currentTimeInSuper = self.layer.convertTime(CACurrentMediaTime(), from: nil)
 currentTimeLocal = shapeLayer.convertTime(currentTimeInSuper, from: self.layer)

 // 设置三角形的动画,由小变大再变小。
 let trianglePathAnimation = CAKeyframeAnimation(keyPath: "path")
 trianglePathAnimation.values = [triangleStartPath.cgPath, triangleMiddlePath.cgPath, triangleEndPath.cgPath]
 trianglePathAnimation.keyTimes = [0.0, 0.3, 0.7]
 trianglePathAnimation.duration = duration * 0.7
 trianglePathAnimation.beginTime = currentTimeLocal + duration * 0.3

 shapeLayer.add(trianglePathAnimation, forKey: nil)
 }
 //**********************************************************************//
 }else {
 // 取消点赞
 // 1. 将红色爱心逐渐缩小至原来的0.1倍,然后设置为不可见并恢复原尺寸大小。
 UIView.animate(withDuration: duration * 0.3, delay: 0, options: .curveEaseInOut) { [weak self] in
 self?.likeImageView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
 } completion: { [weak self] (finished) in
 self?.likeImageView.alpha = 0
 self?.likeImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
 self?.isUserInteractionEnabled = true
 }
 }
 }
}

LikeView即是自定义的点赞视图,可纯代码创建,也可通过xib创建,同时支持设置动画执行时间duration。

调用的地方:

class ViewController: UIViewController {

 override func viewDidLoad() {
 super.viewDidLoad()
 view.backgroundColor = UIColor.black
 // 设置一个0.5秒的动画
 let likeView1 = LikeView(frame: CGRect(x: 110, y: 300, width: 50, height: 50))
 likeView1.duration = 0.5
 self.view.addSubview(likeView1)

 // 设置一个10秒的动画
 let likeView2 = LikeView(frame: CGRect(x: 240, y: 300, width: 50, height: 50))
 likeView2.duration = 10
 self.view.addSubview(likeView2)
 }

}

执行效果:

4. 结束语

代码中主要用到了:UIView基础动画、CGAffineTransform、CATransform3D、UIBezierPath、CAShapeLayer、CAKeyframeAnimation、CABasicAnimation、CAAnimationGroup,另外还有beginTime的计算,也算是个小重点了。

以上只是仿照抖音点赞动画实现的功能,代码不多,但也不少,不知道抖音是具体怎么实现的,如果有什么不对的地方,或者可优化的地方,还请路过的朋友多多指点。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2021-01-28

iOS如何优雅地实现序列动画详解

前言 我们的在做动效中经常会有这样的需求,A动画执行完,执行B动画,B动画执行完执行C动画这样的序列,比如如下效果: iOS 10之前,我们可能这样实现这个动画序列,实际上可能你现在的代码就是这样写的: UIView.animate()提供了一个完成block回调,我们可以用它来触发下一个动画.这样做,我们可以实现这个动画.正如你可以看到的,这坨代码的主要缺点是丑陋,几乎没有可读性. UIViewPropertyAnimator iOS10引入了UIViewPropertyAnimator ,基

iOS基于CATransition实现翻页、旋转等动画效果

基于CATransition实现翻页.旋转.淡化.推进.滑入滑出.立方体.吮吸.波纹等动画效果. 首先看一下效果图: 下面贴上代码: #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "ViewController.h" //获得屏幕的宽高 #define mainW [UIScreen mainScreen].bounds.size.width #define

iOS实现数字倍数动画效果

前言 一个简单的利用 透明度和 缩放 实现的 数字倍数动画 效果图: 实现思路 上代码 看比较清晰 // 数字跳动动画 - (void)labelDanceAnimation:(NSTimeInterval)duration { //透明度 CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; opacityAnimation.duration = 0.4 * d

iOS实现点赞动画特效

本文实例为大家分享了iOS实现点赞动画特效的具体代码,供大家参考,具体内容如下 动画的基本使用 动画的实现基本上是基于对View控件和View的layer属性进行操作,对视图进行移动,尺寸变换,透明度变换,旋转等一系列操作. 关键帧动画: 动画的实现可以分为两个部分,一部分是规定动画的变化内容,比如view需要把scale从0变化到1,这个数字是相对值,即从尺寸为0变化到正常尺寸.另一个部分是规定动画的渐变时间.这样就实现了view在规定时间完成指定变化了,这个变化的过程也可以通过参数设置为非均

iOS自定义转场动画的几种情况

前言 在开发中,无论我们使用 Push 还是 Present 推出新的 ViewController 时,系统为了提高用户体验都会为我们默认加上一些过渡动画.但是,系统默认的动画总是不能满足大家各种各样的需求的,所以系统也为我们提供了在不同场景下自定义过渡动画以及通过手势控制过渡进度的实现方案. 这篇文章记录了自定义转场动画中的几种情况: 模态跳转(Present) 导航控制器跳转(Push) UITabbarController 三方框架--Lottie 效果图 预备 首先,我们现在介绍几个在

iOS实现转场动画的3种方法示例

什么是转场动画 在 NavigationController 里 push 或 pop 一个 View Controller,在 TabBarController 中切换到其他 View Controller,以 Modal 方式显示另外一个 View Controller,这些都是 View Controller Transition.在 storyboard 里,每个 View Controller 是一个 Scene,View Controller Transition 便是从一个 Sce

iOS仿微博导航栏动画(CoreGraphics)的实现方法

前言 昨天刚做完项目的新版本.除了尝试一些新的架构之外.功能方面并没什么特别的地方. 但是顺手搞了一些还算好玩的东西.其一就是这个导航栏的动画. 感觉还算简单易懂.分享一下(其实更多是最近攒了好多封面.不贴出来憋得人难受). 导航栏动画.gif 思路 先介绍CA的两个方法: 基于原始状态的位移 CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) CG_AVAILABLE_STAR

iOS仿抖音视频加载动画效果的实现方法

前言 这几天一直跟开源的抖音demo斗智斗勇,今天跟大家分享的是抖音中或者快手中加载视频的动画,这个加载效果还是挺实用,下面话不多说了,来随着小编一起学习学习吧 上图看成品 实现原理 首先我创建一个视图 @interface ViewController () @property (nonatomic, strong) UIView *playLoadingView; @end @implementation ViewController - (void)viewDidLoad { [super

iOS自定义UIButton点击动画特效

借鉴相关资料,整理了一个很有意思的button动画效果,iOS自定义UIButton点击动画特效 先看一下效果图: 下面贴上代码: ViewController: #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "ViewController.h" #import "HWButton.h" #define mainW [UIScreen m

iOS仿AirPods弹出动画

本文实例为大家分享了iOS仿AirPods弹出动画的具体代码,供大家参考,具体内容如下 效果图 预览图 思路 在当前ViewController下Present另外一个AnimationViewController,在弹出的AnimationViewController中播放动画,弹出的时候原来的ViewController上有一个全屏覆盖的maskView,在弹出时,有一个渐变动画(页面渐黑),在AnimationViewController声明一个代理,在代理方法中实现收起的动画效果(dis

详解 iOS 系统中的视图动画

动画为用户界面的状态转换提供了流畅的可视化效果, 在 iOS 中大量使用了动画效果, 包括改变视图位置. 大小. 从可视化树中删除视图, 隐藏视图等. 你可以考虑用动画效果给用户提供反馈或者用来实现有趣的特效. 在 iOS 系统中, Core Animation 提供了内置的动画支持, 创建动画不需要任何绘图的代码, 你要做的只是激发指定的动画, 接下来就交给 Core Animation 来渲染, 总之, 复杂的动画只需要几行代码就可以了. 哪些属性可以添加动画效果 根据 iOS 视图编程指南

详解IOS开发中生成推送的pem文件

详解IOS开发中生成推送的pem文件 具体步骤如下: 首先,需要一个pem的证书,该证书需要与开发时签名用的一致. 具体生成pem证书方法如下: 1. 登录到 iPhone Developer Connection Portal(http://developer.apple.com/iphone/manage/overview/index.action )并点击 App IDs 2. 创建一个不使用通配符的 App ID .通配符 ID 不能用于推送通知服务.例如,  com.itotem.ip

详解iOS设计中的UIWindow使用

每一个IOS程序都有一个UIWindow,在我们通过模板简历工程的时候,xcode会自动帮我们生成一个window,然后让它变成keyWindow并显示出来.这一切都来的那么自然,以至于我们大部分时候都忽略了自己也是可以创建UIWindow对象.   通常在我们需要自定义UIAlertView的时候(IOS 5.0以前AlertView的背景样式等都不能换)我们可以使用UIWindow来实现(设置windowLevel为Alert级别),网上有很多例子,这里就不详细说了. 一.UIWindowL

详解iOS开发中的转场动画和组动画以及UIView封装动画

一.转场动画 CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果.iOS比Mac OS X的转场动画效果少一点 UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果 属性解析: type:动画过渡类型 subtype:动画过渡方向 startProgress:动画起点(在整体动画的百分比) endProgress:动画终点(在整体动画的百分比) 转场动画代码示例 1.界面搭建 2.实现代码 复制代码

详解iOS应用中自定义UIBarButtonItem导航按钮的创建方法

iOS系统导航栏中有leftBarButtonItem和rightBarButtonItem,我们可以根据自己的需求来自定义这两个UIBarButtonItem. 四种创建方法 系统提供了四种创建的方法: 复制代码 代码如下: - (instancetype)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(id)target action:(SEL)action; - (instancetype)init

详解iOS App中UITableView的创建与内容刷新

UITableView几乎是iOS开发中用处最广的一个控件,当然也是要记相当多东西的一个控件. 创建 首先创建一个新的项目,并添加一个MainViewController的Class文件 打开MainViewController.h文件 @interface MainViewController : UIViewController<UITableViewDataSource,UITableViewDelegate> @property (nonatomic, retain) NSArray

详解Linux系统中的tempfs与/dev/shm

tmpfs 是 Linux/Unix 系统上的一种基于内存的文件系统,即 tmpfs 使用内存或 swap 分区来存储文件. Linux 内核中的 VM 子系统负责在后台管理虚拟内存资源 Virtual Memory,即 RAM 和 swap 资源,透明地将 RAM 页移动到交换分区或从交换分区到 RAM 页,tmpfs 文件系统需要 VM 子系统的页面来存储文件.tmpfs 自己并不知道这些页面是在交换分区还是在 RAM 中:做这种决定是 VM 子系统的工作.tmpfs 文件系统所知道的就是它

详解iOS开发中Keychain的相关使用

一.Keychain 基础 根据苹果的介绍,iOS设备中的Keychain是一个安全的存储容器,可以用来为不同应用保存敏感信息比如用户名,密码,网络密码,认证令牌.苹果自己用keychain来保存Wi-Fi网络密码,VPN凭证等等.它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的. 开发者通常会希望能够利用操作系统提供的功能来保存凭证(credentials)而不是把它们(凭证)保存到NSUserDefault

详解安卓系统中的Android.mk文件

概述     Android.mk文件用来向编译系统描述如何编译你的源代码.更确切地说,该文件其实就是一个小型的Makefile.由于该文件会被NDK的编译工具解析多次,因此应该尽量减少源码中声明变量,因为这些变量可能会被多次定义从而影响到后面的解析.这个文件的语法允许把源代码组织成模块,每个模块属于下列类型之一: APK程序:一般的Android程序,编译打包生成apk文件.     JAVA库:java类库,编译打包生成jar包文件.     C\C++应用程序:可执行的C/C++应用程序.

详解Linux系统中虚拟设备文件的各种实用用法

大家好,我是良许. 大家知道,在 Linux 下,一切皆文件,对于设备文件也是如此.我们在工作的过程中,经常会看到 /dev/null 这个玩意,那它到底是什么呢? 专业地讲,/dev/null 是一个虚拟设备文件.而对程序而言,这些虚拟设备文件则会被当成真实的文件对待.程序可以向这种数据源请求数据,所得到的数据将由操作系统提供.但是,这些数据并不是从磁盘上读取到的,而是由操作系统动态生成的.虚拟设备文件的一个典型例子就是 /dev/zero . 然而,当你想向 /dev/null 写入数据时,

详解iOS App中UiTabBarController组件的基本用法

UiTabBarController这个控制器绝对是项目架构时做常用的一个控件. 我们大致看下控件的效果,我们就知道为什么说他常见了. 这就是最简单的一个雏形,想必现在基本70%的应用界面结构都会是这样的. 在Android中我们以ActivityGroup或是现在的fragment来实现,一个容器中包含多个子控制器. 下面我们还是以建立xib文件的形式来实现一个这样的整体布局的例子. 当然在 xcode中我们会发现其实直接有这么一个模板了 但是直接使用模板后会发现是直接在代码里实现了子布局得添