You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift技术实现:如何利用Hit Test与Bezier Path识别人体部位点击?

Hey there! Let me walk you through building this interactive body part vocabulary tool that works seamlessly on both iPhone and iPad. I’ll break down the process into actionable steps with code examples you can copy-paste and tweak.

Core Concept: Image Hotspot Mapping

The key here is defining clickable "hotspots" on your static body image. When a user taps (or clicks) a hotspot, we’ll trigger a display of the corresponding foreign word. There are two reliable ways to do this—pick the one that fits your needs:


Option 1: Quick & Simple with HTML Image Maps

This is perfect if your body parts have straightforward shapes and you don’t need dynamic adjustments. We’ll use the <map> and <area> tags to define clickable regions.

Step-by-Step Implementation

  1. Prepare Responsive Images
    Create 1x, 2x, and 3x versions of your body image (for Retina displays on iOS). Use srcset to serve the right resolution automatically.
  2. Define Hotspots
    Use an online image map generator (no external links needed—just search for one, upload your image, draw regions, and copy the coords values) to get the coordinates for each body part.
  3. Add Interaction & Display
<!-- Responsive body image -->
<img 
  src="body-1x.png" 
  srcset="body-2x.png 2x, body-3x.png 3x" 
  alt="Human Body" 
  usemap="#bodyMap"
  style="width: 100%; height: auto; max-width: 100%;"
>

<!-- Hotspot definitions -->
<map name="bodyMap">
  <!-- Arm region (polygon shape) -->
  <area 
    shape="poly" 
    coords="120,340, 180,320, 200,450, 140,470" 
    alt="Arm" 
    onclick="showVocab('Arm', 'brazo')"
  >
  <!-- Leg region (polygon shape) -->
  <area 
    shape="poly" 
    coords="220,500, 260,680, 300,680, 340,500" 
    alt="Leg" 
    onclick="showVocab('Leg', 'pierna')"
  >
  <!-- Hand region (circle shape) -->
  <area 
    shape="circle" 
    coords="190,460, 30" 
    alt="Hand" 
    onclick="showVocab('Hand', 'mano')"
  >
</map>

<!-- Vocab display container -->
<div id="vocabDisplay" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); font-size: 18px; display: none;">
  <p><strong>Part:</strong> <span id="partName"></span></p>
  <p><strong>Foreign Word:</strong> <span id="foreignWord"></span></p>
</div>

<script>
function showVocab(part, word) {
  const display = document.getElementById('vocabDisplay');
  document.getElementById('partName').textContent = part;
  document.getElementById('foreignWord').textContent = word;
  display.style.display = 'block';
  
  // Auto-hide after 3 seconds
  setTimeout(() => {
    display.style.display = 'none';
  }, 3000);
}

// Fix iOS touch support for <area> tags
document.querySelectorAll('area').forEach(area => {
  area.addEventListener('touchstart', function(e) {
    e.preventDefault(); // Prevent accidental scrolling
    this.onclick();
  });
});
</script>

Option 2: Flexible JavaScript Coordinate Detection

Use this if you need dynamic hotspot scaling (e.g., your image resizes across different iOS devices) or have complex body part shapes. We’ll use relative coordinates so hotspots stay aligned no matter the image size.

Step-by-Step Implementation

  1. Same Responsive Image Setup
    Use srcset for high-res displays, and set the image to be width-responsive.
  2. Define Relative Regions
    Instead of fixed pixel coordinates, use ratios (0-1) relative to the image’s width/height.
  3. Add Hit Detection Logic
<img 
  src="body-1x.png" 
  srcset="body-2x.png 2x, body-3x.png 3x" 
  alt="Human Body" 
  id="bodyImage"
  style="width: 100%; height: auto; max-width: 100%;"
>

<div id="vocabDisplay" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); font-size: 18px; display: none;">
  <p><strong>Part:</strong> <span id="partName"></span></p>
  <p><strong>Foreign Word:</strong> <span id="foreignWord"></span></p>
</div>

<script>
const bodyImage = document.getElementById('bodyImage');
const vocabDisplay = document.getElementById('vocabDisplay');
const partName = document.getElementById('partName');
const foreignWord = document.getElementById('foreignWord');

// Define body parts with relative coordinates
const bodyParts = [
  {
    name: 'Arm',
    word: 'brazo',
    polygon: [[0.2, 0.4], [0.3, 0.35], [0.35, 0.6], [0.25, 0.65]] // [x-ratio, y-ratio] points
  },
  {
    name: 'Leg',
    word: 'pierna',
    polygon: [[0.4, 0.65], [0.45, 0.9], [0.55, 0.9], [0.6, 0.65]]
  },
  {
    name: 'Hand',
    word: 'mano',
    circle: [0.32, 0.62, 0.05] // [center-x, center-y, radius-ratio]
  }
];

// Helper: Check if point is inside polygon
function pointInPolygon(point, polygon) {
  let inside = false;
  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
    const [xi, yi] = polygon[i];
    const [xj, yj] = polygon[j];
    const intersect = ((yi > point[1]) !== (yj > point[1]))
      && (point[0] < (xj - xi) * (point[1] - yi) / (yj - yi) + xi);
    if (intersect) inside = !inside;
  }
  return inside;
}

// Helper: Check if point is inside circle
function pointInCircle(point, circle) {
  const dx = point[0] - circle[0];
  const dy = point[1] - circle[1];
  return dx*dx + dy*dy <= circle[2]*circle[2];
}

// Handle clicks/touches
function handleInteraction(e) {
  e.preventDefault();
  const rect = bodyImage.getBoundingClientRect();
  
  // Calculate relative click position (0-1 for x/y)
  const x = (e.type === 'touchstart' ? e.touches[0].clientX : e.clientX - rect.left) / rect.width;
  const y = (e.type === 'touchstart' ? e.touches[0].clientY : e.clientY - rect.top) / rect.height;
  
  // Check each body part
  for (const part of bodyParts) {
    let isHit = false;
    if (part.polygon) isHit = pointInPolygon([x, y], part.polygon);
    if (part.circle) isHit = pointInCircle([x, y], part.circle);
    
    if (isHit) {
      partName.textContent = part.name;
      foreignWord.textContent = part.word;
      vocabDisplay.style.display = 'block';
      setTimeout(() => vocabDisplay.style.display = 'none', 3000);
      break;
    }
  }
}

// Bind events for both click and touch
bodyImage.addEventListener('click', handleInteraction);
bodyImage.addEventListener('touchstart', handleInteraction);
</script>

Critical iOS Adaptation Tips

  1. Touch Event Support: Always add touchstart listeners alongside click—iOS devices prioritize touch events over mouse clicks.
  2. Responsive Images: Use srcset to serve 2x/3x images for Retina screens, and set width: 100%; height: auto; to ensure the image scales correctly across iPhone and iPad.
  3. Display Layout: Use fixed positioning for the vocab display to keep it accessible on both small and large screens. Add media queries to adjust font sizes:
@media (max-width: 768px) { /* iPhone */
  #vocabDisplay { font-size: 16px; padding: 12px; }
}
@media (min-width: 769px) { /* iPad */
  #vocabDisplay { font-size: 20px; padding: 18px; }
}

内容的提问来源于stack exchange,提问作者devpreneur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:58:39