如何解决使用矢量算法的SwiftUI线图动画
寻找提高绘图性能的方法(希望不使用Metal)。将LinearGradient添加为Shape的填充对绘制性能有很大的影响,因此我将其省略。
向量
struct LineGraphVector: VectorArithmetic {
var points: [CGPoint.AnimatableData]
static func + (lhs: LineGraphVector,rhs: LineGraphVector) -> LineGraphVector {
return add(lhs: lhs,rhs: rhs,+)
}
static func - (lhs: LineGraphVector,-)
}
static func add(lhs: LineGraphVector,rhs: LineGraphVector,_ sign: (CGFloat,CGFloat) -> CGFloat) -> LineGraphVector {
let maxPoints = max(lhs.points.count,rhs.points.count)
let leftIndices = lhs.points.indices
let rightIndices = rhs.points.indices
var newPoints: [CGPoint.AnimatableData] = []
(0 ..< maxPoints).forEach { index in
if leftIndices.contains(index) && rightIndices.contains(index) {
// Merge points
let lhsPoint = lhs.points[index]
let rhsPoint = rhs.points[index]
newPoints.append(
.init(
sign(lhsPoint.first,rhsPoint.first),sign(lhsPoint.second,rhsPoint.second)
)
)
} else if rightIndices.contains(index),let lastLeftPoint = lhs.points.last {
// Right side has more points,collapse to last left point
let rightPoint = rhs.points[index]
newPoints.append(
.init(
sign(lastLeftPoint.first,rightPoint.first),sign(lastLeftPoint.second,rightPoint.second)
)
)
} else if leftIndices.contains(index),let lastPoint = newPoints.last {
// Left side has more points,collapse to last known point
let leftPoint = lhs.points[index]
newPoints.append(
.init(
sign(lastPoint.first,leftPoint.first),sign(lastPoint.second,leftPoint.second)
)
)
}
}
return .init(points: newPoints)
}
mutating func scale(by rhs: Double) {
points.indices.forEach { index in
self.points[index].scale(by: rhs)
}
}
var magnitudeSquared: Double {
return 1.0
}
static var zero: LineGraphVector {
return .init(points: [])
}
}
形状
struct LineGraphShape: Shape {
var points: [CGPoint]
let closePath: Bool
init(points: [CGPoint],closePath: Bool) {
self.points = points
self.closePath = closePath
}
var animatableData: LineGraphVector {
get { .init(points: points.map { CGPoint.AnimatableData($0.x,$0.y) }) }
set { points = newValue.points.map { CGPoint(x: $0.first,y: $0.second) } }
}
func path(in rect: CGRect) -> Path {
Path { path in
path.move(to: points.first ?? .zero)
path.addLines(points)
switch (closePath,points.first,points.last) {
case (true,.some(let firstPoint),.some(let lastPoint)):
path.addLine(to: .init(x: lastPoint.x,y: rect.height))
path.addLine(to: .init(x: 0.0,y: firstPoint.y))
path.closeSubpath()
default:
break
}
}
}
}
用法
struct ContentView: View {
static let firstPoints: [CGPoint] = [
.init(x: 0.0,y: 0.0),.init(x: 20.0,y: 320.0),.init(x: 40.0,y: 50.0),.init(x: 60.0,y: 10.0),.init(x: 90.0,y: 140.0),.init(x: 200.0,y: 60.0),.init(x: 420.0,y: 20.0),]
static let secondPoints: [CGPoint] = [
.init(x: 0.0,.init(x: 10.0,y: 200.0),.init(x: 30.0,y: 70.0),y: 90.0),.init(x: 50.0,y: 150.0),y: 120.0),.init(x: 70.0,.init(x: 80.0,y: 30.0),.init(x: 100.0,.init(x: 110.0,.init(x: 120.0,.init(x: 130.0,.init(x: 140.0,.init(x: 150.0,.init(x: 160.0,.init(x: 170.0,.init(x: 180.0,.init(x: 190.0,.init(x: 210.0,.init(x: 220.0,.init(x: 230.0,.init(x: 240.0,.init(x: 250.0,.init(x: 260.0,.init(x: 270.0,.init(x: 280.0,.init(x: 290.0,]
static let thirdPoints: [CGPoint] = [
.init(x: 0.0,y: 300.0),y: 400.0),y: 320),]
let pointTypes = [0,1,2]
@State private var selectedPointType = 0
let points: [[CGPoint]] = [firstPoints,secondPoints,thirdPoints]
var body: some View {
VStack {
ZStack {
LineGraphShape(points: points[selectedPointType],closePath: true)
.fill(Color.blue.opacity(0.5))
LineGraphShape(points: points[selectedPointType],closePath: false)
.stroke(
Color.blue,style: .init(
lineWidth: 4.0,lineCap: .round,lineJoin: .round,miterLimit: 10.0
)
)
Text("\(points[selectedPointType].count) Points")
.font(.largeTitle)
.animation(.none)
}
.animation(.easeInOut(duration: 2.0))
VStack {
Picker("",selection: $selectedPointType) {
ForEach(pointTypes,id: \.self) { pointType in
Text("Graph \(pointType)")
}
}
.pickerStyle(SegmentedPickerStyle())
}
.padding(.horizontal,16.0)
.padding(.bottom,32.0)
}
}
}
解决方法
为什么要避免使用Metal?启用它的支持就像将LineGraphShape
包装到Group
中并用drawingGroup()
对其进行修改一样容易。试试看:
...
Group {
let gradient = LinearGradient(
gradient: Gradient(colors: [Color.red,Color.blue]),startPoint: .leading,endPoint: .trailing
)
LineGraphShape(points: points[selectedPointType],closePath: true)
.fill(gradient)
LineGraphShape(points: points[selectedPointType],closePath: false)
.stroke(
gradient,style: .init(
lineWidth: 4.0,lineCap: .round,lineJoin: .round,miterLimit: 10.0
)
)
}
.drawingGroup()
...
显着提高了性能?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。