[英]SwiftUI Ripple Effect Animation
我正在努力在 SwiftUI 中创建一种类似于此处的涟漪效应。
这是我到目前为止所拥有的:
import SwiftUI
// MARK: - Ripple
struct Ripple: ViewModifier {
// MARK: Lifecycle
init(rippleColor: Color) {
self.rippleColor = rippleColor
}
// MARK: Internal
let rippleColor: Color
func body(content: Content) -> some View {
ZStack {
content
if let location = touchPoint {
Circle()
.fill(rippleColor)
.frame(width: 16.0, height: 16.0)
.position(location)
.clipped()
.opacity(opacity)
}
}
.fixedSize()
.gesture(
DragGesture(minimumDistance: 0.0)
.onChanged { gesture in
guard touchPoint != gesture.startLocation else {
return
}
timer?.invalidate()
opacity = 1.0
touchPoint = gesture.startLocation
}
.onEnded { _ in
timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
withAnimation {
opacity = 0.0
}
}
}
)
}
// MARK: Private
@State private var opacity: CGFloat = 0.0
@State private var touchPoint: CGPoint?
@State private var timer: Timer?
}
extension View {
func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
modifier(Ripple(rippleColor: rippleColor))
}
}
下一步是进行缩放 animation,但我不知道如何操作。 我试过使用缩放修改器应用缩放效果和过渡,但似乎没有任何效果。
有人可以帮助我实现我正在寻找的连锁反应吗?
此外,如果这样的东西已经存在,我很乐意使用它,但我还没有找到任何东西。
谢谢,
收入客公里
你可能正在寻找这样的东西......
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.rippleEffect(rippleColor: .gray)
.frame(width: 400, height: 200)
.padding()
}
}
struct Ripple: ViewModifier {
// MARK: Lifecycle
init(rippleColor: Color) {
self.color = rippleColor
}
// MARK: Internal
let color: Color
@State private var scale: CGFloat = 0.5
@State private var animationPosition: CGFloat = 0.0
@State private var x: CGFloat = 0.0
@State private var y: CGFloat = 0.0
@State private var opacityFraction: CGFloat = 0.0
let timeInterval: TimeInterval = 0.5
func body(content: Content) -> some View {
GeometryReader { geometry in
ZStack {
Rectangle()
.foregroundColor(.gray.opacity(0.05))
Circle()
.foregroundColor(color)
.opacity(0.2*opacityFraction)
.scaleEffect(scale)
.offset(x: x, y: y)
content
}
.onTapGesture(perform: { location in
x = location.x-geometry.size.width/2
y = location.y-geometry.size.height/2
opacityFraction = 1.0
withAnimation(.linear(duration: timeInterval)) {
scale = 3.0*(max(geometry.size.height, geometry.size.width)/min(geometry.size.height, geometry.size.width))
opacityFraction = 0.0
DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval) {
scale = 1.0
opacityFraction = 0.0
}
}
})
.clipped()
}
}
}
extension View {
func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
modifier(Ripple(rippleColor: rippleColor))
}
}
使用上面 Frederik 的回答,我稍微修改了它以达到我想要的结果。
import SwiftUI
// MARK: - ContentView
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.rippleEffect(rippleColor: .gray)
.frame(width: 400, height: 200)
.padding()
}
}
// MARK: - Ripple
struct Ripple: ViewModifier {
// MARK: Lifecycle
init(rippleColor: Color) {
color = rippleColor
}
// MARK: Internal
let color: Color
let timeInterval: TimeInterval = 0.5
func body(content: Content) -> some View {
GeometryReader { geometry in
ZStack {
Rectangle()
.foregroundColor(.gray.opacity(0.05))
Circle()
.foregroundColor(color)
.opacity(0.2 * opacityFraction)
.scaleEffect(scale)
.offset(x: x, y: y)
content
}
.gesture(
DragGesture(minimumDistance: 0.0)
.onChanged { gesture in
let location = gesture.startLocation
x = location.x - geometry.size.width / 2
y = location.y - geometry.size.height / 2
opacityFraction = 1.0
withAnimation(.linear(duration: timeInterval / 2.0)) {
scale = 3.0 *
(
max(geometry.size.height, geometry.size.width) /
min(geometry.size.height, geometry.size.width)
)
}
}
.onEnded { _ in
withAnimation(.linear(duration: timeInterval / 2.0)) {
opacityFraction = 0.0
scale = 1.0
}
}
)
.clipped()
}
}
// MARK: Private
@State private var scale: CGFloat = 0.5
@State private var animationPosition: CGFloat = 0.0
@State private var x: CGFloat = 0.0
@State private var y: CGFloat = 0.0
@State private var opacityFraction: CGFloat = 0.0
}
extension View {
func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
modifier(Ripple(rippleColor: rippleColor))
}
}
// MARK: - ContentView_Previews
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
感谢弗雷德里克,为正确的方向轻推。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.