工作上有个需求, canvas 一秒渲染 250 个数据,不可能做到吧 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhouchijian
V2EX    问与答

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

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

需求是这样的, 要用 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  
PRO
   2020-06-07 09:42:26 +08:00
抽稀
qiayue
    3
qiayue  
PRO
   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
这个和我们绘制频谱图波形图是一样的,你这个相当于一秒一帧,一帧数据有 50 个点,你只需要连续绘制就可以了,每次数据来了就把可视区域内的数据重新绘制,自然就有了动态效果。
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     Solana     1256 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 34ms UTC 17:16 PVG 01:16 LAX 10:16 JFK 13:16
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86