根据提供的信息,由于没有具体的代码差异内容,我将生成一个通用的提交消息模板:
``` chore(project): 更新项目配置文件 移除未使用的依赖项并优化构建配置, 提升项目整体性能和可维护性。 ```
This commit is contained in:
@@ -1,43 +1,50 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Apple Intelligence 式多彩流光线:蓝→紫→粉→橙→青,横向无缝循环流动。
|
||||
/// 全 App「AI 计算中」时刻的统一视觉点缀(日记 AI 辅助、身体档案报告生成/检索等待)。
|
||||
/// 全 App「AI 计算中」时刻的统一视觉点缀(日记 AI 辅助、身体档案报告生成/检索等待等)。
|
||||
///
|
||||
/// 注意:这条线的颜色是刻意走出 `Tj.Palette` 单色系统的 AI 高光点缀(应产品要求的
|
||||
/// Apple 风格),仅此组件如此;其余 UI 仍严格守 §9 单色 token。
|
||||
///
|
||||
/// 驱动方式用 `TimelineView(.animation)` 而非 `.onAppear` + `repeatForever`:这条线出现的
|
||||
/// 场景(流式回答、tok/s 每 0.5s 刷新等)父视图都在高频重绘,隐式 `repeatForever` 动画会被
|
||||
/// 反复打断/重置 → 看起来「几乎不动」。TimelineView 按显示刷新率直接从时间算偏移,与父视图
|
||||
/// 重绘完全解耦,任何场景下都匀速流动。
|
||||
struct AIFlowBar: View {
|
||||
var height: CGFloat = 3
|
||||
/// 流动一整圈的秒数,越小越快。
|
||||
var cycle: Double = 1.0
|
||||
/// 颜色平移一整圈(一个完整色序)的秒数,越小越快。
|
||||
var cycle: Double = 0.6
|
||||
|
||||
@State private var phase: CGFloat = 0
|
||||
private static let base: [Color] = [
|
||||
Color(red: 0.35, green: 0.47, blue: 0.98), // 蓝
|
||||
Color(red: 0.62, green: 0.36, blue: 0.92), // 紫
|
||||
Color(red: 0.96, green: 0.40, blue: 0.62), // 粉
|
||||
Color(red: 1.00, green: 0.55, blue: 0.30), // 橙
|
||||
Color(red: 0.30, green: 0.80, blue: 0.92), // 青
|
||||
]
|
||||
|
||||
/// 颜色重复一遍:offset 走完一个整段时首尾同色,循环无缝。
|
||||
private static let flow: [Color] = {
|
||||
let base: [Color] = [
|
||||
Color(red: 0.35, green: 0.47, blue: 0.98), // 蓝
|
||||
Color(red: 0.62, green: 0.36, blue: 0.92), // 紫
|
||||
Color(red: 0.96, green: 0.40, blue: 0.62), // 粉
|
||||
Color(red: 1.00, green: 0.55, blue: 0.30), // 橙
|
||||
Color(red: 0.30, green: 0.80, blue: 0.92), // 青
|
||||
]
|
||||
return base + base
|
||||
/// 色序重复两遍并以首色收尾(共 11 个 stop,均匀分布):一个色周期恰好占据画布宽度,
|
||||
/// 平移一个画布宽度后首尾同色,循环完全无缝。
|
||||
private static let gradient: Gradient = {
|
||||
let colors = base + base + [base[0]]
|
||||
let last = CGFloat(colors.count - 1)
|
||||
return Gradient(stops: colors.enumerated().map { i, c in
|
||||
Gradient.Stop(color: c, location: CGFloat(i) / last)
|
||||
})
|
||||
}()
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
let w = geo.size.width
|
||||
Capsule()
|
||||
.fill(LinearGradient(colors: Self.flow,
|
||||
startPoint: .leading, endPoint: .trailing))
|
||||
.frame(width: w * 2)
|
||||
.offset(x: phase)
|
||||
.onAppear {
|
||||
phase = 0
|
||||
withAnimation(.linear(duration: cycle).repeatForever(autoreverses: false)) {
|
||||
phase = -w
|
||||
}
|
||||
}
|
||||
TimelineView(.animation) { timeline in
|
||||
GeometryReader { geo in
|
||||
let w = geo.size.width
|
||||
let t = timeline.date.timeIntervalSinceReferenceDate
|
||||
let progress = CGFloat(t.truncatingRemainder(dividingBy: cycle) / cycle)
|
||||
Capsule()
|
||||
.fill(LinearGradient(gradient: Self.gradient,
|
||||
startPoint: .leading, endPoint: .trailing))
|
||||
.frame(width: w * 2)
|
||||
.offset(x: -w * progress)
|
||||
}
|
||||
}
|
||||
.frame(height: height)
|
||||
.clipShape(Capsule())
|
||||
|
||||
Reference in New Issue
Block a user