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

如何基于ElectronJS与Svelte实现用户位置越界(超出设定半径)检测?

基于Svelte+Electron实现定期位置检查与半径判断

1. 配置Electron地理位置权限

首先确保Electron允许渲染进程获取地理位置,在主进程(main.js/main.ts)的窗口配置中开启相关权限:

const { app, BrowserWindow } = require('electron');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      contextIsolation: true,
      preload: './preload.js', // 如需Node API交互,用preload脚本隔离
      geolocation: true, // 允许地理位置访问
      backgroundThrottling: false // 可选:防止后台时定时器被系统节流
    }
  });

  mainWindow.loadURL('http://localhost:5173'); // 本地开发地址,打包时替换为build后的index.html路径
}

app.whenReady().then(createWindow);

打包时需在系统层面申请权限,在package.json的build字段添加声明:

"build": {
  "mac": {
    "entitlements": "entitlements.mac.plist",
    "entitlementsInherit": "entitlements.mac.plist"
  },
  "win": {
    "permissions": ["geolocation"]
  }
}

macOS对应的entitlements.mac.plist内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>com.apple.security.personal-information.location</key>
  <true/>
</dict>
</plist>

2. 实现位置获取与距离计算

2.1 球面距离计算函数(Haversine公式)

用这个函数计算两点经纬度之间的直线距离(单位:米):

// 角度转弧度
function degreesToRadians(degrees) {
  return degrees * Math.PI / 180;
}

// 计算两点距离(米)
function calculateDistance(lat1, lon1, lat2, lon2) {
  const earthRadius = 6371000; // 地球平均半径,单位米
  const dLat = degreesToRadians(lat2 - lat1);
  const dLon = degreesToRadians(lon2 - lon1);
  
  const a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2); 
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  return earthRadius * c;
}

2.2 定期检查逻辑(Svelte组件内实现)

在Svelte组件中通过定时器定期获取位置,与预设的中心点、半径对比:

<script>
  import { onMount, onDestroy } from 'svelte';

  // 预设监控参数(可从配置/后端动态获取)
  const centerLat = 31.2304; // 示例中心点纬度(上海)
  const centerLon = 121.4737; // 示例中心点经度(上海)
  const radiusLimit = 1000; // 半径限制,单位米(1公里)

  let checkInterval;
  let monitorStatus = '等待初始化...';

  // 单次位置检查
  function checkLocation() {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        reject('当前环境不支持地理位置服务');
        return;
      }

      navigator.geolocation.getCurrentPosition(
        (position) => {
          const { latitude, longitude } = position.coords;
          const distance = calculateDistance(centerLat, centerLon, latitude, longitude);
          resolve({
            isOutOfRange: distance > radiusLimit,
            distance: Math.round(distance),
            latitude,
            longitude
          });
        },
        (error) => {
          let errMsg = '';
          switch(error.code) {
            case error.PERMISSION_DENIED:
              errMsg = '用户拒绝位置权限';
              break;
            case error.POSITION_UNAVAILABLE:
              errMsg = '位置信息不可用';
              break;
            case error.TIMEOUT:
              errMsg = '获取位置超时';
              break;
          }
          reject(errMsg);
        },
        {
          enableHighAccuracy: false, // 普通场景关闭高精度,节省电量
          timeout: 8000,
          maximumAge: 10000 // 允许使用10秒内的缓存位置
        }
      );
    });
  }

  // 启动定时监控
  function startMonitoring() {
    // 每30秒检查一次(可根据需求调整间隔)
    checkInterval = setInterval(async () => {
      try {
        const result = await checkLocation();
        if (result.isOutOfRange) {
          monitorStatus = `⚠️ 已超出范围!当前距离中心点${result.distance}米`;
          // 此处添加超出范围后的逻辑:弹窗提示、上报后端等
        } else {
          monitorStatus = `✅ 在范围内,距离中心点${result.distance}米`;
        }
      } catch (err) {
        monitorStatus = `❌ 检查失败:${err}`;
      }
    }, 30000);
  }

  onMount(async () => {
    // 页面加载后先执行一次检查
    try {
      const result = await checkLocation();
      monitorStatus = result.isOutOfRange ? 
        `⚠️ 已超出范围!当前距离中心点${result.distance}米` :
        `✅ 在范围内,距离中心点${result.distance}米`;
    } catch (err) {
      monitorStatus = `❌ 初始化检查失败:${err}`;
    }
    startMonitoring();
  });

  onDestroy(() => {
    // 组件销毁时清除定时器,避免内存泄漏
    if (checkInterval) clearInterval(checkInterval);
  });

  // 引入距离计算函数
  function degreesToRadians(degrees) {
    return degrees * Math.PI / 180;
  }

  function calculateDistance(lat1, lon1, lat2, lon2) {
    const earthRadius = 6371000;
    const dLat = degreesToRadians(lat2 - lat1);
    const dLon = degreesToRadians(lon2 - lon1);
    
    const a = 
      Math.sin(dLat/2) * Math.sin(dLat/2) +
      Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) * 
      Math.sin(dLon/2) * Math.sin(dLon/2); 
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    return earthRadius * c;
  }
</script>

<div class="monitor-container">
  <h3>位置监控状态</h3>
  <p class="status-text">{monitorStatus}</p>
</div>

<style>
  .monitor-container {
    padding: 20px;
    border-radius: 8px;
    background: #f5f5f5;
  }
  .status-text {
    font-size: 16px;
    margin-top: 10px;
  }
</style>

3. 关键注意事项

  • 权限引导:第一次打开应用时系统会弹出权限申请,需在UI中提示用户允许;若用户拒绝,提供重新申请权限的入口(可通过navigator.permissions.query检查权限状态)。
  • 性能平衡:检查间隔不宜过短(建议10-60秒),避免频繁定位导致设备耗电过高;高精度定位仅在必要场景开启。
  • 后台运行:若需应用后台持续监控,需确保backgroundThrottling设为false,同时在macOS等系统中配置后台运行权限。
  • 异常处理:必须覆盖定位失败的所有场景,避免应用无响应或崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:40:39