下面是我的代码,我试图解决的问题是圆形形状中的图像看起来像平铺背景,当形状围绕其移动时固定在适当的位置,而我想要的结果是形状本身实际上是个人资料图片,图像随着形状的移动而移动,与固定在位置上的背景不同。
如果我尝试使用 ClipShape(CustomCircleShape...
我很困惑如何将圆圈及其运动转化为图像。
struct WaveAnimationView: View {
@State private var screenCoveragePercent = 45.0
@State private var waveOffset = Angle(degrees: 0)
var body: some View {
ZStack {
let shape = CustomCircleShape(offSet: Angle(degrees: waveOffset.degrees), percent: screenCoveragePercent)
shape.fill(ImagePaint(image: Image("ryan"), sourceRect: CGRect(x: 0, y: 0, width: 1, height: 1), scale: 0.1)) .ignoresSafeArea(.all)
}
.onAppear {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.waveOffset = Angle(degrees: 360)
}
}
}
}
struct CustomCircleShape: Shape {
var offSet: Angle
var percent: Double
var animatableData: Double {
get { offSet.degrees }
set { offSet = Angle(degrees: newValue) }
}
func path(in rect: CGRect) -> Path {
var p = Path()
let lowestWave = 0.02
let highestWave = 1.00
let newPercent = lowestWave + (highestWave - lowestWave) * (percent / 100)
let waveHeight = 0.0020 * rect.height
let yOffSet = CGFloat(1 - newPercent) * (rect.height - 4 * waveHeight) + 2 * waveHeight
let startAngle = offSet
let endAngle = offSet + Angle(degrees: 360 + 10)
var circleCenter = CGPoint.zero
for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) {
let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width
let y = yOffSet + waveHeight * CGFloat(sin(Angle(degrees: angle).radians))
if x == rect.midX {
circleCenter = CGPoint(x: x, y: y)
}
}
if circleCenter == .zero {
circleCenter = CGPoint(x: rect.midX, y: yOffSet + waveHeight * CGFloat(sin(Angle(degrees: offSet.degrees + 180).radians)))
}
let circleRadius: CGFloat = 50
p.addEllipse(in: CGRect(x: circleCenter.x - circleRadius, y: circleCenter.y - circleRadius, width: 2 * circleRadius, height: 2 * circleRadius))
return p
}
}
编辑:下面是显示波浪动画的代码,其中圆圈(我想成为一个图像)正在模仿波浪的运动。
struct WaveAnimation: View {
@State private var screenCoveragePercent = 25.0
@State private var waveOffset = Angle(degrees: 0)
var body: some View {
ZStack {
let opacityOfWater = 0.25
Wave(offSet: Angle(degrees: waveOffset.degrees), percent: screenCoveragePercent)
.fill(Color.blue).opacity(opacityOfWater)
.ignoresSafeArea(.all)
CustomCircle(offSet: Angle(degrees: waveOffset.degrees), percent: screenCoveragePercent)
.fill(Color.blue).opacity(1.0)
.ignoresSafeArea(.all)
InvisibleSlider(percent: $screenCoveragePercent)
}
.onAppear {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.waveOffset = Angle(degrees: 360)
}
}
}
}
struct CustomCircle: Shape {
var offSet: Angle
var percent: Double
var animatableData: Double {
get { offSet.degrees }
set { offSet = Angle(degrees: newValue) }
}
func path(in rect: CGRect) -> Path {
var p = Path()
let lowestWave = 0.02
let highestWave = 1.00
let newPercent = lowestWave + (highestWave - lowestWave) * (percent / 100)
let waveHeight = 0.0020 * rect.height
let yOffSet = CGFloat(1 - newPercent) * (rect.height - 4 * waveHeight) + 2 * waveHeight
let startAngle = offSet
let endAngle = offSet + Angle(degrees: 360 + 10)
var circleCenter = CGPoint.zero
for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) {
let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width
let y = yOffSet + waveHeight * CGFloat(sin(Angle(degrees: angle).radians))
if x == rect.midX {
circleCenter = CGPoint(x: x, y: y)
}
}
if circleCenter == .zero {
circleCenter = CGPoint(x: rect.midX, y: yOffSet + waveHeight * CGFloat(sin(Angle(degrees: offSet.degrees + 180).radians)))
}
let circleRadius: CGFloat = 50
p.addEllipse(in: CGRect(x: circleCenter.x - circleRadius, y: circleCenter.y - circleRadius, width: 2 * circleRadius, height: 2 * circleRadius))
return p
}
}
struct Wave: Shape {
var offSet: Angle
var percent: Double
var animatableData: Double {
get { offSet.degrees }
set { offSet = Angle(degrees: newValue) }
}
func path(in rect: CGRect) -> Path {
var p = Path()
let lowestWave = 0.02
let highestWave = 1.00
let newPercent = lowestWave + (highestWave - lowestWave) * (percent / 100)
let waveHeight = 0.0020 * rect.height
let yOffSet = CGFloat(1 - newPercent) * (rect.height - 4 * waveHeight) + 2 * waveHeight
let startAngle = offSet
let endAngle = offSet + Angle(degrees: 360 + 10)
// Draw wave
p.move(to: CGPoint(x: 0, y: yOffSet + waveHeight * CGFloat(sin(offSet.radians))))
for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) {
let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width
let y = yOffSet + waveHeight * CGFloat(sin(Angle(degrees: angle).radians))
p.addLine(to: CGPoint(x: x, y: y))
}
p.addLine(to: CGPoint(x: rect.width, y: rect.height))
p.addLine(to: CGPoint(x: 0, y: rect.height))
p.closeSubpath()
return p
}
}
您的代码正在对形状相对于其边界(参数)的路径进行动画处理。
rect
您应该创建一个
Image(...).clipShape(Circle())
,并向.offset(y: ...)
其中添加一个 。然后您可以为整个图像的 y 偏移设置动画。我会进行设计
Wave
,使其填充整个给定的rect
. 即删除percent
.Spacer
可以使用a 中的调整波浪覆盖的空间大小VStack
。现在我们把图像放在它上面。图像的 y 偏移量是通过数学计算得出的。查看 的实现如何
yOffset
对应于 的实现Wave.path(in:)
。最后,将所有内容放在一起。