SwiftUI中实现创建反弹动画?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
在写动画之前呢先简单回顾一下SwiftUI中动画的几个要点:
- <李>动画是视图发生变化时的渐变效果李> <>李SwiftUI动画分为隐式动画(.animation())与显式动画(withAnimation())两种李> <李>隐式动画是给视图加动画,视图所有的能动画的变化都能被隐式动画影响李> <>李显式动画是针对某个变化进行动画,能精准控制。李> <李>视图的插入和移除通过过渡(过渡)来做效果,可以组合多个过渡或自定义过渡李> <>李要构建自定义动画,我们需要实现一个可动画的视图修饰器(遵守AnimatableModier协议)或者实现一个GeometryEffect,并将可动画的属性通过animatableData暴露出来李>
<强> 强>
反弹动画属于“起始点和终止点相”等的动画,所以不能够通过SwiftUI中内建的动画来实现(因为这个视图从结果来看没有发生变化)
我们先来构建反弹动画修饰器的框架如下:
struct反弹:AnimatableModifier { var animCount: CGFloat=0 var振幅:CGFloat=10//振幅 var animatableData: CGFloat { 得到{animCount} 集{animCount=newValue} } 函数体(内容:内容)→一些观点{//改变视图动画 } }
下面一步一步来
实现身体方法,好让乘以每次增加时视图能以反弹的方式进行动画。一次反弹就向是视图上弹起又落下、下面是动画曲线大致的样子:
三角函数是<代码> y=abs (sin (x))> 代码,所以身体方法这样实现:
struct反弹:AnimatableModifier { … 函数体(内容:内容)→一些观点{ 让抵消:CGFloat=abs(罪(animCount * .pi * 2) *振幅) 返回的内容。抵消(y:抵消) } }
测试代码:
struct BouncingView:视图 @State var=0 var身体:一些视图{ 按钮(动作:{ withAnimation(动画。easeInOut(持续时间:1)){ 水龙头+=1 } 标签}:{ RoundedRectangle (cornerRadius: 15) .modifier(反弹(animCount: CGFloat(水龙头))) }) .frame(宽度:100,身高:100) } }
点击按钮,就能弹两次了~ ~
这个,# 8203;<代码> @State var水龙头代码>
, # 8203;其实并没有什么实际的意义,只是为了触发动画。
因为SwiftUI里只有观点的状态发生变化才会触发动画,而无法通过某个事件来触发;而我们的动画是一个初始状态和结束状态相等的情况,并没有状态的变化,所以这里强行把点击的次数作为视图状态的变化来触发动画。
我找了好久有没有更优雅的方式来解决这个问题,然而并没有找到==b
暴露给外面的,# 8203;<代码> animCount 代码>,# 8203;应该是一个Int才对,那么就增加一个,# 8203;<代码>,# 8203;animValue> 代码,# 8203;来代替它
struct反弹:AnimatableModifier { 让animCount: Int var animValue: CGFloat var振幅:CGFloat=10//振幅 init (animCount: Int) { 自我。animCount=animCount 自我。animValue=CGFloat (animCount) } var animatableData: CGFloat { 得到{animValue} 集{animValue=newValue} } 函数体(内容:内容)→一些观点{ 让抵消:CGFloat=abs(罪(animValue * .pi * 2) *振幅) 返回的内容。抵消(y:抵消) } }
因为,# 8203;<代码>,# 8203;animValue> 代码,# 8203;被隐藏起来了,所以需要初始化方法
为了方便使用,再添加一个视图的扩展方法:
{扩展视图 func反弹(animCount: Int)→一些观点{ 自我。修饰符(反弹(animCount: animCount)) } }
现在虽然能弹了,但是相对还比较生硬,就想在视图”落地”后再给它增加振幅逐步衰减的几次反弹,一开始尝试了简单的减半反弹,实验证明观感更差,看起来有点难受。
我们实际生活中的反弹的振幅变化应该是符合阻尼正弦波的,参考链接里的公式,修改如下:
struct反弹:AnimatableModifier { 让animCount: Int var animValue: CGFloat var振幅:CGFloat//振幅 var bouncingTimes: Int init (animCount: Int,振幅:CGFloat=10, bouncingTimes: Int=3) { 自我。animCount=animCount 自我。animValue=CGFloat (animCount) 自我。振幅=振幅 自我。bouncingTimes=bouncingTimes } var animatableData: CGFloat { 得到{animValue} 集{animValue=newValue} } 函数体(内容:内容)→一些观点{ 让t=animValue - CGFloat (animCount) 让抵消:CGFloat=abs(战俘(CGFloat (M_E) - t) *罪(t * .pi * CGFloat (bouncingTimes)) *振幅) 返回的内容。抵消(y:抵消) } } {扩展视图 func反弹(animCount: Int, 振幅:CGFloat=10, bouncingTimes: Int=3)→一些观点{ 自我。修饰符(反弹(animCount: animCount, 振幅,振幅, bouncingTimes: bouncingTimes)) } }SwiftUI中实现创建反弹动画