使用矢量算法的SwiftUI线图动画

如何解决使用矢量算法的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)
        }
    }
}

Demo

解决方法

为什么要避免使用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 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res