技术:SwiftUI、SwiftUI3.0、喝水进度、喝水App、喝水进度动画
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
SwiftUI搭建一个类似喝水App的喝水进度动画效果
概述
SwiftUI搭建一个类似喝水App的喝水进度动画效果
详细
一、运行效果
二、项目结构图
三、程序实现 - 过程
思路:
- 先搭建头部的基本信息
- 通过加载水滴的系统图片
- 画路径 - 通过正弦公式 画出一个水纹的弧度
画出下面的效果
- 通过mask 进行遮盖超出范围 - 使用给定视图的 Alpha 通道屏蔽此视图。
- 通过动画 并且每个两秒重复动画 达到类似 日常生活中的水遇到风的
水纹波动效果
- 画几个小透明点 - 类似水的气泡
- 通过添加按钮 控制水滴的进度
1.创建一个项目命名为 WaterWave
1.1.引入资源文件和颜色
颜色
BG#F1F3F9
Green#4747EB
图片1张
2. 创建一个虚拟文件New Group
命名为 View
4. 创建一个文件New File
选择SwiftUI View
类型 命名为Home
Code
ContentView - 主窗口
主要是展示主窗口
Home
//
// ContentView.swift
// Shared
//
// Created by lyh on 2022/8/29.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Home - 主页
思路
- 先搭建头部的基本信息
- 通过加载水滴的系统图片
- 画路径 - 通过正弦公式 画出一个水纹的弧度
画出下面的效果
- 通过mask 进行遮盖超出范围 - 使用给定视图的 Alpha 通道屏蔽此视图。
- 通过动画 并且每个两秒重复动画 达到类似 日常生活中的水遇到风的
水纹波动效果
- 画几个小透明点 - 类似水的气泡
- 通过添加按钮 控制水滴的进度
//
// Home.swift
// WaterWave (iOS)
//
// Created by lyh on 2022/8/29.
//
import SwiftUI
struct Home: View {
@State var progress : CGFloat = 0.5
@State var startAnimation: CGFloat = 0
var body: some View {
VStack{
Image("Pic")
.resizable()
.aspectRatio(contentMode:.fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
.padding(10)
.background(.white,in:Circle())
Text("宇夜iOS")
.fontWeight(.semibold)
.foregroundColor(.gray)
.padding(.bottom,30)
// MARK : Ware Form
GeometryReader{
proxy in
let size = proxy.size
ZStack{
// MARK:Water Drop
// 水滴
Image(systemName: "drop.fill")
.resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.foregroundColor(.white)
// Streching in X Axis
// X轴拉伸
.scaleEffect(x:1.1,y:1)
.offset(y:-1)
// Wave Form Shape
// 波形的形状
WaterWave(progress: progress, waveHeight: 0.1, offset: startAnimation)
.fill(Color("Blue"))
// Water Drops
// 水滴
.overlay(content: {
ZStack{
Circle()
.fill(.white.opacity(0.1))
.frame(width: 15, height: 15)
.offset(x:-20)
Circle()
.fill(.white.opacity(0.1))
.frame(width: 15, height: 15)
.offset(x:40,y:30)
Circle()
.fill(.white.opacity(0.1))
.frame(width: 25, height: 25)
.offset(x:-30,y:80)
Circle()
.fill(.white.opacity(0.1))
.frame(width: 25, height: 25)
.offset(x:50,y:70)
Circle()
.fill(.white.opacity(0.1))
.frame(width: 10, height: 10)
.offset(x:40,y:100)
Circle()
.fill(.white.opacity(0.1))
.frame(width: 10, height: 10)
.offset(x:-40,y:50)
}
})
// Masking into Drop Shape
// 掩蔽成水滴形状
.mask{
Image(systemName: "drop.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(20)
}
// Add Button
.overlay(alignment: .bottom) {
Button{
progress += 0.01
}label: {
Image(systemName: "plus")
.font(.system(size: 40,weight:.black))
.foregroundColor((Color("Blue")))
.shadow(radius: 2)
.padding(25)
.background(.white,in : Circle())
}
.offset(y:40)
}
}
.frame(width: size.width, height: size.height, alignment: .center)
.onAppear{
// Lopping Animation
// 每隔2秒进行动画
withAnimation(.linear(duration: 2).repeatForever(autoreverses: false)){
startAnimation = size.width
}
}
}
.frame(height:350)
.padding(.bottom,50)
// 测试
// Slider(value: $progress)
}
.padding()
.frame(maxWidth:.infinity,maxHeight: .infinity,alignment: .top)
.background(Color("BG"))
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// Shape形状
struct WaterWave: Shape {
var progress : CGFloat
// Wave Height
var waveHeight : CGFloat
// Intial Aniamtion Start
var offset : CGFloat
// Enabling Aniamtion
var animatableData: CGFloat{
get {
offset}
set {
offset = newValue}
}
func path(in rect: CGRect) -> Path {
return Path{
path in
path.move(to:.zero)
// MARK: Drawing Waves using Sine
// 用正弦画波浪
let progressHeight: CGFloat = (1 - progress) * rect.height
let height = waveHeight * rect.height
for value in stride(from: 0, to: rect.width, by: 2){
let x : CGFloat = value
let sine : CGFloat = sin(Angle(degrees: value + offset).radians)
let y : CGFloat = progressHeight + (height * sine)
path.addLine(to: CGPoint(x: x, y: y))
// Bottom Portion
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
}
}
}
}