iOS實(shí)現(xiàn)抖音點(diǎn)贊動(dòng)畫(huà)效果
本文實(shí)例為大家分享了iOS實(shí)現(xiàn)抖音點(diǎn)贊動(dòng)畫(huà)的具體代碼,供大家參考,具體內(nèi)容如下
1. 概述
最近看到抖音點(diǎn)贊愛(ài)心的動(dòng)畫(huà)效果比較好,出于好奇,自己也研究仿照動(dòng)畫(huà)效果寫(xiě)了一個(gè),不喜歡的朋友可不要噴我噢?。?!
話不多說(shuō),先來(lái)看一下執(zhí)行效果。
2. 動(dòng)畫(huà)分析
上面的示例效果有點(diǎn)快,現(xiàn)在來(lái)看一個(gè)慢的,然后在分析動(dòng)畫(huà)組成。
這回看清楚了吧,哈哈。
2.1 動(dòng)畫(huà)過(guò)程分析
咱們就以10秒的點(diǎn)贊動(dòng)畫(huà)來(lái)分析一下:
點(diǎn)贊的時(shí)候:
1、點(diǎn)擊的時(shí)候,白色愛(ài)心逐漸變小到一定程度,然后變成紅色愛(ài)心。(3秒)
2、紅色愛(ài)心慢慢變大,最終有個(gè)緩沖動(dòng)畫(huà),然后恢復(fù)原尺寸。(7秒)
3、在紅色愛(ài)心變大的時(shí)候,有一個(gè)紅色的圓環(huán)逐漸變大,圓環(huán)寬度由小變大,再變小消失。(5秒)
4、在紅色愛(ài)心變大的時(shí)候,還有6個(gè)環(huán)繞愛(ài)心的三角形,三角形由小變大,再變小消失。(7秒)
5、注意,2、3、4的動(dòng)畫(huà)是在1動(dòng)畫(huà)結(jié)束后同時(shí)執(zhí)行的,即延遲3秒再執(zhí)行。
取消點(diǎn)贊的時(shí)候:
1、點(diǎn)擊后紅色愛(ài)心逐漸變小。
2、變小后,設(shè)置不可見(jiàn),并恢復(fù)原尺寸。
2.2 代碼實(shí)現(xiàn)原理分析
1、自定義一個(gè)UIView,并添加兩個(gè)UIImageView,分別顯示紅色愛(ài)心和白色愛(ài)心,紅色愛(ài)心在白色愛(ài)心上面,并設(shè)置紅色愛(ài)心不可見(jiàn)。
2、給UIView添加單擊手勢(shì)。
3、點(diǎn)擊時(shí)判斷是點(diǎn)贊還是取消點(diǎn)贊,如果是點(diǎn)贊:
4、用兩個(gè)UIView自帶的動(dòng)畫(huà),將白色I(xiàn)mageView的transform變小,變小后不可見(jiàn),然后設(shè)置紅色I(xiàn)mageView的transform變大,變大后白色I(xiàn)mageView的transform變回原尺寸。
5、通過(guò)貝塞爾曲線和CAShapeLayer繪制圓環(huán),并給圓環(huán)添加動(dòng)畫(huà)組CAAnimationGroup,動(dòng)畫(huà)組中添加了一個(gè)基礎(chǔ)動(dòng)畫(huà)CABasicAnimation(將圓環(huán)從小變大)和一個(gè)關(guān)鍵幀動(dòng)畫(huà)CAKeyframeAnimation(將圓環(huán)寬度由小變大再變小消失)
6、通過(guò)貝塞爾曲線和CAShapeLayer循環(huán)繪制6個(gè)三角形,并通過(guò)CATransform3DMakeRotation旋轉(zhuǎn)6個(gè)三角形,使其環(huán)繞愛(ài)心一周。
7、給每個(gè)三角形添加一個(gè)關(guān)鍵幀動(dòng)畫(huà)CAKeyframeAnimation(將三角形由小變大再變小消失)
8、如果是取消點(diǎn)贊,比較簡(jiǎn)單,逐漸將紅色愛(ài)心變小,然后設(shè)置不可見(jiàn),白色愛(ài)心自然就顯示出來(lái)了。
9、在動(dòng)畫(huà)執(zhí)行過(guò)程中,關(guān)閉用戶交互,待動(dòng)畫(huà)結(jié)束,再打開(kāi)用戶交互。
分析的有些簡(jiǎn)單,只是提供一種思路,沒(méi)有什么比看代碼更直接的了,來(lái)吧!
3. 全部代碼
代碼中添加了很多的注釋,方便理解。
import UIKit public class LikeView: UIView { // 紅色愛(ài)心視圖 fileprivate var likeImageView = UIImageView() // 白色愛(ài)心視圖 fileprivate var unLikeImageView = UIImageView() // true: 點(diǎn)贊, false:取消點(diǎn)贊 fileprivate var isLike: Bool = false // 動(dòng)畫(huà)時(shí)長(zhǎng),可設(shè)置 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() { // 添加白色愛(ài)心視圖 unLikeImageView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height) unLikeImageView.image = UIImage(named: "icon_like_before") addSubview(unLikeImageView) // 添加紅色愛(ài)心視圖,并設(shè)置不可看。切記紅色愛(ài)心在在白色愛(ài)心的上面。 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) // 添加單擊手勢(shì) let tap = UITapGestureRecognizer(target: self, action: #selector(tapLikeAction)) self.addGestureRecognizer(tap) } // 點(diǎn)擊事件 @objc fileprivate func tapLikeAction() { // 點(diǎn)擊的時(shí)候停止交互,以免反復(fù)點(diǎn)擊。 self.isUserInteractionEnabled = false isLike = !isLike // 點(diǎn)贊 if isLike { // 設(shè)置紅色愛(ài)心不可見(jiàn) likeImageView.alpha = 0 // 將紅色愛(ài)心縮小至原來(lái)0.2倍。 self.likeImageView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2) /* 添加動(dòng)畫(huà), 使白色愛(ài)心變小,紅色愛(ài)心變大,此過(guò)程占用全部動(dòng)畫(huà)時(shí)長(zhǎng)。*/ UIView.animate(withDuration: duration * 0.3, delay: 0, options: .curveEaseInOut) { [weak self] in // 將白色愛(ài)心逐漸變小至0.2倍, self?.unLikeImageView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2) } completion: { [weak self] (finished) in // 設(shè)置紅色愛(ài)心可見(jiàn),此時(shí)是0.2倍大小。 self?.likeImageView.alpha = 1 let duration = self?.duration ?? 0.5 // 白色愛(ài)心變小后,繼續(xù)操作紅色愛(ài)心 UIView.animate(withDuration: duration * 0.7, delay: 0.1, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.8, options: .curveEaseInOut) { // 將紅色愛(ài)心恢復(fù)原大小 self?.likeImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) } completion: { (finished) in // 紅色愛(ài)心變大后,恢復(fù)白色愛(ài)心的尺寸,開(kāi)啟用戶交互。 self?.unLikeImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) self?.isUserInteractionEnabled = true } } //***************** 以下是圓環(huán)動(dòng)畫(huà),在紅色愛(ài)心變大的時(shí)候執(zhí)行。******************// // 小圓環(huán)路徑 let circleStartPath = UIBezierPath(arcCenter: likeImageView.layer.position, radius: self.bounds.size.width / 6, startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: true) // 大圓環(huán)路徑 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) // 創(chuàng)建圓環(huán)圖層,用于顯示圓環(huán)。 let circleLayer = CAShapeLayer() circleLayer.strokeColor = UIColor.red.cgColor circleLayer.fillColor = UIColor.clear.cgColor self.layer.insertSublayer(circleLayer, below: self.likeImageView.layer) // 計(jì)算圓環(huán)圖層的偏移時(shí)間 var currentTimeInSuper = self.layer.convertTime(CACurrentMediaTime(), from: nil) var currentTimeLocal = circleLayer.convertTime(currentTimeInSuper, from: self.layer) // 設(shè)置圓環(huán)動(dòng)畫(huà)組執(zhí)行時(shí)間 let circleGroupDuration = duration * 0.5 // 圓環(huán)動(dòng)畫(huà)組 let circleGroup = CAAnimationGroup() circleGroup.duration = circleGroupDuration // 圓環(huán)動(dòng)畫(huà)組開(kāi)始時(shí)間,此開(kāi)始時(shí)間正好是白色愛(ài)心變小后,紅色愛(ài)心開(kāi)始變大時(shí)。 circleGroup.beginTime = currentTimeLocal + duration * 0.3 // 設(shè)置圓環(huán)路徑變化動(dòng)畫(huà) let circlePathAnimation = CABasicAnimation(keyPath: "path") circlePathAnimation.fromValue = circleStartPath.cgPath circlePathAnimation.toValue = circleEndPath.cgPath // 設(shè)置圓環(huán)寬度變化動(dòng)畫(huà),先變大,再變小。 let circleLineWidthAnimation = CAKeyframeAnimation(keyPath: "lineWidth") circleLineWidthAnimation.values = [1.0, 4.0, 0.3] circleLineWidthAnimation.keyTimes = [0.0, 0.7, 0.9] // 將圓環(huán)的兩個(gè)動(dòng)畫(huà)添加到動(dòng)畫(huà)組。 circleGroup.animations = [circlePathAnimation, circleLineWidthAnimation] // 將動(dòng)畫(huà)添加到圓環(huán)圖層。 circleLayer.add(circleGroup, forKey: nil) //**********************************************************************// //***************** 以下是周圍6個(gè)三角形放射動(dòng)畫(huà),在紅色愛(ài)心變大的時(shí)候執(zhí)行。******************// // 循環(huán)創(chuàng)建三角形圖層,并添加動(dòng)畫(huà)效果 for i in 0..<6 { // 三角形的高 let height = self.bounds.size.height / 2 + 12 // 三角形底邊長(zhǎng) let width = self.bounds.size.width / 10 // 繪制一個(gè)起始三角形路徑 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() // 繪制一個(gè)完全展開(kāi)的三角形路徑 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() // 繪制一個(gè)終了三角形路徑 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() // 設(shè)置圖層中心位置,很重要。 shapeLayer.position = self.likeImageView.layer.position shapeLayer.fillColor = UIColor.red.cgColor // 將圖層進(jìn)行旋轉(zhuǎn)。 shapeLayer.transform = CATransform3DMakeRotation(CGFloat(Double.pi/3) * CGFloat(i), 0, 0, 1) self.layer.insertSublayer(shapeLayer, below: circleLayer) // 計(jì)算三角形圖層的偏移時(shí)間 currentTimeInSuper = self.layer.convertTime(CACurrentMediaTime(), from: nil) currentTimeLocal = shapeLayer.convertTime(currentTimeInSuper, from: self.layer) // 設(shè)置三角形的動(dòng)畫(huà),由小變大再變小。 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 { // 取消點(diǎn)贊 // 1. 將紅色愛(ài)心逐漸縮小至原來(lái)的0.1倍,然后設(shè)置為不可見(jiàn)并恢復(fù)原尺寸大小。 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即是自定義的點(diǎn)贊視圖,可純代碼創(chuàng)建,也可通過(guò)xib創(chuàng)建,同時(shí)支持設(shè)置動(dòng)畫(huà)執(zhí)行時(shí)間duration。
調(diào)用的地方:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.black // 設(shè)置一個(gè)0.5秒的動(dòng)畫(huà) let likeView1 = LikeView(frame: CGRect(x: 110, y: 300, width: 50, height: 50)) likeView1.duration = 0.5 self.view.addSubview(likeView1) // 設(shè)置一個(gè)10秒的動(dòng)畫(huà) let likeView2 = LikeView(frame: CGRect(x: 240, y: 300, width: 50, height: 50)) likeView2.duration = 10 self.view.addSubview(likeView2) } }
執(zhí)行效果:
4. 結(jié)束語(yǔ)
代碼中主要用到了:UIView基礎(chǔ)動(dòng)畫(huà)、CGAffineTransform、CATransform3D、UIBezierPath、CAShapeLayer、CAKeyframeAnimation、CABasicAnimation、CAAnimationGroup,另外還有beginTime的計(jì)算,也算是個(gè)小重點(diǎn)了。
以上只是仿照抖音點(diǎn)贊動(dòng)畫(huà)實(shí)現(xiàn)的功能,代碼不多,但也不少,不知道抖音是具體怎么實(shí)現(xiàn)的,如果有什么不對(duì)的地方,或者可優(yōu)化的地方,還請(qǐng)路過(guò)的朋友多多指點(diǎn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS如何優(yōu)雅地實(shí)現(xiàn)序列動(dòng)畫(huà)詳解
這篇文章主要給大家介紹了關(guān)于iOS如何優(yōu)雅地實(shí)現(xiàn)序列動(dòng)畫(huà)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12提高iOS開(kāi)發(fā)的小技巧和思路小結(jié) (二)
這篇文章主要跟大家分享了關(guān)于提高iOS開(kāi)發(fā)的一些小技巧和思路,通過(guò)本文總結(jié)的這些小技巧和思路相信對(duì)對(duì)大家開(kāi)發(fā)iOS具有一定的參考價(jià)值,感興趣的朋友們可以參考學(xué)習(xí),下面來(lái)跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。2017-04-04Objective-C中利用正則去除非數(shù)字字母漢字方法實(shí)例
正則表達(dá)式對(duì)我們?nèi)粘i_(kāi)發(fā)來(lái)說(shuō)是必不可少的,下面這篇文章主要給大家介紹了關(guān)于Objective-C中如何利用正則去除非數(shù)字字母漢字的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06iOS如何保持程序在后臺(tái)長(zhǎng)時(shí)間運(yùn)行
這篇文章主要為大家詳細(xì)介紹了iOS如何保持程序在后臺(tái)長(zhǎng)時(shí)間運(yùn)行,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09iOS開(kāi)發(fā)中最基本的位置功能實(shí)現(xiàn)示例
這篇文章主要介紹了iOS開(kāi)發(fā)中最基本的位置功能實(shí)現(xiàn)示例,需要的朋友可以參考下2015-09-09xcode8提交ipa失敗無(wú)法構(gòu)建版本問(wèn)題的解決方案
xcode升級(jí)到xcode8后發(fā)現(xiàn)構(gòu)建不了新的版本。怎么解決呢?下面小編給大家?guī)?lái)了xcode8提交ipa失敗無(wú)法構(gòu)建版本問(wèn)題的解決方案,非常不錯(cuò),一起看看吧2016-10-10