humanBody.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <template>
  2. <div class="game-container">
  3. <canvas ref="canvasRef" :width="clientObj.width" :height="clientObj.height"></canvas>
  4. </div>
  5. </template>
  6. <script setup name="HumanBody" lang="ts">
  7. const { proxy } = getCurrentInstance() as any;
  8. const router = useRouter();
  9. const canvasRef = ref(null);
  10. const data = reactive<any>({
  11. bodyposeData: {},//姿态信息
  12. clientObj: {},//浏览器对象
  13. boxes: [],//四个点坐标
  14. proportion: null,//人框和屏幕比例
  15. });
  16. const { bodyposeData, clientObj, boxes, proportion } = toRefs(data);
  17. /**
  18. * 初始化
  19. */
  20. const getInit = (e: any) => {
  21. let arr = e.data.result.keypoints;
  22. let result = [];
  23. for (let i = 0; i < arr.length; i += 3) {
  24. result.push(arr.slice(i, i + 2));
  25. }
  26. console.log("result", result)
  27. bodyposeData.value = result;
  28. if (boxes.value.length == 0) {
  29. // speckText("识别成功");
  30. // proxy?.$modal.msgWarning(`识别成功`);
  31. let arr = e.data.result.boxes;
  32. boxes.value = [{ x: arr[0], y: arr[3] }, { x: arr[0], y: arr[1] }, { x: arr[2], y: arr[1] }, { x: arr[2], y: arr[3] }]
  33. proportion.value = (clientObj.value.height / (arr[3] - arr[1])).toFixed(2);
  34. }
  35. //绘制图案
  36. getCanvas();
  37. };
  38. /**
  39. * 绘图
  40. */
  41. const getCanvas = () => {
  42. const canvas: any = canvasRef.value;
  43. const ctx = canvas.getContext('2d');
  44. // 清空整个画布
  45. ctx.clearRect(0, 0, canvas.width, canvas.height);
  46. // 保存当前状态
  47. ctx.save();
  48. function calculateOffset(a: any, b: any) {
  49. return {
  50. x: b.x - a.x,
  51. y: b.y - a.y
  52. };
  53. }
  54. const pointA = { x: clientObj.value.width / 2, y: clientObj.value.height / 2 };
  55. const pointB = { x: (boxes.value[2].x + boxes.value[0].x) / 2, y: (boxes.value[3].y + boxes.value[1].y) / 2 };
  56. const offset = calculateOffset(pointA, pointB);
  57. ctx.translate(-offset.x, -offset.y);
  58. // console.log("Canvas分辨率", clientObj.value);
  59. // console.log("人体图片四点坐标", boxes.value)
  60. // console.log("Canvas中心", pointA);
  61. // console.log("人体中心", pointB);
  62. // console.log("offset", offset)
  63. // console.log("proportion.value",proportion.value)
  64. const originalPoints = bodyposeData.value;
  65. // 计算缩放后坐标
  66. const postData = originalPoints.map((point: any) => {
  67. const newX = (point[0] - pointB.x) * proportion.value + pointB.x;
  68. const newY = (point[1] - pointB.y) * proportion.value + pointB.y;
  69. return [newX, newY];
  70. });
  71. // console.log("原始坐标:", originalPoints);
  72. // console.log("缩放后坐标:", postData);
  73. //绘制头部
  74. const point1 = { x: postData[4][0], y: postData[4][1] };
  75. const point2 = { x: postData[3][0], y: postData[3][1] };
  76. // 计算椭圆参数
  77. const centerX = (point1.x + point2.x) / 2; // 椭圆中心X
  78. const centerY = (point1.y + point2.y) / 2; // 椭圆中心Y
  79. const distance = Math.sqrt(
  80. Math.pow(point2.x - point1.x, 2) +
  81. Math.pow(point2.y - point1.y, 2)
  82. ); // 两个焦点之间的距离
  83. const radiusX = distance * 0.5; // 水平半径(可调整)
  84. const radiusY = distance * 0.6; // 垂直半径(可调整)
  85. // 1. 绘制填充椭圆
  86. ctx.beginPath();
  87. ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
  88. ctx.fillStyle = 'red'; // 填充颜色
  89. ctx.fill(); // 填充
  90. // 2. 绘制边框
  91. ctx.strokeStyle = 'red';
  92. ctx.lineWidth = 5;
  93. ctx.stroke();
  94. // 绘制每个点
  95. postData.forEach((point: any, index: number) => {
  96. //眼睛鼻子不显示
  97. if (![0, 1, 2].includes(index)) {
  98. const [x, y] = point;
  99. ctx.beginPath();
  100. ctx.arc(x, y, 5, 0, Math.PI * 2); // 绘制半径为5的圆点
  101. ctx.fillStyle = 'red';
  102. ctx.fill();
  103. ctx.lineWidth = 1;
  104. ctx.stroke();
  105. }
  106. });
  107. // 根据点关系连线
  108. const arr = [[10, 8], [8, 6], [6, 5], [5, 7], [7, 9], [6, 12], [5, 11], [12, 11], [12, 14], [14, 16], [11, 13], [13, 15]]
  109. arr.forEach((point: any) => {
  110. let index1 = point[0];
  111. let index2 = point[1];
  112. //连线
  113. const dian1 = { x: postData[index1][0], y: postData[index1][1] };
  114. const dian2 = { x: postData[index2][0], y: postData[index2][1] };
  115. // 绘制连线
  116. ctx.beginPath();
  117. ctx.moveTo(dian1.x, dian1.y); // 起点
  118. ctx.lineTo(dian2.x, dian2.y); // 终点
  119. ctx.strokeStyle = 'red'; // 线条颜色
  120. ctx.lineWidth = 3; // 线条宽度
  121. ctx.stroke(); // 描边
  122. });
  123. ctx.restore(); // 恢复状态
  124. };
  125. onBeforeMount(() => {
  126. });
  127. onMounted(() => {
  128. clientObj.value = {
  129. width: document.querySelector('.game-container').offsetWidth,
  130. height: document.querySelector('.game-container').offsetHeight,
  131. }
  132. })
  133. onBeforeUnmount(() => {
  134. });
  135. //暴露给父组件用
  136. defineExpose({
  137. getInit
  138. })
  139. </script>
  140. <style lang="scss" scoped>
  141. .game-container {
  142. background: #000000;
  143. position: relative;
  144. width: 100%;
  145. height: 100vh;
  146. margin: 0 auto;
  147. overflow: hidden;
  148. }
  149. </style>