• 作者:老汪软件技巧
  • 发表时间:2024-09-29 00:02
  • 浏览量:

有时候需要边框有线条流动的动画效果,接下来几篇文章会来探讨下不同的实现方式。本文主要是介绍第二种方式mask。

/// Masks this view using the alpha channel of the given view.
public func mask<Mask>(_ mask: Mask) -> some View where Mask : View

mask是用另一个视图来给当前视图创建遮罩。只有 mask 中 Alpha 值大于 0 的区域,目标视图才会显示出来,完全透明的部分则不会显示。所以一般会用于对原视图按遮罩视图进行形状裁剪或者可见区域的限制。

形状

借助于SwiftUI已有的各种形状视图,可以很容易创造各种形状的遮罩,如下创建了一个圆形的遮罩:

Rectangle()
	.fill(.cyan)
    .frame(width: 200, height: 200)
    .mask {
     	Circle()
     }

还可以通过调节遮罩的大小,位置等等创建一些动画效果:

 AsyncImage(url: URL(string: "https://images.pexels.com/photos/417074/pexels-photo-417074.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2")) { image in
 	image.image?.resizable()
		.scaledToFill()
  		.frame(width: 400)
	}
	.mask {
		Circle()
		.frame(width: isExpanded ? 500 : 100, height: isExpanded ? 500 : 100)
	}
                
Button(isExpanded ? "Collapse" : "Expand") {
 	withAnimation {
		isExpanded.toggle()
	}
}

文字

比如可以通过添加文字遮罩,并且让文字遮罩从左向右移动从而实现一个光线从左到右滑过文字的效果:

Text("SwiftUI")
    .font(.system(size: 60, weight: .bold))
    .foregroundColor(.black)
    .padding()
    .overlay(
        LinearGradient(gradient: Gradient(colors: [.yellow, .white, .yellow]),
                       startPoint: .leading, endPoint: .trailing)
            .frame(width: 300, height: 100)
            .offset(x: animationOffset)
            .mask(
                Text("SwiftUI")
                    .font(.system(size: 60, weight: .bold))
            )
    )
    .onAppear {
        withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
            animationOffset = 300
        }
    }

渐变

使用透明度渐变的mask会让目标视图也呈现渐隐/渐现的效果:

Text("SwiftUI")
	.font(.system(size: 60, weight: .bold))
	.foregroundStyle(.indigo)
 	.mask {
		LinearGradient(colors: [.blue, .clear], startPoint: .leading, endPoint: .trailing)
 	}

css实现边框动态环绕效果_边框动态_

使用带有透明度的视图作为mask后,目标视图也会带有透明度,比如下面这段代码:

ZStack {
  Text("SwiftUI")
    .font(.system(size: 60, weight: .bold))
    .foregroundStyle(.red)
  AsyncImage(url: URL(string:"https://images.pexels.com/photos/417074/pexels-photo-417074.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
    )
  ) {
    image in
    image.image?.resizable()
      .scaledToFill()
      .frame(width: 400)
  }
  .mask {
    LinearGradient(colors: [.red, .clear], startPoint: .leading, endPoint: .trailing)
  }
}

在没加mask之前,下面的文字是会被这张JPG图片遮住的,但是在应用了带透明度的mask后实际效果会如下图:

在结合AngularGradient后还可以生成如下的效果:

Text("SwiftUI")
  .font(.system(size: 60, weight: .bold))
  .foregroundStyle(.indigo)
  .mask(
    AngularGradient(
      stops: [
        .init(color: .blue, location: 0),
        .init(color: .blue, location: position),
        .init(color: .clear, location: position),
        .init(color: .clear, location: 1),
      ], center: .center, startAngle: .zero, endAngle: .degrees(360)))
Slider(value: $position, in: 0...1) {}

当然,还有很多很多酷炫的效果,感兴趣的同学可以自行去查阅下。接下来回到这篇文章的主题上来,通过mask创建可以动的边框。

边框动效

创意来自油管上的这个视频,对于需要在一些矩形的卡片上应用边框动效的场景比较有用。用这种方式可以产生下面的一些效果(当然通过位移等变化还会有更多的效果):

这个创意的思路是在卡片上覆盖一个更大的卡片,让卡片旋转,然后再用遮罩按卡片的大小裁剪一个边框出来,就会形成边框流动的效果,代码分别如下:

struct MaskBorderAnimation1: View {
    @State private var angle: CGFloat = 0
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 20)
                .fill(.mint)
                .frame(width: 200, height: 200)
            
            RoundedRectangle(cornerRadius: 20)
                .foregroundStyle(.linearGradient(colors: [.cyan, .indigo, .orange, .brown, .red, .blue], startPoint: .top, endPoint: .bottom))
                .frame(width: 300, height: 300)
                .rotationEffect(.degrees(angle))
                .mask {
                    RoundedRectangle(cornerRadius: 20)
                        .stroke(.red, lineWidth: 5)
                        .frame(width: 200, height: 200)
                        
                }
                .animation(.linear(duration: 2).repeatForever(autoreverses: false), value: angle)
        }
        .onAppear {
            angle = 360
        }
    }
}
struct MaskBorderAnimation2: View {
    @State private var angle: CGFloat = 0
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 20)
                .fill(.mint)
                .frame(width: 200, height: 200)
            
            RoundedRectangle(cornerRadius: 20)
                .foregroundStyle(.linearGradient(colors: [.cyan, .indigo, .orange, .brown, .red, .blue], startPoint: .top, endPoint: .bottom))
                .frame(width: 100, height: 300)
                .offset(x: 100)
                .rotationEffect(.degrees(angle))
                .mask {
                    RoundedRectangle(cornerRadius: 20)
                        .stroke(.red, lineWidth: 5)
                        .frame(width: 200, height: 200)
                        
                }
                .animation(.linear(duration: 2).repeatForever(autoreverses: false), value: angle)
        }
        .onAppear {
            angle = 360
        }
    }
}
struct MaskBorderAnimation3: View {
    @State private var angle: CGFloat = 0
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 20)
                .fill(.mint)
                .frame(width: 200, height: 200)
            
            RoundedRectangle(cornerRadius: 20)
                .foregroundStyle(.linearGradient(colors: [.cyan, .indigo, .orange, .brown, .red, .blue], startPoint: .top, endPoint: .bottom))
                .frame(width: 170, height: 300)
                .rotationEffect(.degrees(angle))
                .mask {
                    RoundedRectangle(cornerRadius: 20)
                        .stroke(.red, lineWidth: 5)
                        .frame(width: 200, height: 200)
                        
                }
                .animation(.linear(duration: 2).repeatForever(autoreverses: false), value: angle)
        }
        .onAppear {
            angle = 360
        }
    }
}

总结

本文主要介绍了mask的基本原理和用mask来实现的一些常见效果。最后介绍了用mask来实现边框动效的实现思路。可见用mask还是可以做出很多有意思的动效的。

SwiftUI:让你的边框动起来(一)——dashPhase