如何使用 SwiftsCharts 从左到右为条形动画?

问题描述 投票:0回答:1

我有这张图表:

private struct ChartView: View {
    let sections: [BoughtItemsByTagSection]
    
    var body: some View {
        Chart(sections) { section in
            BarMark(x: .value("Price", section.header.totalPrice),
                    y: .value("Category", section.header.name))
            .foregroundStyle(Theme.accentSec)
        }
        .chartLegend(.hidden)
        .chartXAxis(.hidden)
        .chartYAxis {
            AxisMarks { _ in
                AxisValueLabel()
                    .foregroundStyle(Color.black)
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }
}

我想要一个简单的动画,使水平条从左到右增长。我已经尝试了多次,最接近的一次:

private struct ChartView: View {
    let sections: [BoughtItemsByTagSection]
    @State private var progress: Float = 0

    var body: some View {
        Chart(sections) { section in
            BarMark(
                xStart: .value("Start", 0),
                xEnd: .value("Price", section.header.totalPrice * progress),
                y: .value("Category", section.header.name)
            )
            .foregroundStyle(Theme.accentSec)
            .position(by: .value("Alignment", 0))
        }
        .chartLegend(.hidden)
        .chartXAxis(.hidden)
        .chartYAxis {
            AxisMarks { _ in
                AxisValueLabel()
                    .foregroundStyle(Color.black)
            }
        }
        .aspectRatio(1, contentMode: .fit)
        .onAppear {
            animateChart()
        }
    }
    
    private func animateChart() {
        progress = 0 // Start from zero
        withAnimation(.easeOut(duration: 1.5)) {
            progress = 1
        }
    }
}

但它有一个问题,即条形从中心而不是从左侧生长,这是该图表的自然锚点。如何让它们从左边框开始生长?

ios swift animation swiftui swiftcharts
1个回答
0
投票

您尚未为图表指定 X 轴域,因此 SwiftUI 会自动找到适合您的数据的 X 轴域。条形图从中间开始增长的原因是,当所有数据点均为 0 时,自动确定的域会表现得很奇怪。动画会在该奇怪的域与所需的域之间进行插值。

如果您在

progress = 0.1
处开始动画,您可以看到条形根本没有动画。删除
.chartXAxis(.hidden)
,你会发现实际上是X轴的刻度在动画!

所以只需修复一个域即可。找到数据的最大值并将其用作 X 轴的最大值。

let max = sections.map(\.header.totalPrice).max()!

// ...

.chartXScale(domain: .automatic(dataType: Float.self, modifyInferredDomain: { $0 = [0, max] }))

您不一定必须使用

xStart
/
xEnd
初始化程序。如果乘以
progress
,第一个代码片段中的条形标记也会起作用。

BarMark(
    x: .value("Price", section.header.totalPrice * progress),
    y: .value("Category", section.header.name)
)

另一种方法是为

xEnd
的偏移设置动画。偏移所有条形的
xEnd
,使它们不可见,然后将该偏移量设置为 0。您可以使用
GeometryReader
计算出应该偏移它们多少,尽管这是一个上限 - 较短的条形在它们第一次出现之前需要更长的时间。换句话说,所有的条形都会以相同的速度增长。

GeometryReader { geo in
    Chart(sections) { section in
        BarMark(
            xStart: .value("Start", 0),
            xEnd: .value("Price", section.header.totalPrice),
            y: .value("Category", section.header.name)
        )
        .offset(xEnd: -geo.size.width * (1 - progress))
    }
    // ...
}
© www.soinside.com 2019 - 2024. All rights reserved.