Initial commit

This commit is contained in:
15lu.akari
2025-08-24 14:44:50 +03:00
parent dca1ae410b
commit 5a583d9415
50 changed files with 2019 additions and 17 deletions

View File

@ -0,0 +1,13 @@
import SwiftUI
extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xFF)/255,
green: Double((hex >> 8) & 0xFF)/255,
blue: Double(hex & 0xFF)/255,
opacity: alpha
)
}
}

View 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)
}
}
}

View File

@ -0,0 +1,17 @@
import SwiftUI
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape(RoundedCorner(radius: radius, corners: corners))
}
}
private struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}

View File

@ -0,0 +1,39 @@
import SwiftUI
struct BlockStyle: ViewModifier {
// Добавляем свойство для хранения радиуса скругления
let cornerRadius: CGFloat
func body(content: Content) -> some View {
content
.background(
ZStack {
Color(hex: 0x806C59)
LinearGradient(
stops: [
.init(color: .white.opacity(0.0), location: 0.0871),
.init(color: .white.opacity(0.16), location: 0.6969)
],
startPoint: .bottomLeading,
endPoint: .topTrailing
)
}
.cornerRadius(cornerRadius) // Применяем скругление к фону
)
.shadow(
color: Color.black.opacity(0.10),
radius: 8,
x: 0,
y: 2
)
// Применяем скругление к содержимому (опционально, но лучше для теней)
.cornerRadius(cornerRadius)
}
}
extension View {
// Изменяем расширение, чтобы оно принимало параметр cornerRadius
func blockStyle(cornerRadius: CGFloat) -> some View {
modifier(BlockStyle(cornerRadius: cornerRadius))
}
}