小小小幸运 发表于 2025-3-10 05:04:37

Vue纯JS使用canvas实现统计图

实现展示

https://i-blog.csdnimg.cn/direct/90b7b84d36c64fb2a11f5cedaa2cd754.png
https://i-blog.csdnimg.cn/direct/eadf23cef2c24436a5dd81e6c1539fd0.png
https://i-blog.csdnimg.cn/direct/8ce696590283472383ee2cd020fc0e17.png
数据结构

sleepData 睡眠数据统计图

sleepData:{"groupCountSleep":6,"countSleep":380,"sleepListData":[{"maxBody":51,"sleepStatus":4},{"maxBody":39,"sleepStatus":4},{"maxBody":11,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":9,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":23,"sleepStatus":4},{"maxBody":113,"sleepStatus":4},{"maxBody":59,"sleepStatus":4},{"maxBody":17,"sleepStatus":4},{"maxBody":24,"sleepStatus":4},{"maxBody":7,"sleepStatus":4},{"maxBody":3,"sleepStatus":4},{"maxBody":5,"sleepStatus":4},{"maxBody":5,"sleepStatus":4},{"maxBody":8,"sleepStatus":4},{"maxBody":3,"sleepStatus":4},{"maxBody":24,"sleepStatus":4},{"maxBody":10,"sleepStatus":4},{"maxBody":4,"sleepStatus":4},{"maxBody":29,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":0,"sleepStatus":4},{"maxBody":2,"sleepStatus":3},{"maxBody":9,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":11,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":12,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":22,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":1,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":2,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":7,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":8,"sleepStatus":1},{"maxBody":1,"sleepStatus":3},{"maxBody":10,"sleepStatus":3},{"maxBody":23,"sleepStatus":3},{"maxBody":14,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":30,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":13,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":6,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":26,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":1,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":59,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":8,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":3,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":3,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":7,"sleepStatus":3},{"maxBody":3,"sleepStatus":3},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":24,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":4,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":5,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":3},{"maxBody":1,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":11,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":17,"sleepStatus":2},{"maxBody":0,"sleepStatus":3},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":2},{"maxBody":0,"sleepStatus":1},{"maxBody":0,"sleepStatus":1},{"maxBody":9,"sleepStatus":3}]}
sleepData 饼形统计图

bigCanvasData:{"sleepTime":6.35,"bigCanvas":{"deepSleep":37.9,"rapidSleep":32.9,"eyeMovementSleep":20.3,"sober":8.9}}
originalData 三线统计图

originalData:{"countOriginal":22812,"resultCollection":[{"bloodOxygen":"83.50","heartRate":"0.00","body":"2"},{"bloodOxygen":"76.80","heartRate":"6.64","body":"1"},{"bloodOxygen":"75.20","heartRate":"8.79","body":"1"},{"bloodOxygen":"77.50","heartRate":"11.91","body":"2"},{"bloodOxygen":"74.40","heartRate":"9.18","body":"1"},{"bloodOxygen":"77.30","heartRate":"9.96","body":"5"},{"bloodOxygen":"78.80","heartRate":"10.35","body":"1"},{"bloodOxygen":"79.90","heartRate":"12.70","body":"30"},{"bloodOxygen":"78.50","heartRate":"14.06","body":"13"},{"bloodOxygen":"81.10","heartRate":"15.23","body":"12"},{"bloodOxygen":"78.70","heartRate":"14.06","body":"51"},{"bloodOxygen":"79.00","heartRate":"14.45","body":"31"},{"bloodOxygen":"78.90","heartRate":"14.06","body":"7"},{"bloodOxygen":"78.80","heartRate":"14.06","body":"4"},{"bloodOxygen":"78.80","heartRate":"14.06","body":"1"},{"bloodOxygen":"78.80","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.80","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.90","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.90","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.90","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.80","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.70","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.60","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.60","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.50","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.40","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.50","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.40","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.40","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.40","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.60","heartRate":"14.06","body":"0"},{"bloodOxygen":"78.70","heartRate":"35.74","body":"0"},{"bloodOxygen":"78.80","heartRate":"30.27","body":"0"},{"bloodOxygen":"78.90","heartRate":"46.68","body":"0"},{"bloodOxygen":"79.10","heartRate":"38.48","body":"0"},{"bloodOxygen":"98.80","heartRate":"35.74","body":"0"},{"bloodOxygen":"98.90","heartRate":"34.38","body":"0"},{"bloodOxygen":"99.10","heartRate":"35.35","body":"0"},{"bloodOxygen":"99.20","heartRate":"46.88","body":"0"},{"bloodOxygen":"99.00","heartRate":"44.73","body":"0"},{"bloodOxygen":"99.00","heartRate":"62.11","body":"0"},{"bloodOxygen":"99.00","heartRate":"62.30","body":"0"},{"bloodOxygen":"98.90","heartRate":"70.31","body":"0"},{"bloodOxygen":"98.90","heartRate":"71.09","body":"0"},{"bloodOxygen":"99.00","heartRate":"70.90","body":"0"},{"bloodOxygen":"99.10","heartRate":"71.88","body":"0"},{"bloodOxygen":"99.10","heartRate":"70.70","body":"0"},{"bloodOxygen":"99.10","heartRate":"71.29","body":"0"},{"bloodOxygen":"99.10","heartRate":"70.90","body":"0"},{"bloodOxygen":"99.00","heartRate":"70.31","body":"0"},{"bloodOxygen":"99.00","heartRate":"69.53","body":"0"},{"bloodOxygen":"99.00","heartRate":"69.14","body":"0"},{"bloodOxygen":"98.90","heartRate":"69.34","body":"0"},{"bloodOxygen":"98.90","heartRate":"69.92","body":"0"},{"bloodOxygen":"98.80","heartRate":"69.73","body":"0"},{"bloodOxygen":"98.70","heartRate":"69.92","body":"0"},{"bloodOxygen":"98.60","heartRate":"69.92","body":"0"},{"bloodOxygen":"98.60","heartRate":"70.51","body":"0"},{"bloodOxygen":"98.70","heartRate":"70.31","body":"0"},{"bloodOxygen":"98.70","heartRate":"72.07","body":"0"},{"bloodOxygen":"98.50","heartRate":"71.48","body":"0"},{"bloodOxygen":"98.40","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.40","heartRate":"72.66","body":"0"},{"bloodOxygen":"98.30","heartRate":"73.24","body":"0"},{"bloodOxygen":"98.10","heartRate":"72.85","body":"0"},{"bloodOxygen":"98.00","heartRate":"73.24","body":"0"},{"bloodOxygen":"97.90","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.50","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.40","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.30","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.20","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.10","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.00","heartRate":"73.05","body":"0"},{"bloodOxygen":"96.90","heartRate":"73.05","body":"0"},{"bloodOxygen":"96.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"96.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"96.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"96.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.10","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.20","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.40","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.60","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.90","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.80","heartRate":"73.44","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.20","heartRate":"73.44","body":"3"},{"bloodOxygen":"100.00","heartRate":"73.44","body":"1"},{"bloodOxygen":"98.90","heartRate":"73.44","body":"39"},{"bloodOxygen":"99.00","heartRate":"73.44","body":"2"},{"bloodOxygen":"98.60","heartRate":"73.44","body":"6"},{"bloodOxygen":"98.40","heartRate":"73.44","body":"2"},{"bloodOxygen":"98.20","heartRate":"73.44","body":"1"},{"bloodOxygen":"98.20","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.20","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.30","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.50","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.60","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.50","heartRate":"79.49","body":"3"},{"bloodOxygen":"98.40","heartRate":"77.93","body":"0"},{"bloodOxygen":"98.40","heartRate":"86.91","body":"0"},{"bloodOxygen":"98.40","heartRate":"83.59","body":"0"},{"bloodOxygen":"98.30","heartRate":"86.13","body":"0"},{"bloodOxygen":"98.20","heartRate":"84.77","body":"0"},{"bloodOxygen":"98.00","heartRate":"86.52","body":"0"},{"bloodOxygen":"97.90","heartRate":"85.16","body":"0"},{"bloodOxygen":"97.70","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.50","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.40","heartRate":"85.74","body":"0"},{"bloodOxygen":"97.30","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.20","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.10","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.10","heartRate":"85.55","body":"0"},{"bloodOxygen":"97.00","heartRate":"82.62","body":"0"},{"bloodOxygen":"97.40","heartRate":"83.20","body":"5"},{"bloodOxygen":"97.10","heartRate":"82.81","body":"11"},{"bloodOxygen":"97.60","heartRate":"82.03","body":"0"},{"bloodOxygen":"98.20","heartRate":"82.23","body":"0"},{"bloodOxygen":"98.20","heartRate":"82.42","body":"0"},{"bloodOxygen":"98.30","heartRate":"82.42","body":"0"},{"bloodOxygen":"98.40","heartRate":"82.23","body":"0"},{"bloodOxygen":"98.90","heartRate":"82.23","body":"9"},{"bloodOxygen":"99.30","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.60","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"82.42","body":"0"},{"bloodOxygen":"99.50","heartRate":"80.47","body":"0"},{"bloodOxygen":"99.40","heartRate":"81.05","body":"0"},{"bloodOxygen":"99.30","heartRate":"80.66","body":"0"},{"bloodOxygen":"99.10","heartRate":"80.27","body":"0"},{"bloodOxygen":"99.10","heartRate":"77.54","body":"0"},{"bloodOxygen":"99.00","heartRate":"78.32","body":"0"},{"bloodOxygen":"98.80","heartRate":"77.73","body":"0"},{"bloodOxygen":"98.60","heartRate":"76.95","body":"0"},{"bloodOxygen":"98.70","heartRate":"79.30","body":"0"},{"bloodOxygen":"102.30","heartRate":"79.10","body":"7"},{"bloodOxygen":"98.70","heartRate":"79.30","body":"2"},{"bloodOxygen":"98.70","heartRate":"79.69","body":"6"},{"bloodOxygen":"98.60","heartRate":"79.49","body":"0"},{"bloodOxygen":"98.40","heartRate":"79.69","body":"0"},{"bloodOxygen":"97.90","heartRate":"79.49","body":"0"},{"bloodOxygen":"97.70","heartRate":"80.47","body":"0"},{"bloodOxygen":"97.60","heartRate":"81.25","body":"0"},{"bloodOxygen":"97.60","heartRate":"83.01","body":"0"},{"bloodOxygen":"97.60","heartRate":"81.64","body":"0"},{"bloodOxygen":"97.50","heartRate":"81.84","body":"0"},{"bloodOxygen":"97.40","heartRate":"79.69","body":"0"},{"bloodOxygen":"97.60","heartRate":"81.45","body":"0"},{"bloodOxygen":"97.50","heartRate":"80.86","body":"0"},{"bloodOxygen":"97.40","heartRate":"81.25","body":"0"},{"bloodOxygen":"97.60","heartRate":"81.84","body":"0"},{"bloodOxygen":"97.60","heartRate":"82.42","body":"0"},{"bloodOxygen":"97.60","heartRate":"82.03","body":"0"},{"bloodOxygen":"97.70","heartRate":"82.42","body":"0"},{"bloodOxygen":"97.70","heartRate":"81.64","body":"0"},{"bloodOxygen":"97.60","heartRate":"80.86","body":"0"},{"bloodOxygen":"97.60","heartRate":"80.86","body":"0"},{"bloodOxygen":"97.50","heartRate":"81.25","body":"0"},{"bloodOxygen":"97.50","heartRate":"80.86","body":"0"},{"bloodOxygen":"97.50","heartRate":"81.05","body":"0"},{"bloodOxygen":"97.40","heartRate":"81.45","body":"0"},{"bloodOxygen":"97.30","heartRate":"80.86","body":"0"},{"bloodOxygen":"97.20","heartRate":"80.47","body":"0"},{"bloodOxygen":"97.20","heartRate":"79.69","body":"0"},{"bloodOxygen":"97.10","heartRate":"78.32","body":"0"},{"bloodOxygen":"97.20","heartRate":"76.95","body":"0"},{"bloodOxygen":"97.40","heartRate":"75.98","body":"0"},{"bloodOxygen":"97.60","heartRate":"77.54","body":"0"},{"bloodOxygen":"97.70","heartRate":"77.15","body":"0"},{"bloodOxygen":"97.80","heartRate":"77.54","body":"0"},{"bloodOxygen":"98.20","heartRate":"77.93","body":"0"},{"bloodOxygen":"98.20","heartRate":"78.32","body":"0"},{"bloodOxygen":"98.30","heartRate":"77.73","body":"0"},{"bloodOxygen":"98.30","heartRate":"77.93","body":"0"},{"bloodOxygen":"98.20","heartRate":"77.93","body":"0"},{"bloodOxygen":"98.20","heartRate":"81.84","body":"0"},{"bloodOxygen":"98.10","heartRate":"80.86","body":"0"},{"bloodOxygen":"98.00","heartRate":"81.64","body":"0"},{"bloodOxygen":"98.00","heartRate":"80.66","body":"0"},{"bloodOxygen":"97.90","heartRate":"82.81","body":"0"},{"bloodOxygen":"97.80","heartRate":"78.13","body":"0"},{"bloodOxygen":"97.70","heartRate":"79.10","body":"0"},{"bloodOxygen":"97.70","heartRate":"75.00","body":"0"},{"bloodOxygen":"97.70","heartRate":"75.98","body":"0"},{"bloodOxygen":"97.50","heartRate":"75.00","body":"0"},{"bloodOxygen":"97.40","heartRate":"75.00","body":"0"},{"bloodOxygen":"97.60","heartRate":"74.80","body":"0"},{"bloodOxygen":"97.70","heartRate":"75.78","body":"0"},{"bloodOxygen":"97.90","heartRate":"76.37","body":"0"},{"bloodOxygen":"98.00","heartRate":"76.17","body":"0"},{"bloodOxygen":"98.10","heartRate":"75.98","body":"0"},{"bloodOxygen":"98.30","heartRate":"76.37","body":"0"},{"bloodOxygen":"98.50","heartRate":"75.98","body":"0"},{"bloodOxygen":"98.50","heartRate":"76.56","body":"0"},{"bloodOxygen":"98.70","heartRate":"77.93","body":"0"},{"bloodOxygen":"98.70","heartRate":"78.71","body":"0"},{"bloodOxygen":"98.70","heartRate":"79.10","body":"0"},{"bloodOxygen":"98.60","heartRate":"78.52","body":"0"},{"bloodOxygen":"98.70","heartRate":"78.91","body":"0"},{"bloodOxygen":"98.60","heartRate":"78.71","body":"0"},{"bloodOxygen":"98.40","heartRate":"78.71","body":"0"},{"bloodOxygen":"98.20","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.10","heartRate":"75.78","body":"0"},{"bloodOxygen":"98.00","heartRate":"76.56","body":"0"},{"bloodOxygen":"97.90","heartRate":"75.98","body":"0"},{"bloodOxygen":"97.80","heartRate":"75.39","body":"0"},{"bloodOxygen":"97.70","heartRate":"75.59","body":"0"},{"bloodOxygen":"97.70","heartRate":"75.78","body":"0"},{"bloodOxygen":"97.70","heartRate":"77.15","body":"0"},{"bloodOxygen":"97.70","heartRate":"78.52","body":"0"},{"bloodOxygen":"97.60","heartRate":"77.93","body":"0"},{"bloodOxygen":"97.60","heartRate":"79.30","body":"0"},{"bloodOxygen":"97.60","heartRate":"79.30","body":"0"},{"bloodOxygen":"97.60","heartRate":"77.34","body":"0"},{"bloodOxygen":"97.50","heartRate":"77.54","body":"0"},{"bloodOxygen":"97.50","heartRate":"74.22","body":"0"},{"bloodOxygen":"97.40","heartRate":"75.39","body":"0"},{"bloodOxygen":"97.50","heartRate":"74.41","body":"0"},{"bloodOxygen":"97.60","heartRate":"74.80","body":"0"},{"bloodOxygen":"97.60","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.60","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.90","heartRate":"74.02","body":"0"},{"bloodOxygen":"98.10","heartRate":"75.20","body":"0"},{"bloodOxygen":"98.10","heartRate":"75.78","body":"0"},{"bloodOxygen":"98.10","heartRate":"78.13","body":"0"},{"bloodOxygen":"98.20","heartRate":"79.69","body":"0"},{"bloodOxygen":"98.20","heartRate":"78.91","body":"0"},{"bloodOxygen":"98.00","heartRate":"76.17","body":"0"},{"bloodOxygen":"97.90","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.80","heartRate":"75.78","body":"0"},{"bloodOxygen":"97.70","heartRate":"74.22","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.83","body":"0"},{"bloodOxygen":"97.60","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.50","heartRate":"73.83","body":"0"},{"bloodOxygen":"97.40","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.30","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.40","heartRate":"72.85","body":"0"},{"bloodOxygen":"97.60","heartRate":"73.05","body":"0"},{"bloodOxygen":"97.80","heartRate":"72.85","body":"0"},{"bloodOxygen":"98.00","heartRate":"73.05","body":"0"},{"bloodOxygen":"98.40","heartRate":"73.83","body":"0"},{"bloodOxygen":"98.50","heartRate":"73.63","body":"0"},{"bloodOxygen":"98.40","heartRate":"73.63","body":"0"},{"bloodOxygen":"98.30","heartRate":"73.63","body":"0"},{"bloodOxygen":"98.10","heartRate":"73.44","body":"0"},{"bloodOxygen":"98.00","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.90","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.70","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.50","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.50","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.40","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.30","heartRate":"73.63","body":"0"},{"bloodOxygen":"97.30","heartRate":"74.41","body":"0"},{"bloodOxygen":"97.30","heartRate":"74.22","body":"0"},{"bloodOxygen":"97.30","heartRate":"74.41","body":"0"},{"bloodOxygen":"97.40","heartRate":"74.61","body":"0"},{"bloodOxygen":"97.70","heartRate":"74.80","body":"0"},{"bloodOxygen":"98.00","heartRate":"74.80","body":"0"},{"bloodOxygen":"98.30","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.50","heartRate":"74.61","body":"1"},{"bloodOxygen":"98.60","heartRate":"74.80","body":"3"},{"bloodOxygen":"98.70","heartRate":"74.61","body":"0"},{"bloodOxygen":"99.00","heartRate":"74.61","body":"0"},{"bloodOxygen":"99.00","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.80","heartRate":"74.61","body":"0"},{"bloodOxygen":"99.30","heartRate":"74.61","body":"1"},{"bloodOxygen":"99.10","heartRate":"74.61","body":"9"},{"bloodOxygen":"98.90","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.70","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.40","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.20","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.00","heartRate":"74.61","body":"0"},{"bloodOxygen":"97.80","heartRate":"74.61","body":"0"},{"bloodOxygen":"97.50","heartRate":"74.61","body":"0"},{"bloodOxygen":"97.40","heartRate":"74.61","body":"0"},{"bloodOxygen":"97.20","heartRate":"77.15","body":"0"},{"bloodOxygen":"97.00","heartRate":"76.56","body":"0"},{"bloodOxygen":"97.00","heartRate":"77.15","body":"0"},{"bloodOxygen":"97.10","heartRate":"76.95","body":"0"},{"bloodOxygen":"97.30","heartRate":"78.32","body":"0"},{"bloodOxygen":"97.60","heartRate":"77.34","body":"0"},{"bloodOxygen":"97.80","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.20","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.40","heartRate":"77.34","body":"2"},{"bloodOxygen":"98.60","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.60","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.30","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.20","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.00","heartRate":"77.34","body":"0"},{"bloodOxygen":"98.00","heartRate":"77.34","body":"0"},{"bloodOxygen":"97.80","heartRate":"58.79","body":"0"},{"bloodOxygen":"97.60","heartRate":"52.54","body":"0"},{"bloodOxygen":"97.60","heartRate":"54.88","body":"0"},{"bloodOxygen":"97.50","heartRate":"60.55","body":"0"},{"bloodOxygen":"97.50","heartRate":"56.25","body":"0"},{"bloodOxygen":"97.40","heartRate":"65.63","body":"0"},{"bloodOxygen":"97.30","heartRate":"72.66","body":"0"},{"bloodOxygen":"97.30","heartRate":"79.49","body":"0"},{"bloodOxygen":"97.30","heartRate":"77.93","body":"0"},{"bloodOxygen":"97.30","heartRate":"78.32","body":"0"},{"bloodOxygen":"97.30","heartRate":"75.39","body":"0"},{"bloodOxygen":"97.40","heartRate":"74.22","body":"0"},{"bloodOxygen":"97.70","heartRate":"72.85","body":"0"},{"bloodOxygen":"98.10","heartRate":"74.22","body":"0"},{"bloodOxygen":"98.40","heartRate":"74.41","body":"0"},{"bloodOxygen":"98.50","heartRate":"73.83","body":"0"},{"bloodOxygen":"98.60","heartRate":"74.22","body":"0"},{"bloodOxygen":"98.70","heartRate":"74.22","body":"0"},{"bloodOxygen":"98.90","heartRate":"74.02","body":"0"},{"bloodOxygen":"98.90","heartRate":"72.66","body":"0"},{"bloodOxygen":"98.90","heartRate":"73.05","body":"0"},{"bloodOxygen":"98.70","heartRate":"72.66","body":"0"},{"bloodOxygen":"98.60","heartRate":"71.88","body":"0"},{"bloodOxygen":"98.40","heartRate":"72.27","body":"0"},{"bloodOxygen":"98.40","heartRate":"72.27","body":"0"},{"bloodOxygen":"98.40","heartRate":"73.05","body":"0"},{"bloodOxygen":"98.30","heartRate":"73.24","body":"0"},{"bloodOxygen":"98.30","heartRate":"74.61","body":"0"},{"bloodOxygen":"98.20","heartRate":"74.22","body":"0"},{"bloodOxygen":"98.10","heartRate":"75.20","body":"0"},{"bloodOxygen":"98.20","heartRate":"74.22","body":"0"},{"bloodOxygen":"98.20","heartRate":"73.63","body":"0"},{"bloodOxygen":"98.10","heartRate":"73.83","body":"0"},{"bloodOxygen":"98.10","heartRate":"73.63","body":"0"},{"bloodOxygen":"98.20","heartRate":"74.02","body":"0"},{"bloodOxygen":"98.20","heartRate":"73.83","body":"0"},{"bloodOxygen":"98.00","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.90","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.70","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.80","heartRate":"74.02","body":"0"},{"bloodOxygen":"97.70","heartRate":"76.95","body":"0"},{"bloodOxygen":"97.50","heartRate":"76.17","body":"0"},{"bloodOxygen":"97.60","heartRate":"76.76","body":"0"},{"bloodOxygen":"97.50","heartRate":"77.34","body":"0"},{"bloodOxygen":"97.40","heartRate":"77.15","body":"0"},{"bloodOxygen":"97.30","heartRate":"76.95","body":"0"},{"bloodOxygen":"97.30","heartRate":"76.37","body":"0"},{"bloodOxygen":"97.20","heartRate":"76.17","body":"0"},{"bloodOxygen":"97.20","heartRate":"76.76","body":"0"},{"bloodOxygen":"97.30","heartRate":"78.13","body":"0"},{"bloodOxygen":"97.40","heartRate":"77.34","body":"0"},{"bloodOxygen":"97.50","heartRate":"77.93","body":"0"},{"bloodOxygen":"97.60","heartRate":"78.13","body":"0"},{"bloodOxygen":"97.70","heartRate":"78.13","body":"0"},{"bloodOxygen":"97.70","heartRate":"77.73","body":"0"},{"bloodOxygen":"101.30","heartRate":"68.55","body":"2"}]}
样式实现

注意:下拉框的使用的是Element UI的下拉框组件
<template>
    <div>
      <div style="min-height: 100vh;background-color: #eaf2ff;overflow: hidden;">
            <div class="phy-item" style="margin-top: 25px">
                <div>
                  <div style="margin-left: -895px;margin-top: 20px">
                        <img width="475px" height="36px" src="图片路径./upload/67c7ff09799b1.png" />
                  </div>
                  <div class="top-right-div">
                        <el-select v-model="selectValue" placeholder="请选择" @change="handlePathChange" v-loading.fullscreen.lock="fullscreenLoading">
                            <el-option
                                    v-for="item in selectListData"
                                    :key="item.value"
                                    :label="item.label"
                                    :value="item.value">
                            </el-option>
                        </el-select>
                  </div>
                </div>
                <div style="display: flex;margin-top: 70px;height: 500px;">
                  <div class="svg_class">
                        <!-- 显示饼图 -->
                        <svg :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" style="overflow: visible" @mouseleave="resetHighlight"><!-- 绘制饼图的每个扇形 -->
                            <path v-for="(slice, index) in slices" :key="index" :d="slice.path" :fill="slice.color" @mouseover="highlightSlice(index)"
                                  :style="{transform: slice.highlighted ? 'scale(1.1)' : 'scale(1)',transformOrigin: 'center',transition: 'transform 0.2s ease'}"/>
                            <!-- 显示当前悬停的数据 -->
                            <text v-if="hoveredIndex !== null" :x="width / 2" :y="height / 2" text-anchor="middle" dominant-baseline="middle" font-size="16" fill="#FFFFFF">
                              {{slices.label }}: {{ slices.value }}%
                            </text>
                        </svg>
                  </div>
                  <div class="mySleep_class">
                        <!-- 睡眠检测 -->
                        <canvas id="mySleep" ref="multiLineChartSleep" width="850" height="400"></canvas>
                  </div>
                </div>
            </div>

            <div class="phy-item" style="margin-top: 25px;">
                <div style="margin-left: -985px;margin-top: 20px">
                  <img width="383px" height="36px" src="图片路径./upload/67c8171b82805.png" />
                </div>
                <div style="margin-top: 20px;height: 480px;">
                  <!-- 原始 -->
                  <canvas id="myOriginal" ref="multiLineChartOriginal" width="1200" height="400" @mousemove="handleMouseMoveOriginal"></canvas>
                  <!-- 工具提示 -->
                  <div v-if="hoveredPoint" :style="tooltipStyle">
                        <p>{{ hoveredPoint.label }}: {{ hoveredPoint.value }}</p>
                  </div>
                </div>
            </div>
      </div>
    </div>
</template>

<script>
    import qs from "qs";

    export default {
      data() {
            return {
                // 示例数据
                originalData: {}, // 原始数据
                sleepData: {},// 睡眠数据
                bigCanvasData: {
                  sleepTime: 0,
                  bigCanvas: {
                        deepSleep: 0,
                        rapidSleep: 0,
                        eyeMovementSleep: 0,
                        sober: 0
                  }
                },// 统计数据
                // 每条线的颜色和标签
                lines: [
                  { label: '血氧', key: 'bloodOxygen', color: '#2956ff' },
                  { label: '心率', key: 'heartRate', color: '#ff3472' },
                  { label: '体动', key: 'body', color: '#ffa531' }
                ],
                // 鼠标悬停的数据点
                hoveredPoint: null,
                // 工具提示的位置
                tooltipStyle: {
                  position: 'fixed', // 使用 fixed 定位,确保工具提示相对于视口定位
                  top: '0',
                  left: '0',
                  // backgroundColor: 'white', 背景颜色
                  // border: '1px solid #ccc', 边框
                  padding: '10px',
                  display: 'none',
                  zIndex: 1000, // 设置较高的 z-index
                  pointerEvents: 'none' // 防止工具提示遮挡鼠标事件
                },
                //---------------------------睡眠数据-------------------------
                colorsSleep: [
                  { label: '深睡',color: '#2956ff', prevX: 120},
                  { label: '浅睡',color: '#5f81ff', prevX: 180},
                  { label: '眼动',color: '#97adff', prevX: 240},
                  { label: '清醒',color: '#ffb7b7', prevX: 300},
                ],
                //---------------------------扇形统计图高低数据-------------------------
                width: 240, // SVG 宽度
                height: 240, // SVG 高度
                hoveredIndex: null, // 当前悬停的扇形索引

                //---------------------------下拉数据-------------------------
                selectListData: [],
                selectValue: '',
                fullscreenLoading: true,
            };
      },
      computed: {
            // 计算每个扇形的数据
            slices() {
                const total = Object.values(this.bigCanvasData.bigCanvas).reduce((sum, value) => sum + value, 0);
                if (total === 0) return []; // 如果数据为空,返回空数组

                let startAngle = 0;
                return this.colorsSleep.map((color, index) => {
                  const value = Object.values(this.bigCanvasData.bigCanvas);
                  const angle = (value / total) * 360;
                  const path = this.calculateSlicePath(startAngle, angle);
                  startAngle += angle;

                  return {
                        label: color.label,
                        value: value,
                        color: color.color,
                        path: path,
                        highlighted: this.hoveredIndex === index
                  };
                });
            }
      },
      mounted() {
            this.doSelect();
      },
      methods: {
            async doSelect(){
                try {
                  // 通过异步接口获取调用接口的反馈状态和内容...
                  const {status: retCode, data: retJson} = await this.$http({
                        method: 'post',
                        url: '/wxapi.php/SleepTracker/doSelect'
                  });

                  // 如果状态码有错误...
                  if (retCode != 200) {
                        this.$message.error('获取列表数据失败!');
                        this.m_bIsLoading = false;
                        return;
                  }
                  // 获取失败的处理 => 显示获取到的错误信息...
                  if (retJson.err_code) {
                        this.$message.error(retJson.err_msg);
                        this.m_bIsLoading = false;
                        return;
                  }

                  this.selectListData = retJson.selectListData;
                  this.selectValue = this.selectListData.value;

                  // 获取数据
                  await this.doOriginalTrackerList();
                  await this.doSleepTrackerList();

                  this.fullscreenLoading = false;
                } catch (inError) {
                  console.log(inError);
                  this.$message.error(inError);
                }
            },
            // 原始数据获取
            async doOriginalTrackerList() {
                try {
                  // 然后,准备调用参数...
                  let thePostData = {
                        pathSleep: this.selectValue
                  }
                  // 通过异步接口获取调用接口的反馈状态和内容...
                  const {status: retCode, data: retJson} = await this.$http({
                        method: 'post',
                        url: '/wxapi.php/SleepTracker/readOriginal',
                        data: qs.stringify(thePostData)
                  });

                  // 如果状态码有错误...
                  if (retCode != 200) {
                        this.$message.error('获取原始数据失败!');
                        this.m_bIsLoading = false;
                        return;
                  }
                  // 获取失败的处理 => 显示获取到的错误信息...
                  if (retJson.err_code) {
                        this.$message.error(retJson.err_msg);
                        this.m_bIsLoading = false;
                        return;
                  }
                  this.originalData = retJson.OriginalList;
                  console.log('this.originalData: ', this.originalData);
                  this.drawMultiLineChartOriginal();
                } catch (inError) {
                  console.log(inError);
                  this.$message.error(inError);
                }
            },
            // 睡眠数据获取
            async doSleepTrackerList() {
                try {
                  let thePostData = {
                        pathSleep: this.selectValue
                  }
                  // 通过异步接口获取调用接口的反馈状态和内容...
                  const {status: retCode, data: retJson} = await this.$http({
                        method: 'post',
                        url: '/wxapi.php/SleepTracker/readSleep',
                        data: qs.stringify(thePostData)
                  });

                  // 如果状态码有错误...
                  if (retCode != 200) {
                        this.$message.error('获取睡眠数据失败!');
                        this.m_bIsLoading = false;
                        return;
                  }
                  // 获取失败的处理 => 显示获取到的错误信息...
                  if (retJson.err_code) {
                        this.$message.error(retJson.err_msg);
                        this.m_bIsLoading = false;
                        return;
                  }
                  this.sleepData = retJson.sleepList;
                  console.log('this.sleepData: ', this.sleepData);

                  this.bigCanvasData = retJson.bigCanvasData;
                  console.log('this.bigCanvasData: ', this.bigCanvasData);

                  this.drawSleepChartSleep();
                  this.calculateSlicePath();
                } catch (inError) {
                  console.log(inError);
                  this.$message.error(inError);
                }
            },
            // ------------------------------------------------------- 下拉框修改 ---------------------------------------------------------------------
            async handlePathChange(inProductID) {
                this.fullscreenLoading = true;
                console.log("inProductID: ",inProductID)
                this.selectValue = inProductID;

                // 获取数据
                await this.doOriginalTrackerList();
                await this.doSleepTrackerList();
                this.fullscreenLoading = false;
            },
            // ------------------------------------------------------- 饼形的路径 图 ---------------------------------------------------------------------
            calculateSlicePath(startAngle, angle) {
                const centerX = this.width / 2;
                const centerY = this.height / 2;
                const radius = Math.min(this.width, this.height) / 2;

                const start = this.polarToCartesian(centerX, centerY, radius, startAngle);
                const end = this.polarToCartesian(centerX, centerY, radius, startAngle + angle);

                const largeArcFlag = angle > 180 ? 1 : 0;

                return `M ${centerX} ${centerY} L ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${end.x} ${end.y} Z`;
            },
            // 将极坐标转换为笛卡尔坐标
            polarToCartesian(centerX, centerY, radius, angleInDegrees) {
                const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180;
                return {
                  x: centerX + radius * Math.cos(angleInRadians),
                  y: centerY + radius * Math.sin(angleInRadians)
                };
            },
            // 高亮扇形
            highlightSlice(index) {
                this.hoveredIndex = index;
            },
            // 重置高亮
            resetHighlight() {
                this.hoveredIndex = null;
            },
            // ------------------------------------------------------- 睡眠监控 图 ---------------------------------------------------------------------
            drawSleepChartSleep() {
                const canvas = this.$refs.multiLineChartSleep;
                const ctx = canvas.getContext('2d');
                ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空整个画布

                const padding = 50;
                const chartWidth = canvas.width - padding * 2;
                const chartHeight = canvas.height - padding * 2;

                // Y 轴固定值
                const yValues = ; // 深睡, 浅睡, 眼动, 清醒
                const yLabels = ['', '深睡', '浅睡', '眼动', '清醒'];

                // X 轴分成 当前睡了几个小时
                const xScale = chartWidth / (this.sleepData.groupCountSleep + 1);

                // 绘制 Y 轴
                ctx.beginPath();
                ctx.moveTo(padding, padding);
                ctx.lineTo(padding, padding + chartHeight);
                ctx.strokeStyle = '#ffffff'; // Y轴的线条用同背景颜色相同进行隐藏
                ctx.lineWidth = 1;
                ctx.stroke();

                // 绘制 Y 轴刻度
                ctx.fillStyle = '#666666';
                ctx.font = '14px Arial';
                ctx.textAlign = 'right';
                yValues.forEach((value, index) => {
                  // 调整 Y 轴刻度位置
                  const y = padding + chartHeight - (chartHeight / 4) * index;
                  ctx.fillText(yLabels, padding - 10, y+42);
                });

                // 绘制 X 轴
                ctx.beginPath();
                ctx.moveTo(padding, padding + chartHeight);
                ctx.lineTo(padding + chartWidth, padding + chartHeight);
                ctx.strokeStyle = '#CCCCCC';
                ctx.lineWidth = 1;
                ctx.stroke();

                // 绘制 X 轴刻度
                ctx.fillStyle = '#999999';
                ctx.font = '12px Arial';
                ctx.textAlign = 'center';

                for (let i = 0; i <= (this.sleepData.groupCountSleep + 1); i++) {
                  const x = padding + i * xScale;
                  ctx.fillText(i, x, padding + chartHeight + 20);
                }

                // // 绘制标题
                // ctx.fillStyle = '#000';
                // ctx.font = '18px Arial';
                // ctx.textAlign = 'center';
                // ctx.fillText('睡眠检测', padding, padding - 20);// 标题位置
                //
                // // 绘制圆点
                // for (let i = 1; i < 5; i++) {
                //   // 绘制圆点
                //   ctx.beginPath(); // 开始新的路径
                //   ctx.arc(padding + (60 * i), padding - 25, 5, 0, 2 * Math.PI); // 创建圆
                //   ctx.fillStyle = this.colorsSleep.color; // 设置填充颜色
                //   ctx.fill(); // 填充圆
                //   ctx.strokeStyle = this.colorsSleep.color; // 设置边框颜色
                //   ctx.stroke();
                //
                //   // 绘制标签
                //   ctx.fillStyle = '#000';
                //   ctx.font = '14px Arial';
                //   ctx.textAlign = 'left';
                //   // padding + 70 + (60 * (i-1))X轴的位置算法
                //   ctx.fillText(this.colorsSleep.label, this.colorsSleep.prevX, padding - 20);
                // }

                let prevX = padding+1;
                let prevY = padding + chartHeight - ((this.sleepData.sleepListData.sleepStatus) / 4) * chartHeight;

                const xStep = xScale / 60;// 现在数据总共有6个小时的数量,所以没一小时的x段又要分成60分
                ctx.lineWidth = 5;//线条粗细度
                this.sleepData.sleepListData.forEach((data, index) => {

                  const x = padding + (index * xStep)+1;
                  const y = padding + chartHeight - ((data.sleepStatus) / 4) * chartHeight;
                  const y2 = padding-3 + chartHeight - ((data.sleepStatus-1) / 4) * chartHeight;

                  if (index != 0 && this.sleepData.sleepListData.sleepStatus != data.sleepStatus){
                        ctx.beginPath(); // 开始新的路径
                        ctx.moveTo(x, y); // 移动到当前点
                  }else {
                        // 绘制线条
                        ctx.beginPath();
                        ctx.strokeStyle = this.colorsSleep.color; // 线条颜色
                        ctx.moveTo(prevX, prevY);
                        ctx.lineTo(x, y);
                        ctx.stroke();
                  }


                  // 绘制整个区域的线,造成一个区域全是这个颜色
                  for (let i = 1; i<= (y2-y); i++){
                        ctx.beginPath();
                        ctx.strokeStyle = this.colorsSleep.color; // 线条颜色
                        ctx.moveTo(prevX, y+i);
                        ctx.lineTo(x, y+i);
                        ctx.stroke();
                  }

                  // 更新上一个点的位置
                  prevX = x;
                  prevY = y;
                });
            },

            // ------------------------------------------------------- 原始数据 图 ---------------------------------------------------------------------
            drawMultiLineChartOriginal() {
                const canvas = this.$refs.multiLineChartOriginal;
                const ctx = canvas.getContext('2d');

                // 清空画布
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                // 设置图表区域的内边距
                const padding = 50;
                const chartWidth = canvas.width - padding * 2;
                const chartHeight = canvas.height - padding * 2;

                // 计算 X 轴的刻度
                const xScale = chartWidth / (this.originalData.resultCollection.length - 1);

                // 计算 Y 轴的最大值
                const maxY = Math.max(
                  ...this.originalData.resultCollection.map(d => parseFloat(d.bloodOxygen)),
                  ...this.originalData.resultCollection.map(d => parseFloat(d.heartRate)),
                  ...this.originalData.resultCollection.map(d => parseFloat(d.body))
                );

                // 绘制 X 轴和 Y 轴
                ctx.beginPath();
                ctx.moveTo(padding, padding);
                ctx.lineTo(padding, padding + chartHeight);
                ctx.lineTo(padding + chartWidth, padding + chartHeight);
                ctx.strokeStyle = '#CCCCCC';
                ctx.lineWidth = 2;
                ctx.stroke();

                // 绘制 X 轴刻度
                ctx.fillStyle = '#999999';
                ctx.font = '12px Arial';
                ctx.textAlign = 'center';
                const xStep = this.originalData.resultCollection.length / 10;
                for (let i = 0; i <= 10; i++) {
                  const x = padding + (i * xStep) * xScale;
                  const label = Math.round((i * xStep) * (this.originalData.countOriginal / this.originalData.resultCollection.length));
                  ctx.fillText(label, x, padding + chartHeight + 20);
                }

                // 绘制 Y 轴刻度
                ctx.textAlign = 'right';
                for (let i = 0; i <= 10; i++) {
                  const y = padding + chartHeight - (chartHeight / 10) * i;
                  const value = (maxY / 10) * i;
                  ctx.fillText(value.toFixed(1), padding - 10, y + 5);
                }

                // 绘制三条线和数据点
                this.lines.forEach(line => {
                  this.drawLineOriginal(ctx, line, padding, chartHeight, xScale, maxY);
                  this.drawDataPointsOriginal(ctx, line, padding, chartHeight, xScale, maxY);
                });

                // 在统计图内部绘制标题和图例
                // this.drawTitleAndLegendOriginal(ctx, padding, chartWidth, chartHeight);
            },
            drawLineOriginal(ctx, line, padding, chartHeight, xScale, maxY) {
                const data = this.originalData.resultCollection.map(d => parseFloat(d));
                ctx.beginPath();
                ctx.moveTo(padding, padding + chartHeight - (data / maxY) * chartHeight);
                data.forEach((value, index) => {
                  const x = padding + index * xScale;
                  const y = padding + chartHeight - (value / maxY) * chartHeight;
                  ctx.lineTo(x, y);
                });
                ctx.strokeStyle = line.color;
                ctx.lineWidth = 2;
                ctx.stroke();
            },
            drawDataPointsOriginal(ctx, line, padding, chartHeight, xScale, maxY) {
                const data = this.originalData.resultCollection.map(d => parseFloat(d));
                data.forEach((value, index) => {
                  const x = padding + index * xScale;
                  const y = padding + chartHeight - (value / maxY) * chartHeight;
                  ctx.beginPath();
                  ctx.fill();
                });
            },
            drawTitleAndLegendOriginal(ctx, padding, chartWidth, chartHeight) {
                // 绘制标题
                ctx.fillStyle = '#000';
                ctx.font = '18px Arial';
                ctx.textAlign = 'center';
                ctx.fillText('原始数据', padding + chartWidth / 2, padding - 30);// 标题位置

                // 绘制图例
                const legendX = padding + 20; // 图例的起始 X 坐标
                const legendY = padding + 20; // 图例的起始 Y 坐标
                const legendItemHeight = 20; // 每个图例项的高度
                const legendColorBoxSize = 16; // 颜色方块的大小

                this.lines.forEach((line, index) => {
                  // 绘制颜色方块
                  ctx.fillStyle = line.color;
                  ctx.fillRect((padding + chartWidth / 2) - (70 - (60 * index)), (legendY + legendItemHeight) - 60, legendColorBoxSize, legendColorBoxSize);

                  // 绘制标签
                  ctx.fillStyle = '#000';
                  ctx.font = '14px Arial';
                  ctx.textAlign = 'left';
                  // 第一个当前50(-0) 第二个当前 -10(-60) 第三个当前 70(-60)
                  ctx.fillText(line.label, (padding + chartWidth / 2) - (50 - (60 * index)), (legendY + legendItemHeight + 14) - 60);
                });
            },

            handleMouseMoveOriginal(event) {
                const canvas = this.$refs.multiLineChartOriginal;
                const rect = canvas.getBoundingClientRect();
                const mouseX = event.clientX - rect.left;
                const mouseY = event.clientY - rect.top;

                const padding = 50;
                const chartWidth = canvas.width - padding * 2;
                const chartHeight = canvas.height - padding * 2;
                const xScale = chartWidth / (this.originalData.resultCollection.length - 1);
                const maxY = Math.max(
                  ...this.originalData.resultCollection.map(d => parseFloat(d.bloodOxygen)),
                  ...this.originalData.resultCollection.map(d => parseFloat(d.heartRate)),
                  ...this.originalData.resultCollection.map(d => parseFloat(d.body))
                );

                // 查找鼠标附近的数据点
                this.hoveredPoint = null;
                this.lines.forEach(line => {
                  const data = this.originalData.resultCollection.map(d => parseFloat(d));
                  data.forEach((value, index) => {
                        const x = padding + index * xScale;
                        const y = padding + chartHeight - (value / maxY) * chartHeight;
                        if (Math.abs(mouseX - x) < 10 && Math.abs(mouseY - y) < 10) {
                            this.hoveredPoint = {
                              label: line.label,
                              value: value
                            };
                            this.tooltipStyle.display = 'block';
                            this.tooltipStyle.left = `${event.clientX + 10}px`;
                            this.tooltipStyle.top = `${event.clientY + 10}px`;
                        }
                  });
                });

                if (!this.hoveredPoint) {
                  this.tooltipStyle.display = 'none';
                }
            }
      },
    }
</script>

<style scoped>
    /*canvas {*/
    /*    border: 1px solid #ddd; !* 外部边框 *!*/
    /*    background-color: #f9f9f9; !* 背景颜色 *!*/
    /*}*/
    #myOriginal {
      margin: 0 auto;            /* 设置左右外边距为 auto,以便居中 */
      display: block;            /* 将 canvas 设置为块级元素 */
    }

    .phy-item {
      display: flex;
      flex-direction: column; /* 纵向排列 */
      margin: 0 auto; /* 设置左右外边距为 auto,以便居中 */
      justify-content: space-between; /* 将子元素分布在容器的两端 */
      align-items: center; /* 垂直居中 */
      text-align: center;
      position: relative;
      width: 1400px;
      height: 500px;
      background: #ffffff; /* 背景色 */
      border-radius: 16px;
      box-shadow:
                -10px -10px 20px rgba(224, 237, 252, 0.8), /* 左上角渐变颜色 */
                10px 10px 20px rgba(241, 248, 254, 0.8); /* 右下角渐变颜色 */
      flex-grow: 1; /* 让子元素填充父元素的空间 */
    }
    .mySleep_class{
      flex: 2; /* 线条图占据 2/3 的空间 */
      margin-left: 105px;
      margin-top: -50px;
    }
    .svg_class{
      flex: 1; /* 饼图占据剩余空间的一半 */
      margin-top: 40px; /* 设置左右外边距为 auto,以便居中 */
    }

    .top-right-div {
      position: absolute; /* 使用绝对定位 */
      top: 17px; /* 距离顶部 40px */
      right: 25px; /* 距离右侧 10px */
    }

    /*#mySleep {*/

    /*}*/
</style>

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Vue纯JS使用canvas实现统计图