Сделать плавную анимацию кнопки send

This commit is contained in:
2026-03-02 19:56:09 +05:00
parent 8238fd1940
commit 003c262378

View File

@@ -75,6 +75,17 @@ struct ChatDetailView: View {
!messageText.isEmpty !messageText.isEmpty
} }
private var sendButtonProgress: CGFloat {
shouldShowSendButton ? 1 : 0
}
private var micButtonProgress: CGFloat {
shouldShowSendButton ? 0 : 1
}
private var sendButtonWidth: CGFloat { 38 }
private var sendButtonHeight: CGFloat { 36 }
private var composerHorizontalPadding: CGFloat { private var composerHorizontalPadding: CGFloat {
isInputFocused ? 16 : 28 isInputFocused ? 16 : 28
} }
@@ -337,7 +348,7 @@ private extension ChatDetailView {
.padding(.horizontal, 16) .padding(.horizontal, 16)
} }
HStack(alignment: .bottom, spacing: 6) { HStack(alignment: .bottom, spacing: shouldShowSendButton ? 0 : 6) {
Button { Button {
// Placeholder for attachment picker // Placeholder for attachment picker
} label: { } label: {
@@ -366,7 +377,7 @@ private extension ChatDetailView {
.padding(.bottom, 8) .padding(.bottom, 8)
.frame(maxWidth: .infinity, minHeight: 36, alignment: .bottomLeading) .frame(maxWidth: .infinity, minHeight: 36, alignment: .bottomLeading)
HStack(alignment: .center, spacing: 8) { HStack(alignment: .center, spacing: 0) {
Button { Button {
// Placeholder for quick actions // Placeholder for quick actions
} label: { } label: {
@@ -381,10 +392,9 @@ private extension ChatDetailView {
.accessibilityLabel("Quick actions") .accessibilityLabel("Quick actions")
.buttonStyle(ChatDetailGlassPressButtonStyle()) .buttonStyle(ChatDetailGlassPressButtonStyle())
} }
.padding(.trailing, 8) .padding(.trailing, 8 + (sendButtonWidth * sendButtonProgress))
.frame(height: 36, alignment: .center) .frame(height: 36, alignment: .center)
.overlay(alignment: .trailing) {
if shouldShowSendButton {
Button(action: sendCurrentMessage) { Button(action: sendCurrentMessage) {
ZStack { ZStack {
// Mirrors the layered blend stack from the original SVG icon. // Mirrors the layered blend stack from the original SVG icon.
@@ -431,8 +441,10 @@ private extension ChatDetailView {
.blendMode(.overlay) .blendMode(.overlay)
} }
.compositingGroup() .compositingGroup()
.opacity(0.42 + (0.58 * sendButtonProgress))
.scaleEffect(0.72 + (0.28 * sendButtonProgress))
.frame(width: 22, height: 19) .frame(width: 22, height: 19)
.frame(width: 44, height: 36) .frame(width: sendButtonWidth, height: sendButtonHeight)
.background { .background {
Capsule().fill(Color(hex: 0x008BFF)) Capsule().fill(Color(hex: 0x008BFF))
} }
@@ -440,7 +452,18 @@ private extension ChatDetailView {
.accessibilityLabel("Send") .accessibilityLabel("Send")
.disabled(!canSend) .disabled(!canSend)
.buttonStyle(ChatDetailGlassPressButtonStyle()) .buttonStyle(ChatDetailGlassPressButtonStyle())
.transition(.move(edge: .trailing).combined(with: .opacity)) .allowsHitTesting(shouldShowSendButton)
.opacity(Double(sendButtonProgress))
.scaleEffect(0.74 + (0.26 * sendButtonProgress), anchor: .trailing)
.blur(radius: (1 - sendButtonProgress) * 2.1)
.mask(
Capsule()
.frame(
width: max(0.001, sendButtonWidth * sendButtonProgress),
height: max(0.001, sendButtonHeight * sendButtonProgress)
)
.frame(width: sendButtonWidth, height: sendButtonHeight, alignment: .trailing)
)
} }
} }
.padding(3) .padding(3)
@@ -451,7 +474,6 @@ private extension ChatDetailView {
) )
} }
if !shouldShowSendButton {
Button(action: trailingAction) { Button(action: trailingAction) {
TelegramVectorIcon( TelegramVectorIcon(
pathData: TelegramIconPath.microphone, pathData: TelegramIconPath.microphone,
@@ -464,8 +486,16 @@ private extension ChatDetailView {
} }
.accessibilityLabel("Voice message") .accessibilityLabel("Voice message")
.buttonStyle(ChatDetailGlassPressButtonStyle()) .buttonStyle(ChatDetailGlassPressButtonStyle())
.transition(.move(edge: .trailing).combined(with: .opacity)) .allowsHitTesting(!shouldShowSendButton)
} .opacity(Double(micButtonProgress))
.scaleEffect(
x: max(0.001, 0.42 + (0.58 * micButtonProgress)),
y: 0.78 + (0.22 * micButtonProgress),
anchor: .trailing
)
.blur(radius: (1 - micButtonProgress) * 2.4)
.frame(width: 42 * micButtonProgress, height: 42, alignment: .trailing)
.clipped()
} }
.padding(.horizontal, composerHorizontalPadding) .padding(.horizontal, composerHorizontalPadding)
.padding(.top, 4) .padding(.top, 4)