Vue纯JS使用canvas实现统计图

打印 上一主题 下一主题

主题 917|帖子 917|积分 2751

实现展示




数据结构

sleepData 睡眠数据统计图

  1. 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 饼形统计图

  1. bigCanvasData:{"sleepTime":6.35,"bigCanvas":{"deepSleep":37.9,"rapidSleep":32.9,"eyeMovementSleep":20.3,"sober":8.9}}
复制代码
originalData 三线统计图

  1. 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的下拉框组件
  1. <template>
  2.     <div>
  3.         <div style="min-height: 100vh;background-color: #eaf2ff;overflow: hidden;">
  4.             <div class="phy-item" style="margin-top: 25px">
  5.                 <div>
  6.                     <div style="margin-left: -895px;margin-top: 20px">
  7.                         <img width="475px" height="36px" src="图片路径./upload/67c7ff09799b1.png" />
  8.                     </div>
  9.                     <div class="top-right-div">
  10.                         <el-select v-model="selectValue" placeholder="请选择" @change="handlePathChange" v-loading.fullscreen.lock="fullscreenLoading">
  11.                             <el-option
  12.                                     v-for="item in selectListData"
  13.                                     :key="item.value"
  14.                                     :label="item.label"
  15.                                     :value="item.value">
  16.                             </el-option>
  17.                         </el-select>
  18.                     </div>
  19.                 </div>
  20.                 <div style="display: flex;margin-top: 70px;height: 500px;">
  21.                     <div class="svg_class">
  22.                         <!-- 显示饼图 -->
  23.                         <svg :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" style="overflow: visible" @mouseleave="resetHighlight"><!-- 绘制饼图的每个扇形 -->
  24.                             <path v-for="(slice, index) in slices" :key="index" :d="slice.path" :fill="slice.color" @mouseover="highlightSlice(index)"
  25.                                   :style="{transform: slice.highlighted ? 'scale(1.1)' : 'scale(1)',transformOrigin: 'center',transition: 'transform 0.2s ease'}"/>
  26.                             <!-- 显示当前悬停的数据 -->
  27.                             <text v-if="hoveredIndex !== null" :x="width / 2" :y="height / 2" text-anchor="middle" dominant-baseline="middle" font-size="16" fill="#FFFFFF">
  28.                                 {{slices[hoveredIndex].label }}: {{ slices[hoveredIndex].value }}%
  29.                             </text>
  30.                         </svg>
  31.                     </div>
  32.                     <div class="mySleep_class">
  33.                         <!-- 睡眠检测 -->
  34.                         <canvas id="mySleep" ref="multiLineChartSleep" width="850" height="400"></canvas>
  35.                     </div>
  36.                 </div>
  37.             </div>
  38.             <div class="phy-item" style="margin-top: 25px;">
  39.                 <div style="margin-left: -985px;margin-top: 20px">
  40.                     <img width="383px" height="36px" src="图片路径./upload/67c8171b82805.png" />
  41.                 </div>
  42.                 <div style="margin-top: 20px;height: 480px;">
  43.                     <!-- 原始 -->
  44.                     <canvas id="myOriginal" ref="multiLineChartOriginal" width="1200" height="400" @mousemove="handleMouseMoveOriginal"></canvas>
  45.                     <!-- 工具提示 -->
  46.                     <div v-if="hoveredPoint" :style="tooltipStyle">
  47.                         <p>{{ hoveredPoint.label }}: {{ hoveredPoint.value }}</p>
  48.                     </div>
  49.                 </div>
  50.             </div>
  51.         </div>
  52.     </div>
  53. </template>
  54. <script>
  55.     import qs from "qs";
  56.     export default {
  57.         data() {
  58.             return {
  59.                 // 示例数据
  60.                 originalData: {}, // 原始数据
  61.                 sleepData: {},// 睡眠数据
  62.                 bigCanvasData: {
  63.                     sleepTime: 0,
  64.                     bigCanvas: {
  65.                         deepSleep: 0,
  66.                         rapidSleep: 0,
  67.                         eyeMovementSleep: 0,
  68.                         sober: 0
  69.                     }
  70.                 },// 统计数据
  71.                 // 每条线的颜色和标签
  72.                 lines: [
  73.                     { label: '血氧', key: 'bloodOxygen', color: '#2956ff' },
  74.                     { label: '心率', key: 'heartRate', color: '#ff3472' },
  75.                     { label: '体动', key: 'body', color: '#ffa531' }
  76.                 ],
  77.                 // 鼠标悬停的数据点
  78.                 hoveredPoint: null,
  79.                 // 工具提示的位置
  80.                 tooltipStyle: {
  81.                     position: 'fixed', // 使用 fixed 定位,确保工具提示相对于视口定位
  82.                     top: '0',
  83.                     left: '0',
  84.                     // backgroundColor: 'white', 背景颜色
  85.                     // border: '1px solid #ccc', 边框
  86.                     padding: '10px',
  87.                     display: 'none',
  88.                     zIndex: 1000, // 设置较高的 z-index
  89.                     pointerEvents: 'none' // 防止工具提示遮挡鼠标事件
  90.                 },
  91.                 //---------------------------睡眠数据-------------------------
  92.                 colorsSleep: [
  93.                     { label: '深睡',  color: '#2956ff', prevX: 120},
  94.                     { label: '浅睡',  color: '#5f81ff', prevX: 180},
  95.                     { label: '眼动',  color: '#97adff', prevX: 240},
  96.                     { label: '清醒',  color: '#ffb7b7', prevX: 300},
  97.                 ],
  98.                 //---------------------------扇形统计图高低数据-------------------------
  99.                 width: 240, // SVG 宽度
  100.                 height: 240, // SVG 高度
  101.                 hoveredIndex: null, // 当前悬停的扇形索引
  102.                 //---------------------------下拉数据-------------------------
  103.                 selectListData: [],
  104.                 selectValue: '',
  105.                 fullscreenLoading: true,
  106.             };
  107.         },
  108.         computed: {
  109.             // 计算每个扇形的数据
  110.             slices() {
  111.                 const total = Object.values(this.bigCanvasData.bigCanvas).reduce((sum, value) => sum + value, 0);
  112.                 if (total === 0) return []; // 如果数据为空,返回空数组
  113.                 let startAngle = 0;
  114.                 return this.colorsSleep.map((color, index) => {
  115.                     const value = Object.values(this.bigCanvasData.bigCanvas)[index];
  116.                     const angle = (value / total) * 360;
  117.                     const path = this.calculateSlicePath(startAngle, angle);
  118.                     startAngle += angle;
  119.                     return {
  120.                         label: color.label,
  121.                         value: value,
  122.                         color: color.color,
  123.                         path: path,
  124.                         highlighted: this.hoveredIndex === index
  125.                     };
  126.                 });
  127.             }
  128.         },
  129.         mounted() {
  130.             this.doSelect();
  131.         },
  132.         methods: {
  133.             async doSelect(){
  134.                 try {
  135.                     // 通过异步接口获取调用接口的反馈状态和内容...
  136.                     const {status: retCode, data: retJson} = await this.$http({
  137.                         method: 'post',
  138.                         url: '/wxapi.php/SleepTracker/doSelect'
  139.                     });
  140.                     // 如果状态码有错误...
  141.                     if (retCode != 200) {
  142.                         this.$message.error('获取列表数据失败!');
  143.                         this.m_bIsLoading = false;
  144.                         return;
  145.                     }
  146.                     // 获取失败的处理 => 显示获取到的错误信息...
  147.                     if (retJson.err_code) {
  148.                         this.$message.error(retJson.err_msg);
  149.                         this.m_bIsLoading = false;
  150.                         return;
  151.                     }
  152.                     this.selectListData = retJson.selectListData;
  153.                     this.selectValue = this.selectListData[0].value;
  154.                     // 获取数据
  155.                     await this.doOriginalTrackerList();
  156.                     await this.doSleepTrackerList();
  157.                     this.fullscreenLoading = false;
  158.                 } catch (inError) {
  159.                     console.log(inError);
  160.                     this.$message.error(inError);
  161.                 }
  162.             },
  163.             // 原始数据获取
  164.             async doOriginalTrackerList() {
  165.                 try {
  166.                     // 然后,准备调用参数...
  167.                     let thePostData = {
  168.                         pathSleep: this.selectValue
  169.                     }
  170.                     // 通过异步接口获取调用接口的反馈状态和内容...
  171.                     const {status: retCode, data: retJson} = await this.$http({
  172.                         method: 'post',
  173.                         url: '/wxapi.php/SleepTracker/readOriginal',
  174.                         data: qs.stringify(thePostData)
  175.                     });
  176.                     // 如果状态码有错误...
  177.                     if (retCode != 200) {
  178.                         this.$message.error('获取原始数据失败!');
  179.                         this.m_bIsLoading = false;
  180.                         return;
  181.                     }
  182.                     // 获取失败的处理 => 显示获取到的错误信息...
  183.                     if (retJson.err_code) {
  184.                         this.$message.error(retJson.err_msg);
  185.                         this.m_bIsLoading = false;
  186.                         return;
  187.                     }
  188.                     this.originalData = retJson.OriginalList;
  189.                     console.log('this.originalData: ', this.originalData);
  190.                     this.drawMultiLineChartOriginal();
  191.                 } catch (inError) {
  192.                     console.log(inError);
  193.                     this.$message.error(inError);
  194.                 }
  195.             },
  196.             // 睡眠数据获取
  197.             async doSleepTrackerList() {
  198.                 try {
  199.                     let thePostData = {
  200.                         pathSleep: this.selectValue
  201.                     }
  202.                     // 通过异步接口获取调用接口的反馈状态和内容...
  203.                     const {status: retCode, data: retJson} = await this.$http({
  204.                         method: 'post',
  205.                         url: '/wxapi.php/SleepTracker/readSleep',
  206.                         data: qs.stringify(thePostData)
  207.                     });
  208.                     // 如果状态码有错误...
  209.                     if (retCode != 200) {
  210.                         this.$message.error('获取睡眠数据失败!');
  211.                         this.m_bIsLoading = false;
  212.                         return;
  213.                     }
  214.                     // 获取失败的处理 => 显示获取到的错误信息...
  215.                     if (retJson.err_code) {
  216.                         this.$message.error(retJson.err_msg);
  217.                         this.m_bIsLoading = false;
  218.                         return;
  219.                     }
  220.                     this.sleepData = retJson.sleepList;
  221.                     console.log('this.sleepData: ', this.sleepData);
  222.                     this.bigCanvasData = retJson.bigCanvasData;
  223.                     console.log('this.bigCanvasData: ', this.bigCanvasData);
  224.                     this.drawSleepChartSleep();
  225.                     this.calculateSlicePath();
  226.                 } catch (inError) {
  227.                     console.log(inError);
  228.                     this.$message.error(inError);
  229.                 }
  230.             },
  231.             // ------------------------------------------------------- 下拉框修改 ---------------------------------------------------------------------
  232.             async handlePathChange(inProductID) {
  233.                 this.fullscreenLoading = true;
  234.                 console.log("inProductID: ",inProductID)
  235.                 this.selectValue = inProductID;
  236.                 // 获取数据
  237.                 await this.doOriginalTrackerList();
  238.                 await this.doSleepTrackerList();
  239.                 this.fullscreenLoading = false;
  240.             },
  241.             // ------------------------------------------------------- 饼形的路径 图 ---------------------------------------------------------------------
  242.             calculateSlicePath(startAngle, angle) {
  243.                 const centerX = this.width / 2;
  244.                 const centerY = this.height / 2;
  245.                 const radius = Math.min(this.width, this.height) / 2;
  246.                 const start = this.polarToCartesian(centerX, centerY, radius, startAngle);
  247.                 const end = this.polarToCartesian(centerX, centerY, radius, startAngle + angle);
  248.                 const largeArcFlag = angle > 180 ? 1 : 0;
  249.                 return `M ${centerX} ${centerY} L ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${end.x} ${end.y} Z`;
  250.             },
  251.             // 将极坐标转换为笛卡尔坐标
  252.             polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  253.                 const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180;
  254.                 return {
  255.                     x: centerX + radius * Math.cos(angleInRadians),
  256.                     y: centerY + radius * Math.sin(angleInRadians)
  257.                 };
  258.             },
  259.             // 高亮扇形
  260.             highlightSlice(index) {
  261.                 this.hoveredIndex = index;
  262.             },
  263.             // 重置高亮
  264.             resetHighlight() {
  265.                 this.hoveredIndex = null;
  266.             },
  267.             // ------------------------------------------------------- 睡眠监控 图 ---------------------------------------------------------------------
  268.             drawSleepChartSleep() {
  269.                 const canvas = this.$refs.multiLineChartSleep;
  270.                 const ctx = canvas.getContext('2d');
  271.                 ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空整个画布
  272.                 const padding = 50;
  273.                 const chartWidth = canvas.width - padding * 2;
  274.                 const chartHeight = canvas.height - padding * 2;
  275.                 // Y 轴固定值
  276.                 const yValues = [0, 1, 2, 3, 4]; // 深睡, 浅睡, 眼动, 清醒
  277.                 const yLabels = ['', '深睡', '浅睡', '眼动', '清醒'];
  278.                 // X 轴分成 当前睡了几个小时
  279.                 const xScale = chartWidth / (this.sleepData.groupCountSleep + 1);
  280.                 // 绘制 Y 轴
  281.                 ctx.beginPath();
  282.                 ctx.moveTo(padding, padding);
  283.                 ctx.lineTo(padding, padding + chartHeight);
  284.                 ctx.strokeStyle = '#ffffff'; // Y轴的线条用同背景颜色相同进行隐藏
  285.                 ctx.lineWidth = 1;
  286.                 ctx.stroke();
  287.                 // 绘制 Y 轴刻度
  288.                 ctx.fillStyle = '#666666';
  289.                 ctx.font = '14px Arial';
  290.                 ctx.textAlign = 'right';
  291.                 yValues.forEach((value, index) => {
  292.                     // 调整 Y 轴刻度位置
  293.                     const y = padding + chartHeight - (chartHeight / 4) * index;
  294.                     ctx.fillText(yLabels[index], padding - 10, y+42);
  295.                 });
  296.                 // 绘制 X 轴
  297.                 ctx.beginPath();
  298.                 ctx.moveTo(padding, padding + chartHeight);
  299.                 ctx.lineTo(padding + chartWidth, padding + chartHeight);
  300.                 ctx.strokeStyle = '#CCCCCC';
  301.                 ctx.lineWidth = 1;
  302.                 ctx.stroke();
  303.                 // 绘制 X 轴刻度
  304.                 ctx.fillStyle = '#999999';
  305.                 ctx.font = '12px Arial';
  306.                 ctx.textAlign = 'center';
  307.                 for (let i = 0; i <= (this.sleepData.groupCountSleep + 1); i++) {
  308.                     const x = padding + i * xScale;
  309.                     ctx.fillText(i, x, padding + chartHeight + 20);
  310.                 }
  311.                 // // 绘制标题
  312.                 // ctx.fillStyle = '#000';
  313.                 // ctx.font = '18px Arial';
  314.                 // ctx.textAlign = 'center';
  315.                 // ctx.fillText('睡眠检测', padding, padding - 20);// 标题位置
  316.                 //
  317.                 // // 绘制圆点
  318.                 // for (let i = 1; i < 5; i++) {
  319.                 //     // 绘制圆点
  320.                 //     ctx.beginPath(); // 开始新的路径
  321.                 //     ctx.arc(padding + (60 * i), padding - 25, 5, 0, 2 * Math.PI); // 创建圆
  322.                 //     ctx.fillStyle = this.colorsSleep[i - 1].color; // 设置填充颜色
  323.                 //     ctx.fill(); // 填充圆
  324.                 //     ctx.strokeStyle = this.colorsSleep[i - 1].color; // 设置边框颜色
  325.                 //     ctx.stroke();
  326.                 //
  327.                 //     // 绘制标签
  328.                 //     ctx.fillStyle = '#000';
  329.                 //     ctx.font = '14px Arial';
  330.                 //     ctx.textAlign = 'left';
  331.                 //     // padding + 70 + (60 * (i-1))  X轴的位置算法
  332.                 //     ctx.fillText(this.colorsSleep[i - 1].label, this.colorsSleep[i - 1].prevX, padding - 20);
  333.                 // }
  334.                 let prevX = padding+1;
  335.                 let prevY = padding + chartHeight - ((this.sleepData.sleepListData[0].sleepStatus) / 4) * chartHeight;
  336.                 const xStep = xScale / 60;// 现在数据总共有6个小时的数量,所以没一小时的x段又要分成60分
  337.                 ctx.lineWidth = 5;//线条粗细度
  338.                 this.sleepData.sleepListData.forEach((data, index) => {
  339.                     const x = padding + (index * xStep)+1;
  340.                     const y = padding + chartHeight - ((data.sleepStatus) / 4) * chartHeight;
  341.                     const y2 = padding-3 + chartHeight - ((data.sleepStatus-1) / 4) * chartHeight;
  342.                     if (index != 0 && this.sleepData.sleepListData[index-1].sleepStatus != data.sleepStatus){
  343.                         ctx.beginPath(); // 开始新的路径
  344.                         ctx.moveTo(x, y); // 移动到当前点
  345.                     }else {
  346.                         // 绘制线条
  347.                         ctx.beginPath();
  348.                         ctx.strokeStyle = this.colorsSleep[data.sleepStatus - 1].color; // 线条颜色
  349.                         ctx.moveTo(prevX, prevY);
  350.                         ctx.lineTo(x, y);
  351.                         ctx.stroke();
  352.                     }
  353.                     // 绘制整个区域的线,造成一个区域全是这个颜色
  354.                     for (let i = 1; i<= (y2-y); i++){
  355.                         ctx.beginPath();
  356.                         ctx.strokeStyle = this.colorsSleep[data.sleepStatus - 1].color; // 线条颜色
  357.                         ctx.moveTo(prevX, y+i);
  358.                         ctx.lineTo(x, y+i);
  359.                         ctx.stroke();
  360.                     }
  361.                     // 更新上一个点的位置
  362.                     prevX = x;
  363.                     prevY = y;
  364.                 });
  365.             },
  366.             // ------------------------------------------------------- 原始数据 图 ---------------------------------------------------------------------
  367.             drawMultiLineChartOriginal() {
  368.                 const canvas = this.$refs.multiLineChartOriginal;
  369.                 const ctx = canvas.getContext('2d');
  370.                 // 清空画布
  371.                 ctx.clearRect(0, 0, canvas.width, canvas.height);
  372.                 // 设置图表区域的内边距
  373.                 const padding = 50;
  374.                 const chartWidth = canvas.width - padding * 2;
  375.                 const chartHeight = canvas.height - padding * 2;
  376.                 // 计算 X 轴的刻度
  377.                 const xScale = chartWidth / (this.originalData.resultCollection.length - 1);
  378.                 // 计算 Y 轴的最大值
  379.                 const maxY = Math.max(
  380.                     ...this.originalData.resultCollection.map(d => parseFloat(d.bloodOxygen)),
  381.                     ...this.originalData.resultCollection.map(d => parseFloat(d.heartRate)),
  382.                     ...this.originalData.resultCollection.map(d => parseFloat(d.body))
  383.                 );
  384.                 // 绘制 X 轴和 Y 轴
  385.                 ctx.beginPath();
  386.                 ctx.moveTo(padding, padding);
  387.                 ctx.lineTo(padding, padding + chartHeight);
  388.                 ctx.lineTo(padding + chartWidth, padding + chartHeight);
  389.                 ctx.strokeStyle = '#CCCCCC';
  390.                 ctx.lineWidth = 2;
  391.                 ctx.stroke();
  392.                 // 绘制 X 轴刻度
  393.                 ctx.fillStyle = '#999999';
  394.                 ctx.font = '12px Arial';
  395.                 ctx.textAlign = 'center';
  396.                 const xStep = this.originalData.resultCollection.length / 10;
  397.                 for (let i = 0; i <= 10; i++) {
  398.                     const x = padding + (i * xStep) * xScale;
  399.                     const label = Math.round((i * xStep) * (this.originalData.countOriginal / this.originalData.resultCollection.length));
  400.                     ctx.fillText(label, x, padding + chartHeight + 20);
  401.                 }
  402.                 // 绘制 Y 轴刻度
  403.                 ctx.textAlign = 'right';
  404.                 for (let i = 0; i <= 10; i++) {
  405.                     const y = padding + chartHeight - (chartHeight / 10) * i;
  406.                     const value = (maxY / 10) * i;
  407.                     ctx.fillText(value.toFixed(1), padding - 10, y + 5);
  408.                 }
  409.                 // 绘制三条线和数据点
  410.                 this.lines.forEach(line => {
  411.                     this.drawLineOriginal(ctx, line, padding, chartHeight, xScale, maxY);
  412.                     this.drawDataPointsOriginal(ctx, line, padding, chartHeight, xScale, maxY);
  413.                 });
  414.                 // 在统计图内部绘制标题和图例
  415.                 // this.drawTitleAndLegendOriginal(ctx, padding, chartWidth, chartHeight);
  416.             },
  417.             drawLineOriginal(ctx, line, padding, chartHeight, xScale, maxY) {
  418.                 const data = this.originalData.resultCollection.map(d => parseFloat(d[line.key]));
  419.                 ctx.beginPath();
  420.                 ctx.moveTo(padding, padding + chartHeight - (data[0] / maxY) * chartHeight);
  421.                 data.forEach((value, index) => {
  422.                     const x = padding + index * xScale;
  423.                     const y = padding + chartHeight - (value / maxY) * chartHeight;
  424.                     ctx.lineTo(x, y);
  425.                 });
  426.                 ctx.strokeStyle = line.color;
  427.                 ctx.lineWidth = 2;
  428.                 ctx.stroke();
  429.             },
  430.             drawDataPointsOriginal(ctx, line, padding, chartHeight, xScale, maxY) {
  431.                 const data = this.originalData.resultCollection.map(d => parseFloat(d[line.key]));
  432.                 data.forEach((value, index) => {
  433.                     const x = padding + index * xScale;
  434.                     const y = padding + chartHeight - (value / maxY) * chartHeight;
  435.                     ctx.beginPath();
  436.                     ctx.fill();
  437.                 });
  438.             },
  439.             drawTitleAndLegendOriginal(ctx, padding, chartWidth, chartHeight) {
  440.                 // 绘制标题
  441.                 ctx.fillStyle = '#000';
  442.                 ctx.font = '18px Arial';
  443.                 ctx.textAlign = 'center';
  444.                 ctx.fillText('原始数据', padding + chartWidth / 2, padding - 30);// 标题位置
  445.                 // 绘制图例
  446.                 const legendX = padding + 20; // 图例的起始 X 坐标
  447.                 const legendY = padding + 20; // 图例的起始 Y 坐标
  448.                 const legendItemHeight = 20; // 每个图例项的高度
  449.                 const legendColorBoxSize = 16; // 颜色方块的大小
  450.                 this.lines.forEach((line, index) => {
  451.                     // 绘制颜色方块
  452.                     ctx.fillStyle = line.color;
  453.                     ctx.fillRect((padding + chartWidth / 2) - (70 - (60 * index)), (legendY + legendItemHeight) - 60, legendColorBoxSize, legendColorBoxSize);
  454.                     // 绘制标签
  455.                     ctx.fillStyle = '#000';
  456.                     ctx.font = '14px Arial';
  457.                     ctx.textAlign = 'left';
  458.                     // 第一个当前50(-0) 第二个当前 -10(-60) 第三个当前 70(-60)
  459.                     ctx.fillText(line.label, (padding + chartWidth / 2) - (50 - (60 * index)), (legendY + legendItemHeight + 14) - 60);
  460.                 });
  461.             },
  462.             handleMouseMoveOriginal(event) {
  463.                 const canvas = this.$refs.multiLineChartOriginal;
  464.                 const rect = canvas.getBoundingClientRect();
  465.                 const mouseX = event.clientX - rect.left;
  466.                 const mouseY = event.clientY - rect.top;
  467.                 const padding = 50;
  468.                 const chartWidth = canvas.width - padding * 2;
  469.                 const chartHeight = canvas.height - padding * 2;
  470.                 const xScale = chartWidth / (this.originalData.resultCollection.length - 1);
  471.                 const maxY = Math.max(
  472.                     ...this.originalData.resultCollection.map(d => parseFloat(d.bloodOxygen)),
  473.                     ...this.originalData.resultCollection.map(d => parseFloat(d.heartRate)),
  474.                     ...this.originalData.resultCollection.map(d => parseFloat(d.body))
  475.                 );
  476.                 // 查找鼠标附近的数据点
  477.                 this.hoveredPoint = null;
  478.                 this.lines.forEach(line => {
  479.                     const data = this.originalData.resultCollection.map(d => parseFloat(d[line.key]));
  480.                     data.forEach((value, index) => {
  481.                         const x = padding + index * xScale;
  482.                         const y = padding + chartHeight - (value / maxY) * chartHeight;
  483.                         if (Math.abs(mouseX - x) < 10 && Math.abs(mouseY - y) < 10) {
  484.                             this.hoveredPoint = {
  485.                                 label: line.label,
  486.                                 value: value
  487.                             };
  488.                             this.tooltipStyle.display = 'block';
  489.                             this.tooltipStyle.left = `${event.clientX + 10}px`;
  490.                             this.tooltipStyle.top = `${event.clientY + 10}px`;
  491.                         }
  492.                     });
  493.                 });
  494.                 if (!this.hoveredPoint) {
  495.                     this.tooltipStyle.display = 'none';
  496.                 }
  497.             }
  498.         },
  499.     }
  500. </script>
  501. <style scoped>
  502.     /*canvas {*/
  503.     /*    border: 1px solid #ddd; !* 外部边框 *!*/
  504.     /*    background-color: #f9f9f9; !* 背景颜色 *!*/
  505.     /*}*/
  506.     #myOriginal {
  507.         margin: 0 auto;              /* 设置左右外边距为 auto,以便居中 */
  508.         display: block;              /* 将 canvas 设置为块级元素 */
  509.     }
  510.     .phy-item {
  511.         display: flex;
  512.         flex-direction: column; /* 纵向排列 */
  513.         margin: 0 auto; /* 设置左右外边距为 auto,以便居中 */
  514.         justify-content: space-between; /* 将子元素分布在容器的两端 */
  515.         align-items: center; /* 垂直居中 */
  516.         text-align: center;
  517.         position: relative;
  518.         width: 1400px;
  519.         height: 500px;
  520.         background: #ffffff; /* 背景色 */
  521.         border-radius: 16px;
  522.         box-shadow:
  523.                 -10px -10px 20px rgba(224, 237, 252, 0.8), /* 左上角渐变颜色 */
  524.                 10px 10px 20px rgba(241, 248, 254, 0.8); /* 右下角渐变颜色 */
  525.         flex-grow: 1; /* 让子元素填充父元素的空间 */
  526.     }
  527.     .mySleep_class{
  528.         flex: 2; /* 线条图占据 2/3 的空间 */
  529.         margin-left: 105px;
  530.         margin-top: -50px;
  531.     }
  532.     .svg_class{
  533.         flex: 1; /* 饼图占据剩余空间的一半 */
  534.         margin-top: 40px; /* 设置左右外边距为 auto,以便居中 */
  535.     }
  536.     .top-right-div {
  537.         position: absolute; /* 使用绝对定位 */
  538.         top: 17px; /* 距离顶部 40px */
  539.         right: 25px; /* 距离右侧 10px */
  540.     }
  541.     /*#mySleep {*/
  542.     /*}*/
  543. </style>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表