|
|
@@ -1,77 +1,108 @@
|
|
|
-import React from 'react';
|
|
|
+import React, { useEffect } from 'react';
|
|
|
+import HorseSvg from '@/assets/imgs/Animal/horse.svg';
|
|
|
|
|
|
-interface HorseBodyProps {
|
|
|
+interface EquineBodyProps {
|
|
|
selectedId: string;
|
|
|
onPathClick: (id: string) => void;
|
|
|
}
|
|
|
|
|
|
-const EquineBody: React.FC<HorseBodyProps> = ({ selectedId, onPathClick }) => {
|
|
|
+const EquineBody: React.FC<EquineBodyProps> = ({ selectedId, onPathClick }) => {
|
|
|
+ /**
|
|
|
+ * 事件委托: 在 SVG 根元素处理所有点击
|
|
|
+ */
|
|
|
+ const handleSvgClick = (e: React.MouseEvent<SVGSVGElement>) => {
|
|
|
+ const target = e.target as SVGElement;
|
|
|
+
|
|
|
+ // 查找被点击的 path 元素
|
|
|
+ let pathElement: SVGPathElement | null = null;
|
|
|
+
|
|
|
+ if (target.tagName === 'path') {
|
|
|
+ pathElement = target as SVGPathElement;
|
|
|
+ } else if (target.tagName === 'g') {
|
|
|
+ // 如果点击在 g 上,查找子 path
|
|
|
+ pathElement = target.querySelector('path[id^="Equine_"]');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pathElement || !pathElement.id) return;
|
|
|
+
|
|
|
+ console.log('点击马部位:', pathElement.id);
|
|
|
+ onPathClick(pathElement.id);
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 控制 SVG 高亮状态
|
|
|
+ */
|
|
|
+ useEffect(() => {
|
|
|
+ const svg = document.querySelector('.horse-svg') as SVGSVGElement | null;
|
|
|
+ if (!svg) return;
|
|
|
+
|
|
|
+ // 重置所有 path 状态
|
|
|
+ svg.querySelectorAll('path[id^="Equine_"]').forEach((path) => {
|
|
|
+ path.classList.remove('active');
|
|
|
+ path.classList.add('inactive');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 高亮选中的 path
|
|
|
+ if (selectedId) {
|
|
|
+ const activePath = svg.querySelector(`#${CSS.escape(selectedId)}`);
|
|
|
+ if (activePath) {
|
|
|
+ activePath.classList.remove('inactive');
|
|
|
+ activePath.classList.add('active');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [selectedId]);
|
|
|
+
|
|
|
return (
|
|
|
- <svg viewBox="0 0 540 471" width="100%">
|
|
|
- <path
|
|
|
- id="Equine_Head"
|
|
|
- d="M149.3,49.5L129.1,37.1 103.8,26 87.4,22 69.9,3.4 65.1,3.5 65.1,15.2 69.2,24.7 54.8,35.4 43.9,49.2 38.7,59.9 17.4,80.7 2.4,98 -1.2,106.2 2,118.3 10.3,127 22,131.3 31.9,129.3 37.3,122.7 53.9,112.9 64.3,109.4 75.5,109.6 82.9,106.3 88,101.8 93.6,101.7 97.3,103.5z"
|
|
|
- fill={selectedId === 'Equine_Head' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Head' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Head')}
|
|
|
- />
|
|
|
- <path
|
|
|
- id="Equine_Spine"
|
|
|
- d="M147.7,49.5L119.2,78.7 220.4,175.8 392.7,169.2 475.7,227.3C475.7,227.3,480.5,194,480.5,194L478.7,174.6 472.6,160.8 476.1,160.1 485.4,167.4 491.2,187.2 492,279 498.8,313.1 512.6,348 523.8,364.7 531.3,370.9 533.6,359.1 534,347.3 529,304 529.8,279.8 533,250.6 533.5,225.4 528.8,204.2 516.2,177.3 495.3,148.3 475,131.4 450.3,118.6 423.4,112.3 389.8,111.3 348,121.4 306.2,127.2 270.1,127.6 251.8,123.4 224.4,111.3 176,70.2z"
|
|
|
- fill={selectedId === 'Equine_Spine' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Spine' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Spine')}
|
|
|
- />
|
|
|
- <path
|
|
|
- id="Equine_Left_Forelimb"
|
|
|
- d="M119,78.4L96.5,102.2 110.6,126.7 140.4,200.2 144.5,230.9 154.8,253 163.7,261.6 168,286.6 173.3,308.4 176.2,315.6 178.4,337 179.4,360 183.6,380.7 185.6,404.5 183.1,423 157.6,451.6 160.6,456 166.3,457.3 187.3,457.6 190.7,455.1 194.8,448.5 190,441.6 206,427.4 208.4,421 205.4,391.4 205.4,369.4 208.4,355.1 209.7,324.7 214,295 223.7,271 223,173z"
|
|
|
- fill={selectedId === 'Equine_Left_Forelimb' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Left_Forelimb' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Left_Forelimb')}
|
|
|
- />{' '}
|
|
|
- <path
|
|
|
- id="Equine_Left_Hindlimb"
|
|
|
- d="M396.8,166L397,253 411.9,290.6 423.3,321.6 432.8,339.7 436.8,356.1 440,363.9 444.8,414.7 444.8,429.8 437.8,466.6 440,469.7 452.2,467.6 455,462 455.5,456 452.6,448.7 454.3,435 455.8,432.5 455.6,412.7 452.2,381.5 450.2,350.6 449.6,331.2 447.9,323.8 444.6,316.2 438.3,290 435.7,272.3 435.8,262.2 439,225.3z"
|
|
|
- fill={selectedId === 'Equine_Left_Hindlimb' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Left_Hindlimb' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Left_Hindlimb')}
|
|
|
- />{' '}
|
|
|
- <path
|
|
|
- id="Equine_Right_Forelimb"
|
|
|
- d="M221.5,273L235,273 240,295 242.3,308 246.4,314.6 246.5,321 256.4,331.6 264.3,342.5 264.5,345.7 261.8,348.5 259,348 254,354 258,357 258,359.6 252,363 226,364 223,363 222,361 240,346 239,340 230.5,325 226,322.5 207.7,298.7 213,283z"
|
|
|
- fill={selectedId === 'Equine_Right_Forelimb' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Right_Forelimb' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Right_Forelimb')}
|
|
|
- />{' '}
|
|
|
- <path
|
|
|
- id="Equine_Right_Hindlimb"
|
|
|
- d="M382.6,244.9L395.4,249.21768 414.6,273.1 445.3,320.8 455.5,340.7 447.5,393 444.6,435.7 442.4,449 439.8,453.5 435.8,455 429,465.5 431.2,472.8 429,479.5 423.3,481.6 411.3,481 399.4,478 398.8,475 420.9,440.6 427,372 424.8,363.8 424.6,354.3 424.6,348.3 419.1,336.9 411.8,311 403.6,278.4 395.7,264.4 388,254 380.6,246.9z"
|
|
|
- fill={selectedId === 'Equine_Right_Hindlimb' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_Right_Hindlimb' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_Right_Hindlimb')}
|
|
|
- />
|
|
|
- <path
|
|
|
- id="Equine_THORAX"
|
|
|
- d="M221.2,173.4 L400.2,170.5 400,253 Q375,233,350,243 L221.2,253z"
|
|
|
- fill={selectedId === 'Equine_THORAX' ? '#f5222d' : '#ccc'}
|
|
|
- stroke={selectedId === 'Equine_THORAX' ? '#ff4d4f' : '#ccc'}
|
|
|
- strokeWidth={2}
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => onPathClick('Equine_THORAX')}
|
|
|
- />
|
|
|
- </svg>
|
|
|
+ <>
|
|
|
+ {/* 样式定义 */}
|
|
|
+ <style>
|
|
|
+ {`
|
|
|
+ .horse-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .horse-svg {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 默认状态 */
|
|
|
+ .horse-svg path[id^="Equine_"] {
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* hover 效果 */
|
|
|
+ .horse-svg path[id^="Equine_"]:hover {
|
|
|
+ filter: drop-shadow(0 0 8px #ff4d4f);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 激活状态 */
|
|
|
+ .horse-svg .active {
|
|
|
+ fill: #f5222d;
|
|
|
+ stroke: #ff4d4f;
|
|
|
+ stroke-width: 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 非激活状态 */
|
|
|
+ .horse-svg .inactive {
|
|
|
+ fill: #ccc;
|
|
|
+ stroke: #ccc;
|
|
|
+ stroke-width: 2;
|
|
|
+ }
|
|
|
+ `}
|
|
|
+ </style>
|
|
|
+
|
|
|
+ {/* SVG 容器 */}
|
|
|
+ <div className="horse-container">
|
|
|
+ <HorseSvg
|
|
|
+ className="horse-svg"
|
|
|
+ onClick={handleSvgClick}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
);
|
|
|
};
|
|
|
|