mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Various improvements
This commit is contained in:
parent
ea2c1e24af
commit
1d20927877
@ -382,9 +382,10 @@ public final class GiftCompositionComponent: Component {
|
||||
container.frame.origin.x = floor((availableSize.width - visualSize.width) / 2.0)
|
||||
|
||||
container.layer.animatePosition(
|
||||
from: CGPoint(x: -containerWidth - visualSize.width * 0.5 + containerWidth / 2.0 - self.spacingX - 70.0, y: container.frame.center.y),
|
||||
from: CGPoint(x: -containerWidth - visualSize.width * 0.5 + containerWidth / 2.0 - self.spacingX - 120.0, y: container.frame.center.y),
|
||||
to: CGPoint(x: container.frame.center.x, y: container.frame.center.y),
|
||||
duration: self.maxAnimDuration,
|
||||
delay: 0.05,
|
||||
timingFunction: kCAMediaTimingFunctionSpring,
|
||||
completion: { [weak self] _ in
|
||||
guard let self, let container = self.decelContainer else {
|
||||
@ -396,8 +397,7 @@ public final class GiftCompositionComponent: Component {
|
||||
)
|
||||
|
||||
self.spinState = .decelerating
|
||||
self.spinLink?.invalidate()
|
||||
self.spinLink = nil
|
||||
self.ensureDisplayLink()
|
||||
}
|
||||
|
||||
private func handleDecelArrived(container: UIView, iconSize: CGSize, visualSize: CGSize) {
|
||||
@ -506,14 +506,91 @@ public final class GiftCompositionComponent: Component {
|
||||
self.componentState?.updated(transition: .easeInOut(duration: 0.25))
|
||||
self.component?.requestUpdate(.easeInOut(duration: 0.25))
|
||||
}
|
||||
self.applyEdge3DHorizontal()
|
||||
case .decelerating:
|
||||
break
|
||||
self.applyEdge3DHorizontal()
|
||||
case .idle, .settled:
|
||||
self.spinLink?.invalidate()
|
||||
self.spinLink = nil
|
||||
}
|
||||
}
|
||||
|
||||
private let minScaleAtEdgeX: CGFloat = 0.7
|
||||
private let yawAtEdgeDegrees: CGFloat = 25.0
|
||||
private let edgeFalloffX: CGFloat = 0.25
|
||||
|
||||
@inline(__always)
|
||||
private func smoothstep01(_ x: CGFloat) -> CGFloat {
|
||||
let t = max(0.0, min(1.0, x))
|
||||
return t * t * (3.0 - 2.0 * t)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func liveMidX(in container: UIView, of view: UIView) -> CGFloat {
|
||||
if let pres = view.layer.presentation() {
|
||||
let p = container.layer.convert(pres.position, from: view.layer.superlayer)
|
||||
return p.x
|
||||
}
|
||||
return view.center.x
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func midXInsideAnimatedContainer(in selfView: UIView, container: UIView, hostView: UIView) -> CGFloat {
|
||||
let contPres = container.layer.presentation() ?? container.layer
|
||||
let hostPres = hostView.layer.presentation() ?? hostView.layer
|
||||
|
||||
let hostOffsetFromContainerCenter = hostPres.position.x - container.bounds.midX
|
||||
return contPres.position.x + hostOffsetFromContainerCenter
|
||||
}
|
||||
|
||||
private func edge3DTransformFor(midX: CGFloat, containerWidth: CGFloat, baseScale: CGFloat = 1.0) -> CATransform3D {
|
||||
guard containerWidth > 0 else {
|
||||
return CATransform3DMakeScale(baseScale, baseScale, 1.0)
|
||||
}
|
||||
let d = abs((midX - containerWidth * 0.5) / (containerWidth * 0.5))
|
||||
|
||||
let uRaw = (d - edgeFalloffX) / (1.0 - edgeFalloffX)
|
||||
let u = smoothstep01(max(0.0, min(1.0, uRaw)))
|
||||
|
||||
let scale = (1.0 - u) * baseScale + (minScaleAtEdgeX * baseScale) * u
|
||||
let yawSign: CGFloat = (midX < containerWidth * 0.5) ? 1.0 : -1.0
|
||||
let yawRadians = (yawAtEdgeDegrees * .pi / 180.0) * u * yawSign
|
||||
|
||||
var t = CATransform3DIdentity
|
||||
t = CATransform3DRotate(t, yawRadians, 0.0, 1.0, 0.0)
|
||||
t = CATransform3DScale(t, scale, scale, 1.0)
|
||||
return t
|
||||
}
|
||||
|
||||
private func applyEdge3DHorizontal() {
|
||||
let containerWidth = self.bounds.width
|
||||
guard containerWidth > 0.0 else { return }
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
|
||||
for w in self.activeWrappers {
|
||||
guard w.superview === self else { continue }
|
||||
let midX = liveMidX(in: self, of: w)
|
||||
if let host = w.subviews.first {
|
||||
let baseScale = max(0.01, w.bounds.width / max(host.bounds.width, 0.01))
|
||||
host.layer.transform = edge3DTransformFor(midX: midX, containerWidth: containerWidth, baseScale: baseScale)
|
||||
}
|
||||
}
|
||||
|
||||
for hostView in self.decelItemHosts {
|
||||
guard let container = self.decelContainer, hostView.superview === container && hostView !== self.decelItemHosts.first else {
|
||||
continue
|
||||
}
|
||||
let midX = midXInsideAnimatedContainer(in: self, container: container, hostView: hostView)
|
||||
if let host = hostView.subviews.first {
|
||||
let baseScale = max(0.01, hostView.bounds.width / max(host.bounds.width, 0.01))
|
||||
host.layer.transform = edge3DTransformFor(midX: midX, containerWidth: containerWidth, baseScale: baseScale)
|
||||
}
|
||||
}
|
||||
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
public func update(component: GiftCompositionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
|
@ -1549,15 +1549,15 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
Queue.mainQueue().after(1.5) {
|
||||
Queue.mainQueue().after(1.2) {
|
||||
self.revealedAttributes.insert(.backdrop)
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
Queue.mainQueue().after(1.0) {
|
||||
Queue.mainQueue().after(0.7) {
|
||||
self.revealedAttributes.insert(.pattern)
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
Queue.mainQueue().after(1.0) {
|
||||
Queue.mainQueue().after(0.7) {
|
||||
self.revealedAttributes.insert(.model)
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
@ -2648,31 +2648,43 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: "Collectible #", font: textFont, color: .white, tintColor: textColor)))
|
||||
]
|
||||
|
||||
let numberFont = Font.with(size: 13.0, traits: .monospacedNumbers)
|
||||
let spinningItems: [AnyComponentWithIdentity<Empty>] = [
|
||||
AnyComponentWithIdentity(id: "0", component: AnyComponent(Text(text: "0", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "1", component: AnyComponent(Text(text: "1", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "2", component: AnyComponent(Text(text: "2", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "3", component: AnyComponent(Text(text: "3", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "4", component: AnyComponent(Text(text: "4", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "5", component: AnyComponent(Text(text: "5", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "6", component: AnyComponent(Text(text: "6", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "7", component: AnyComponent(Text(text: "7", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "8", component: AnyComponent(Text(text: "8", font: textFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "9", component: AnyComponent(Text(text: "9", font: textFont, color: textColor)))
|
||||
AnyComponentWithIdentity(id: "0", component: AnyComponent(Text(text: "0", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "1", component: AnyComponent(Text(text: "1", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "2", component: AnyComponent(Text(text: "2", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "3", component: AnyComponent(Text(text: "3", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "4", component: AnyComponent(Text(text: "4", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "5", component: AnyComponent(Text(text: "5", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "6", component: AnyComponent(Text(text: "6", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "7", component: AnyComponent(Text(text: "7", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "8", component: AnyComponent(Text(text: "8", font: numberFont, color: textColor))),
|
||||
AnyComponentWithIdentity(id: "9", component: AnyComponent(Text(text: "9", font: numberFont, color: textColor)))
|
||||
]
|
||||
if let numberValue = uniqueGift?.number {
|
||||
let numberString = "\(numberValue)"
|
||||
let numberString = formatCollectibleNumber(numberValue, dateTimeFormat: environment.dateTimeFormat)
|
||||
var i = 0
|
||||
var index = 0
|
||||
for c in numberString {
|
||||
items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(SlotsComponent(
|
||||
item: AnyComponent(Text(text: String(c), font: textFont, color: .white)),
|
||||
items: spinningItems,
|
||||
isAnimating: i > state.revealedNumberDigits,
|
||||
tintColor: textColor,
|
||||
verticalOffset: -1.0 - UIScreenPixel,
|
||||
motionBlur: false,
|
||||
size: CGSize(width: 8.0, height: 14.0))))
|
||||
)
|
||||
let s = String(c)
|
||||
if s == "\u{00A0}" {
|
||||
items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(Text(text: s, font: textFont, color: .white, tintColor: textColor)))
|
||||
)
|
||||
} else if [".", ","].contains(s) {
|
||||
items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(Text(text: s, font: numberFont, color: .white, tintColor: textColor)))
|
||||
)
|
||||
} else {
|
||||
items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(SlotsComponent(
|
||||
item: AnyComponent(Text(text: String(c), font: numberFont, color: .white)),
|
||||
items: spinningItems,
|
||||
isAnimating: index > state.revealedNumberDigits,
|
||||
tintColor: textColor,
|
||||
verticalOffset: -1.0 - UIScreenPixel,
|
||||
motionBlur: false,
|
||||
size: CGSize(width: 8.0, height: 14.0))))
|
||||
)
|
||||
index += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
@ -340,9 +340,7 @@ final class SlotsComponent<ChildEnvironment: Equatable>: Component {
|
||||
self.spawnRandomSlot(availableSize: availableSize)
|
||||
}
|
||||
case .decelerating:
|
||||
let t = clamp01(self.decelTotalSteps > 1
|
||||
? Double(self.decelStepIndex) / Double(self.decelTotalSteps - 1)
|
||||
: 1.0)
|
||||
let t = clamp01(self.decelTotalSteps > 1 ? Double(self.decelStepIndex) / Double(self.decelTotalSteps - 1) : 1.0)
|
||||
|
||||
if let last = self.lastSpawnTime, now - last >= self.currentInterval {
|
||||
if !self.decelQueue.isEmpty {
|
||||
|
Loading…
x
Reference in New Issue
Block a user