我有这张图表:
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
}
}
}
但它有一个问题,即条形从中心而不是从左侧生长,这是该图表的自然锚点。如何让它们从左边框开始生长?
您尚未为图表指定 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))
}
// ...
}