Initial commit
This commit is contained in:
71
WhiteNights/Utils/MarqueeText.swift
Normal file
71
WhiteNights/Utils/MarqueeText.swift
Normal file
@ -0,0 +1,71 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MarqueeText: View {
|
||||
let text: String
|
||||
let font: Font
|
||||
let foregroundColor: Color
|
||||
private let speed: CGFloat = 10 // пикселей в секунду
|
||||
|
||||
@State private var textWidth: CGFloat = 0
|
||||
@State private var containerWidth: CGFloat = 0
|
||||
@State private var offset: CGFloat = 0
|
||||
@State private var animationStarted = false
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
Text(text)
|
||||
.font(font)
|
||||
.foregroundColor(foregroundColor)
|
||||
.lineLimit(1)
|
||||
.fixedSize() // важно, чтобы не обрезался
|
||||
.background(WidthGetter())
|
||||
.offset(x: offset)
|
||||
.onAppear {
|
||||
containerWidth = geo.size.width
|
||||
if textWidth > containerWidth, !animationStarted {
|
||||
animationStarted = true
|
||||
let distance = textWidth - containerWidth
|
||||
let duration = Double(distance / speed)
|
||||
withAnimation(.linear(duration: duration).repeatForever(autoreverses: true)) {
|
||||
offset = -distance
|
||||
}
|
||||
}
|
||||
}
|
||||
.clipped()
|
||||
}
|
||||
.frame(height: lineHeight(for: font))
|
||||
.onPreferenceChange(WidthKey.self) { width in
|
||||
textWidth = width
|
||||
}
|
||||
}
|
||||
|
||||
private func lineHeight(for font: Font) -> CGFloat {
|
||||
switch font {
|
||||
case .largeTitle: return 34
|
||||
case .title: return 28
|
||||
case .title2: return 22
|
||||
case .title3: return 20
|
||||
case .headline: return 17
|
||||
case .body: return 17
|
||||
case .callout: return 16
|
||||
case .subheadline: return 15
|
||||
case .caption: return 13
|
||||
case .caption2: return 12
|
||||
default: return 17
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: — Helpers для измерения ширины текста
|
||||
private struct WidthKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = 0
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() }
|
||||
}
|
||||
|
||||
private struct WidthGetter: View {
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
Color.clear.preference(key: WidthKey.self, value: geo.size.width)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user