You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI中实现对角线Carousel并让中间元素突出?

SwiftUI 对角线Carousel实现方案

ScrollView原生仅支持单轴滚动,无法满足对角线滚动需求,你可以通过自定义手势监听+位置动态计算的方式实现对应效果,具体实现逻辑如下:

  • 拖拽手势处理
    使用DragGesture()监听用户拖拽操作,将x、y方向的偏移量按对角线斜率合并为总滚动进度,绑定到@State修饰的滚动偏移变量。拖拽结束后添加惯性动效和停靠逻辑,保证元素会自动对齐到中间展示位。
  • 元素布局计算
    用ZStack叠加所有Carousel元素,每个元素的x、y偏移都按固定比例和当前滚动进度计算,实现整体沿对角线滚动的效果。你可以通过调整x、y的偏移比例自定义对角线的倾斜角度。
  • 差异化样式配置
    计算每个元素和当前居中位置的偏移差值,根据差值动态调整元素样式:
    • 差值小于0.5(中间元素):放大1.1-1.3倍,设置你需要的普通圆角,zIndex调整到最高,添加更深的阴影突出层级
    • 差值大于等于1(两侧元素):缩小到0.7-0.9倍,圆角设置为元素宽度的1/2实现圆形效果,适当降低透明度,zIndex调低避免遮挡中间元素
  • 性能优化
    当元素数量大于5个时,仅渲染当前可视范围内的3-5个元素,超出范围的元素直接隐藏,减少不必要的绘制开销。

核心实现代码参考:

import SwiftUI

struct DiagonalCarousel: View {
    // 单个元素基础尺寸
    private let itemSize: CGSize = .init(width: 100, height: 100)
    // 对角线步长,调整x、y数值可以修改对角线斜率
    private let step: (x: CGFloat, y: CGFloat) = (60, 40)
    private let items = Array(0..<10)
    @State private var scrollOffset: CGFloat = 0
    @State private var currentIndex: Int = 0
    
    var body: some View {
        ZStack {
            ForEach(items, id: \.self) { index in
                // 计算元素相对中间位置的偏移量
                let relativeOffset = CGFloat(index) - scrollOffset / (step.x + step.y) * 2
                let absOffset = abs(relativeOffset)
                
                RoundedRectangle(cornerRadius: absOffset < 0.5 ? 16 : itemSize.width / 2)
                    .fill(Color.blue.opacity(0.8))
                    .frame(width: itemSize.width, height: itemSize.height)
                    .offset(
                        x: relativeOffset * step.x,
                        y: relativeOffset * step.y
                    )
                    .scaleEffect(absOffset < 0.5 ? 1.2 : 0.8)
                    .zIndex(10 - absOffset)
                    .opacity(absOffset > 2 ? 0 : (absOffset < 0.5 ? 1 : 0.7))
                    .shadow(radius: absOffset < 0.5 ? 10 : 2)
            }
        }
        .gesture(
            DragGesture()
                .onChanged { value in
                    // 合并x、y偏移计算总滚动量
                    let totalDrag = value.translation.width + value.translation.height
                    scrollOffset = -totalDrag
                }
                .onEnded { value in
                    // 拖拽结束后计算停靠的目标位置
                    let predictDrag = value.predictedEndTranslation.width + value.predictedEndTranslation.height
                    let targetOffset = -predictDrag
                    let targetIndex = round(targetOffset / (step.x + step.y) * 2)
                    withAnimation(.easeOut(duration: 0.3)) {
                        scrollOffset = targetIndex * (step.x + step.y) / 2
                        currentIndex = max(0, min(items.count - 1, Int(targetIndex)))
                    }
                }
        )
    }
}

如果需要实现无限滚动效果,只需要在滚动到数据源边界时重置scrollOffset数值,同时循环处理元素索引即可,不需要修改核心逻辑。

内容的提问来源于stack exchange,提问作者Red Lion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 20:45:03