单击按钮时如何为我的胶囊进度设置动画?
How can I animate my capsule's progress when a button is clicked?
希望你们一切都好。
我是这个社区的新手,我希望我们能互相帮助成长。
我目前正在使用 swiftUI 构建一个 MacOS 应用程序,我有这个胶囊形状,里面有一个水动画。水动画只是水平移动的波浪,使用可动画数据和路径创建。我的问题是,我在屏幕上还有另外两个按钮,一个播放按钮和一个停止按钮。他们应该分别开始向胶囊注水和停止,他们确实这样做了,但他们应该用动画来完成,但事实并非如此。
下面是我的代码以获取更多详细信息。提前致谢。
GeometryReader { geometry in
VStack {
Spacer()
BreathingWave(progress: $progressValue, phase: phase)
.fill(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.clipShape(Capsule())
.border(Color.gray, width: 3)
.frame(width: geometry.size.width / 12)
.onAppear {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.phase = .pi * 2
}
}
HStack {
Spacer()
Image(systemName: "play.circle.fill")
.resizable()
.renderingMode(.template)
.frame(width: 50, height: 50)
.foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.scaleEffect(scalePlay ? 1.25 : 1)
.onHover(perform: { scalPlay in
withAnimation(.linear(duration: 0.1)) {
scalePlay = scalPlay
}
})
.onTapGesture {
withAnimation(
.easeInOut(duration: duration)
.repeatForever(autoreverses: true)) {
if(progressValue < 1) {
progressValue += 0.1
}
else {
progressValue = progressValue
}
}
}
Spacer()
Image(systemName: "stop.circle.fill")
.resizable()
.renderingMode(.template)
.frame(width: 50, height: 50)
.foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.scaleEffect(scaleStop ? 1.25 : 1)
.onHover(perform: { scalStop in
withAnimation(.linear(duration: 0.1)) {
scaleStop = scalStop
}
})
.onTapGesture {
withAnimation(.linear) {
progressValue = 0.0
}
}
Spacer()
}
.padding(.bottom, 50)
}
}
这是呼吸波的代码
struct BreathingWave: Shape {
@Binding var progress: Float
var applitude: CGFloat = 10
var waveLength: CGFloat = 20
var phase: CGFloat
var animatableData: CGFloat {
get { phase }
set { phase = newValue }
}
func path(in rect: CGRect) -> Path {
var path = Path()
let width = rect.width
let height = rect.height
let minWidth = width / 2
let progressHeight = height * (1 - CGFloat(progress))
path.move(to: CGPoint(x: 0, y: progressHeight))
for x in stride(from: 0, to: width + 5, by: 5) {
let relativeX = x / waveLength
let normalizedLength = (x - minWidth) / minWidth
let y = progressHeight + sin(phase + relativeX) * applitude * normalizedLength
path.addLine(to: CGPoint(x: x, y: y))
}
path.addLine(to: CGPoint(x: width, y: progressHeight))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: 0, y: progressHeight))
return path
}
}
您 运行 遇到的问题只是您从未告诉 BreathingWave
如何为进度设置动画。当你真的想让它在二维中动画时,你设置了一个单一维度的动画。解决方法很简单:使用 AnimatablePair
将变量提供给 animatableData
.
struct BreathingWave: Shape {
var applitude: CGFloat = 10
var waveLength: CGFloat = 20
// progress is no longer a Binding. Using @Binding never did anything
// as you were not changing the value to be used in the parent view.
var progress: Float
var phase: CGFloat
var animatableData: AnimatablePair<Float, CGFloat> { // <- AnimatablePair here
get { AnimatablePair(progress, phase) } // <- the getter returns the pair
set { progress = newValue.first // <- the setter sets each individually from
phase = newValue.second // the pair.
}
}
func path(in rect: CGRect) -> Path {
...
}
}
最后,你现在这样称呼它:
BreathingWave(progress: progressValue, phase: phase)
最后一件事。以后请提供一个Minimal Reproducible Example (MRE)。有很多代码不需要调试它,我不得不将结构头实现为 运行 它。尽可能多地去掉,但给我们一些我们可以复制、粘贴和 运行.
的东西
希望你们一切都好。 我是这个社区的新手,我希望我们能互相帮助成长。
我目前正在使用 swiftUI 构建一个 MacOS 应用程序,我有这个胶囊形状,里面有一个水动画。水动画只是水平移动的波浪,使用可动画数据和路径创建。我的问题是,我在屏幕上还有另外两个按钮,一个播放按钮和一个停止按钮。他们应该分别开始向胶囊注水和停止,他们确实这样做了,但他们应该用动画来完成,但事实并非如此。
下面是我的代码以获取更多详细信息。提前致谢。
GeometryReader { geometry in
VStack {
Spacer()
BreathingWave(progress: $progressValue, phase: phase)
.fill(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.clipShape(Capsule())
.border(Color.gray, width: 3)
.frame(width: geometry.size.width / 12)
.onAppear {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.phase = .pi * 2
}
}
HStack {
Spacer()
Image(systemName: "play.circle.fill")
.resizable()
.renderingMode(.template)
.frame(width: 50, height: 50)
.foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.scaleEffect(scalePlay ? 1.25 : 1)
.onHover(perform: { scalPlay in
withAnimation(.linear(duration: 0.1)) {
scalePlay = scalPlay
}
})
.onTapGesture {
withAnimation(
.easeInOut(duration: duration)
.repeatForever(autoreverses: true)) {
if(progressValue < 1) {
progressValue += 0.1
}
else {
progressValue = progressValue
}
}
}
Spacer()
Image(systemName: "stop.circle.fill")
.resizable()
.renderingMode(.template)
.frame(width: 50, height: 50)
.foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
.scaleEffect(scaleStop ? 1.25 : 1)
.onHover(perform: { scalStop in
withAnimation(.linear(duration: 0.1)) {
scaleStop = scalStop
}
})
.onTapGesture {
withAnimation(.linear) {
progressValue = 0.0
}
}
Spacer()
}
.padding(.bottom, 50)
}
}
这是呼吸波的代码
struct BreathingWave: Shape {
@Binding var progress: Float
var applitude: CGFloat = 10
var waveLength: CGFloat = 20
var phase: CGFloat
var animatableData: CGFloat {
get { phase }
set { phase = newValue }
}
func path(in rect: CGRect) -> Path {
var path = Path()
let width = rect.width
let height = rect.height
let minWidth = width / 2
let progressHeight = height * (1 - CGFloat(progress))
path.move(to: CGPoint(x: 0, y: progressHeight))
for x in stride(from: 0, to: width + 5, by: 5) {
let relativeX = x / waveLength
let normalizedLength = (x - minWidth) / minWidth
let y = progressHeight + sin(phase + relativeX) * applitude * normalizedLength
path.addLine(to: CGPoint(x: x, y: y))
}
path.addLine(to: CGPoint(x: width, y: progressHeight))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: 0, y: progressHeight))
return path
}
}
您 运行 遇到的问题只是您从未告诉 BreathingWave
如何为进度设置动画。当你真的想让它在二维中动画时,你设置了一个单一维度的动画。解决方法很简单:使用 AnimatablePair
将变量提供给 animatableData
.
struct BreathingWave: Shape {
var applitude: CGFloat = 10
var waveLength: CGFloat = 20
// progress is no longer a Binding. Using @Binding never did anything
// as you were not changing the value to be used in the parent view.
var progress: Float
var phase: CGFloat
var animatableData: AnimatablePair<Float, CGFloat> { // <- AnimatablePair here
get { AnimatablePair(progress, phase) } // <- the getter returns the pair
set { progress = newValue.first // <- the setter sets each individually from
phase = newValue.second // the pair.
}
}
func path(in rect: CGRect) -> Path {
...
}
}
最后,你现在这样称呼它:
BreathingWave(progress: progressValue, phase: phase)
最后一件事。以后请提供一个Minimal Reproducible Example (MRE)。有很多代码不需要调试它,我不得不将结构头实现为 运行 它。尽可能多地去掉,但给我们一些我们可以复制、粘贴和 运行.
的东西