V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zhouchijian
V2EX  ›  问与答

工作上有个需求, canvas 一秒渲染 250 个数据,不可能做到吧

  •  
  •   zhouchijian · 2020-06-07 09:39:14 +08:00 · 5962 次点击
    这是一个创建于 1663 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求是这样的, 要用 canvas 画出心电图,后端 1 秒给我传 1 个数组,数组里有 250 个数据 结构是这样的 https://nas-1253716007.cos.ap-guangzhou.myqcloud.com/mmexport1591493767956.png 但是呢,实际效果就是,渲染速度跟不上后端给我数据的速度。 我是用了 requestAnimationRequest 来循环渲染数据的。 想了一下,1 秒渲染 250 个数据,不就是 250 帧了吗,普通的屏幕才 60hz 的刷新率,感觉不可能做到的啊

    49 条回复    2020-06-08 21:37:24 +08:00
    zhouchijian
        1
    zhouchijian  
    OP
       2020-06-07 09:42:16 +08:00
    格式怎么乱掉了。。。
    qiayue
        2
    qiayue  
       2020-06-07 09:42:26 +08:00
    抽稀
    qiayue
        3
    qiayue  
       2020-06-07 09:43:09 +08:00
    合并、取样、抽稀
    imdong
        4
    imdong  
       2020-06-07 09:47:40 +08:00
    应该是一秒一帧,如何做到尽可能少的绘制。

    看了你的格式,起码 0-36 这样的数据可以压缩为一次操作?

    应该是类似折线图吧?最简单的能给出的意见就是这样。

    连续在 dom 上绘制 速度比较慢,能不能把操作在内存绘制好,然后放到 dom 上呢?

    一秒钟渲染一张图片应该问题不大,具体我也不懂,业余人士瞎比比。
    cornetCat
        5
    cornetCat  
       2020-06-07 09:51:34 +08:00
    1 秒渲染 250 个数据,不就是 250 帧了吗
    这不对吧
    想达到 60fps,应该是 1/60s 内渲染完 250 个数据
    Perry
        6
    Perry  
       2020-06-07 09:52:37 +08:00
    需求完全没说清楚,请先说清楚这 250 个数据是什么数据,需求是什么
    MoHen9
        7
    MoHen9  
       2020-06-07 09:55:29 +08:00 via Android
    每次多绘制几个点就行,人眼看不出来区别的,我在 Android 上也是这样做的,我们用 C++做的客户端也不会一次只绘制一个点,采样率有可能是 1000/s,难道也要每秒绘制 1000 次?

    楼上方法也可以
    hakono
        8
    hakono  
       2020-06-07 09:58:18 +08:00 via Android
    我看你这数据,感觉是一秒渲染一次啊
    如果后端给你的数据是每帧数据,先不提渲染赶不赶得上,你显示器首先刷新率绝对就没有 250Hz 吧,就算真渲染出了 250fps,你这也是没有任何用处啊。直接 250 个数据每隔 10 个取一个数,渲染成 25fps 人眼都感觉不太出区别
    binux
        9
    binux  
       2020-06-07 10:00:28 +08:00
    250 个标量?别说 250 个了,25000 个都没问题
    whileFalse
        10
    whileFalse  
       2020-06-07 10:00:33 +08:00   ❤️ 3
    大哥,一秒传你一次数据,其中包含 250 个数,那其实是 1hz 啊……只不过每次屏幕更新要在已有数据基础上绘制 250 个点而已。如果忽略数据处理、网络传输、绘制等时间,数据从收集到展现的平均时间是 0.5s 。

    当然,用户可以觉得这种仿照微软 windows 任务管理器的更新速度不够接地气,并愿意加一倍的钱让他更新速度变快。那你就可以每秒钟绘制 2 帧( 2hz ),并每帧绘制 125 个点。依照这种思路,只要用户肯出 60 倍的钱,你就可以每秒绘制 60 次,每次绘制 4 个点或 5 个点。你可以以 settimeout 方式执行每秒多次绘制。

    如果用户肯出 100 倍的钱一劳永逸地完美地解决这个问题,那你就用 requestAnimationRequest 吧。你可以很简单地计算出上一次执行到本次执行之间用了多少毫秒,然后除以 4 (即 1000/250 ),得出你本次应该绘制的点的数量。此外,你还应该对上次绘制的图进行平移。
    zhouchijian
        11
    zhouchijian  
    OP
       2020-06-07 10:15:10 +08:00
    @hakono 怪我没说清楚,其实要的效果就是类似于扫描动画
    类似于这种
    http://pic.16xx8.com/allimg/110810/1A0303356-0.gif
    https://nas-1253716007.cos.ap-guangzhou.myqcloud.com/1591495891362.mp4
    zhouchijian
        12
    zhouchijian  
    OP
       2020-06-07 10:23:22 +08:00   ❤️ 1
    @MoHen9 有道理,每次只绘制一个点太蠢了
    zhugefubin
        13
    zhugefubin  
       2020-06-07 10:25:52 +08:00 via Android
    一下绘制一组数据不行么
    binbinyouliiii
        14
    binbinyouliiii  
       2020-06-07 10:27:33 +08:00
    4K 显示器有 3840*2160 个像素点,岂不是有 3840*2160 的刷新率
    zhouchijian
        15
    zhouchijian  
    OP
       2020-06-07 10:30:37 +08:00
    @imdong 其实就是折线图,但是要求有动画效果,就是类似于那种扫描效果,现在是每次只绘制一个点,你说的对,应该尽可能少的绘制
    zhouchijian
        16
    zhouchijian  
    OP
       2020-06-07 10:32:01 +08:00
    @zhugefubin 我也想这么简单粗暴,但是要有动画效果
    7gugu
        17
    7gugu  
       2020-06-07 10:58:19 +08:00 via Android
    不考虑一下现有的图表库么?比如 Echarts 和 Uchart 等.
    reus
        18
    reus  
       2020-06-07 10:59:20 +08:00
    你一秒渲染多少帧,和屏幕刷新率有什么关系?

    那些游戏一秒几十几百帧,为什么可以显示?

    一辆车从你眼前开过,你眨一下眼睛,难道车会根据你眨眼睛慢了,速度就变了?

    显示器就是你的眼睛,只是在采样观察外部世界,而外部世界并不会受你的眼睛影响。
    zhugefubin
        19
    zhugefubin  
       2020-06-07 11:20:14 +08:00 via Android
    我对这个不是很懂
    @zhouchijian
    abelce
        20
    abelce  
       2020-06-07 11:36:07 +08:00 via iPhone
    你一秒渲染一次就可以了呀,我以前渲染几十万个点都没毛病
    zhouchijian
        21
    zhouchijian  
    OP
       2020-06-07 11:42:25 +08:00 via Android
    @reus 对喔。。难道是我理解错刷新率了
    zhouchijian
        22
    zhouchijian  
    OP
       2020-06-07 11:43:11 +08:00 via Android
    @7gugu 不给用 echarts,原因我也不知道
    zhouchijian
        23
    zhouchijian  
    OP
       2020-06-07 11:57:42 +08:00 via Android
    @abelce 不行,要有动画,一秒渲染一次就没动画了
    jackmod
        24
    jackmod  
       2020-06-07 12:19:18 +08:00
    markdown 要 2 次换行才是新段落,发之前点一下预览
    reus
        25
    reus  
       2020-06-07 12:57:34 +08:00 via Android
    @zhouchijian 动画的话,每秒渲染 30 次左右就够了,也就是 256/32,每次渲染 8 个点,每 1000/32ms 渲染一次
    reus
        26
    reus  
       2020-06-07 12:58:28 +08:00 via Android
    @zhouchijian 一秒一次就是 1hz 的动画,怎么叫没有?
    rockjike
        27
    rockjike  
       2020-06-07 13:29:25 +08:00 via Android
    一桢更新 10 个数据,一个周期不一定要 1s,可以先把后端数据储存起来
    Cabana
        28
    Cabana  
       2020-06-07 13:37:52 +08:00 via Android
    1 秒 250 个数据分成 25 帧,一帧绘制 10 个数据不行吗?
    nightv2
        29
    nightv2  
       2020-06-07 13:55:00 +08:00 via Android
    比如说每秒在内存中把 250 个数据生成一个折线图,然后和上一秒的折线图做一个带有动画效果的切换,切换应该是有现成的轮子的
    javaluo
        30
    javaluo  
       2020-06-07 14:39:24 +08:00 via Android
    每秒渲染一次,弄一个遮罩平行移动展示出来不就行了?
    aturx
        31
    aturx  
       2020-06-07 14:49:38 +08:00 via Android
    同事做过类似的,要动画效果,可以传数据的时候可以几秒传一组数据,渲染的时候频率高点持续渲染。
    justin2018
        32
    justin2018  
       2020-06-07 16:23:48 +08:00
    zhouchijian
        33
    zhouchijian  
    OP
       2020-06-07 16:33:21 +08:00 via Android
    @justin2018 对,就是这种效果
    zhouchijian
        34
    zhouchijian  
    OP
       2020-06-07 16:35:46 +08:00   ❤️ 1
    楼上各位老哥的意思我都懂,就是一次绘制多个点,不要只绘制一个点,抽空写了个小 demo

    https://jsrun.net/xM2Kp
    sarlanori
        35
    sarlanori  
       2020-06-07 16:39:01 +08:00 via Android
    这个和我们绘制频谱图波形图是一样的,你这个相当于一秒一帧,一帧数据有 250 个点,你只需要连续绘制就可以了,每次数据来了就把可视区域内的数据重新绘制,自然就有了动态效果。
    ghostheaven
        36
    ghostheaven  
       2020-06-07 16:39:36 +08:00 via Android
    动画和绘制的时间精确控制在一秒,开始的时候缓存几秒数据再开始
    Torpedo
        37
    Torpedo  
       2020-06-07 16:41:55 +08:00
    你延迟一秒画。
    不就变成你有 250 个数据,然后在 1s 里,60 帧,每帧画 4 个。
    reus
        38
    reus  
       2020-06-07 20:25:19 +08:00
    @zhouchijian 闪瞎眼……不要每次都重绘,做一下平移,然后渲染新的点就行了。这么闪,还不如用 svg,至少不闪……
    jiejiss
        39
    jiejiss  
       2020-06-07 20:52:16 +08:00
    动画效果 30Hz 足够用了,你每帧绘制 8 - 9 个点就行

    但是 requestAnimationFrame 每帧之间的间隔不一定一样,比如设备卡了上一个 frame 和下一个 frame 之间间隔 1s 怎么办?所以你可以整个全局数组,拿 setInterval 以固定的频率(比如 30Hz )往里 push 数据,然后在 requestAnimationFrame 里把这个数组里的全部数据消费掉
    jakezh
        40
    jakezh  
       2020-06-07 22:08:24 +08:00
    那我这种一秒渲染 50 万个数据的怎么算帧率?
    zhouchijian
        41
    zhouchijian  
    OP
       2020-06-07 22:30:43 +08:00 via Android
    @jakezh 那老哥你是怎么解决这种问题的
    zhouchijian
        42
    zhouchijian  
    OP
       2020-06-07 22:38:57 +08:00 via Android
    @reus 闪应该是正常的。。因为我每帧都绘制 10 个点了。。每 16ms 绘制一次,改成每帧绘制 4 个点就舒服多了。。至于 svg,刚开始我也想用 svg 的,因为项目的老代码也是用 canvas 来做心电图的,然后时间紧,就想着继续用 canvas,没有用 svg,因为以前也没写过 svg 。。
    cheng6563
        43
    cheng6563  
       2020-06-07 23:05:34 +08:00 via Android
    双缓冲,就不会闪了。。。。这不是基本操作吗
    zhouchijian
        44
    zhouchijian  
    OP
       2020-06-07 23:16:00 +08:00 via Android
    @cheng6563 不清楚耶,第一次搞这种图表
    reus
        45
    reus  
       2020-06-07 23:55:24 +08:00
    @zhouchijian 用双缓冲: https://stackoverflow.com/questions/2795269/does-html5-canvas-support-double-buffering
    就是不要直接清空,而是在另一个 canvas 里更新,更新完了再切换,就不会闪了
    wangcheng
        46
    wangcheng  
       2020-06-08 00:33:58 +08:00
    不应该假设 requestAnimationFrame 每 16ms 绘制一次,应该用 requestAnimationFrame 回调中的第一个参数时间戳来计算动画。否则帧率不是 60 的时候动画速度就乱了。
    weixiangzhe
        47
    weixiangzhe  
       2020-06-08 09:35:17 +08:00 via Android
    按比例采样吧,
    redbuck
        48
    redbuck  
       2020-06-08 14:27:03 +08:00
    渲染和生成路径(即更新 view model)分开.

    生成路径随便 push.

    渲染按照固定频率把路径绘制到 canvas 上.
    jakezh
        49
    jakezh  
       2020-06-08 21:37:24 +08:00
    我是先渲染上,然后按照鼠标事件加动画。
    没有你这种每秒全局刷新的需求
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6060 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 01:59 · PVG 09:59 · LAX 17:59 · JFK 20:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.