1. 运行截图

演示地址

2. 在文件夹wwwroot/lib,添加geolocation子文件夹,添加geolocation.js文件

本组件主要是调用浏览器两个API实现基于浏览器的定位功能,现代桌面和移动端都支持.包括MAUI Blazor

navigator.geolocation.getCurrentPosition
navigator.geolocation.watchPosition

2.1 获取定位

其中持续定位watchPosition方法会通过wrapper.invokeMethodAsync('UpdateWatchID', id)返回监听器ID到c#存起来,移除的监听器和注销时候用到.

js代码
export function getLocation(wrapper, getPosition = true) {
console.log('start ' + (getPosition ? 'getLocation' : 'watchPosition'));
var currentDistance = 0.0;
var totalDistance = 0.0;
var lastLat;
var lastLong;
var status; if (getPosition) getCurrentPosition(); else watchPosition(); Number.prototype.toRadians = function () {
return this * Math.PI / 180;
} function distance(latitude1, longitude1, latitude2, longitude2) {
// R is the radius of the earth in kilometers
var R = 6371; var deltaLatitude = (latitude2 - latitude1).toRadians();
var deltaLongitude = (longitude2 - longitude1).toRadians();
latitude1 = latitude1.toRadians(), latitude2 = latitude2.toRadians(); var a = Math.sin(deltaLatitude / 2) *
Math.sin(deltaLatitude / 2) +
Math.cos(latitude1) *
Math.cos(latitude2) *
Math.sin(deltaLongitude / 2) *
Math.sin(deltaLongitude / 2); var c = 2 * Math.atan2(Math.sqrt(a),
Math.sqrt(1 - a));
var d = R * c;
return d;
} function updateStatus(message) {
status = message;
wrapper.invokeMethodAsync('UpdateStatus', message);
} function watchPosition() {
if (navigator.geolocation) {
status = "HTML5 Geolocation is supported in your browser.";
updateStatus(status);
var id = navigator.geolocation.watchPosition(updateLocation,
handleLocationError,
{ maximumAge: 20000 });
wrapper.invokeMethodAsync('UpdateWatchID', id);
}
} function getCurrentPosition() {
if (navigator.geolocation) {
updateStatus("HTML5 Geolocation is supported in your browser.");
navigator.geolocation.getCurrentPosition(updateLocation,
handleLocationError);
}
} function updateLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var accuracy = position.coords.accuracy;
var timestamp = position.timestamp; // sanity test... don't calculate distance if accuracy
// value too large
if (accuracy >= 500) {
updateStatus("Need more accurate values to calculate distance.");
} // calculate distance
currentDistance = 0.0;
if ((lastLat != null) && (lastLong != null)) {
currentDistance = distance(latitude, longitude, lastLat, lastLong);
totalDistance += currentDistance;
} lastLat = latitude;
lastLong = longitude; updateStatus("Location successfully updated."); console.log("updateLocation end");
var geolocationitem = {
"Status": status,
"Latitude": latitude,
"Longitude": longitude,
"Accuracy": accuracy,
"Timestamp": timestamp,
"CurrentDistance": currentDistance,
"TotalDistance": totalDistance,
"LastLat": lastLat,
"LastLong": lastLong,
};
wrapper.invokeMethodAsync('GetResult', geolocationitem);
} function handleLocationError(error) {
switch (error.code) {
case 0:
updateStatus("There was an error while retrieving your location: " + error.message);
break;
case 1:
updateStatus("The user prevented this page from retrieving a location.");
break;
case 2:
updateStatus("The browser was unable to determine your location: " + error.message);
break;
case 3:
updateStatus("The browser timed out before retrieving the location.");
break;
}
} }

2.2 组件页面点击停止持续定位,把监听器ID传入移除的监听器.

export function clearWatchLocation(wrapper,id) {
//扩展阅读:移除的监听器
//id = navigator.geolocation.watchPosition(success, error, options);
console.log('clearWatch ' + id);
navigator.geolocation.clearWatch(id);
wrapper.invokeMethodAsync('UpdateStatus', 'clearWatch ok');
}

3. 打开Components文件夹 , 新建Geolocation文件夹,新建三个文件

3.1 GeolocationItem.cs 定位数据类

cs代码
using System;
using System.ComponentModel; namespace Blazor100.Components
{ /// <summary>
/// 定位数据类
/// </summary>
public class Geolocationitem
{
/// <summary>
/// 状态
/// </summary>
/// <returns></returns>
[DisplayName("状态")]
public string? Status { get; set; } /// <summary>
/// 纬度
/// </summary>
/// <returns></returns>
[DisplayName("纬度")]
public decimal Latitude { get; set; } /// <summary>
/// 经度
/// </summary>
/// <returns></returns>
[DisplayName("经度")]
public decimal Longitude { get; set; } /// <summary>
/// 准确度(米)<para></para>
/// 将以m指定维度和经度值与实际位置的差距,置信度为95%.
/// </summary>
[DisplayName("准确度(米)")]
public decimal Accuracy { get; set; } /// <summary>
/// 时间戳
/// </summary>
[DisplayName("时间戳")]
public long Timestamp { get; set; } /// <summary>
/// 时间
/// </summary>
[DisplayName("时间")]
public DateTime LastUpdateTime { get => UnixTimeStampToDateTime(Timestamp); } /// <summary>
/// 移动距离
/// </summary>
[DisplayName("移动距离")]
public decimal CurrentDistance { get; set; } = 0.0M; /// <summary>
/// 总移动距离
/// </summary>
[DisplayName("总移动距离")]
public decimal TotalDistance { get; set; } = 0.0M; /// <summary>
/// 最后一次获取到的纬度
/// </summary>
[DisplayName("最后一次获取到的纬度")]
public decimal LastLat { get; set; } /// <summary>
/// 最后一次获取到的经度
/// </summary>
[DisplayName("最后一次获取到的经度")]
public decimal LastLong { get; set; } /// <summary>
///
/// </summary>
/// <param name="unixTimeStamp"></param>
/// <returns></returns>
public static DateTime UnixTimeStampToDateTime(long unixTimeStamp)
{
// Unix timestamp is seconds past epoch
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
dtDateTime = dtDateTime.AddMilliseconds(unixTimeStamp).ToLocalTime();
return dtDateTime;
} }
}

3.1 Geolocation.razor 组件razor

@implements IAsyncDisposable
@namespace Blazor100.Components <div @ref="GeolocationElement">
@if (ShowButtons)
{
if (WatchID == null)
{
<button class="btn btn-primary" type="button" onclick="@GetLocation">@GetLocationButtonText</button>
<button class="btn btn-primary" type="button" onclick="@WatchPosition">@WatchPositionButtonText</button>
}
else
{
<button class="btn btn-primary" type="button" onclick="@ClearWatch">@ClearWatchPositionButtonText</button>}
}
</div>

3.2 Geolocation.razor.cs 组件代码

cs代码
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Diagnostics.CodeAnalysis; namespace Blazor100.Components; /// <summary>
/// Geolocation 组件基类
/// <para></para>
/// 扩展阅读:Chrome中模拟定位信息,清除定位信息<para></para>
/// https://blog.csdn.net/u010844189/article/details/81163438
/// </summary>
public partial class Geolocation
{
[Inject] IJSRuntime? JS { get; set; } /// <summary>
/// 获得/设置 定位
/// </summary>
[Parameter]
[NotNull]
public string? GeolocationInfo { get; set; } /// <summary>
/// 获得/设置 获取位置按钮文字 默认为 获取位置
/// </summary>
[Parameter]
[NotNull]
public string? GetLocationButtonText { get; set; } = "获取位置"; /// <summary>
/// 获得/设置 获取持续定位监听器ID
/// </summary>
[Parameter]
public long? WatchID { get; set; } /// <summary>
/// 获得/设置 获取移动距离追踪按钮文字 默认为 移动距离追踪
/// </summary>
[Parameter]
[NotNull]
public string? WatchPositionButtonText { get; set; } = "移动距离追踪"; /// <summary>
/// 获得/设置 获取停止追踪按钮文字 默认为 停止追踪
/// </summary>
[Parameter]
[NotNull]
public string? ClearWatchPositionButtonText { get; set; } = "停止追踪"; /// <summary>
/// 获得/设置 是否显示默认按钮界面
/// </summary>
[Parameter]
public bool ShowButtons { get; set; } = true; /// <summary>
///
/// </summary>
protected ElementReference GeolocationElement { get; set; } /// <summary>
/// 获得/设置 定位结果回调方法
/// </summary>
[Parameter]
public Func<Geolocationitem, Task>? OnResult { get; set; } /// <summary>
/// 获得/设置 状态更新回调方法
/// </summary>
[Parameter]
public Func<string, Task>? OnUpdateStatus { get; set; } private IJSObjectReference? module;
private DotNetObjectReference<Geolocation>? InstanceGeo { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender)
{
try
{
if (firstRender)
{
module = await JS!.InvokeAsync<IJSObjectReference>("import", "./lib/geolocation/geolocation.js");
InstanceGeo = DotNetObjectReference.Create(this);
}
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
} async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
//await module.InvokeVoidAsync("destroy");
InstanceGeo!.Dispose();
await module.DisposeAsync();
}
} /// <summary>
/// 获取定位
/// </summary>
public virtual async Task GetLocation()
{
try
{
await module!.InvokeVoidAsync("getLocation", InstanceGeo);
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
} /// <summary>
/// 持续定位
/// </summary>
public virtual async Task WatchPosition()
{
try
{
await module!.InvokeVoidAsync("getLocation", InstanceGeo, false);
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
} /// <summary>
/// 持续定位
/// </summary>
public virtual async Task ClearWatch()
{
await module!.InvokeVoidAsync("clearWatchLocation", InstanceGeo, WatchID);
WatchID = null;
} /// <summary>
/// 定位完成回调方法
/// </summary>
/// <param name="geolocations"></param>
/// <returns></returns>
[JSInvokable]
public async Task GetResult(Geolocationitem geolocations)
{
try
{
if (OnResult != null) await OnResult.Invoke(geolocations);
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
} /// <summary>
/// 获得/设置 错误回调方法
/// </summary>
[Parameter]
public Func<string, Task>? OnError { get; set; } /// <summary>
/// 状态更新回调方法
/// </summary>
/// <param name="status"></param>
/// <returns></returns>
[JSInvokable]
public async Task UpdateStatus(string status)
{
if (OnUpdateStatus != null) await OnUpdateStatus.Invoke(status);
} /// <summary>
/// 监听器ID回调方法
/// </summary>
/// <param name="watchID"></param>
/// <returns></returns>
[JSInvokable]
public Task UpdateWatchID(long watchID)
{
this.WatchID = watchID;
return Task.CompletedTask;
} }

4. Pages文件夹添加GeolocationPage.razor文件,用于演示组件调用.

4.1 GeolocationPage.razor

razor代码
@page "/geolocations"

<h3>定位/持续定位 Geolocation</h3>

<p>@message</p>

<Blazor100.Components.Geolocation OnResult="@OnResult" OnUpdateStatus="@OnUpdateStatus" OnError="@OnError" />

<p>@status</p>

<div class="table-container">

    <div class="table-toolbar">
</div> <div class="table-wrapper">
<table class="table is-single table-demo">
<colgroup>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th><div class="table-cell"><span class="table-text">纬度</span></div></th>
<th><div class="table-cell"><span class="table-text">经度</span></div></th>
<th><div class="table-cell"><span class="table-text">准确度(米)</span></div></th>
<th><div class="table-cell"><span class="table-text">时间戳</span></div></th>
<th><div class="table-cell"><span class="table-text">时间</span></div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div class="table-cell">@geolocations?.Latitude</div></td>
<td><div class="table-cell">@geolocations?.Longitude</div></td>
<td><div class="table-cell">@geolocations?.Accuracy</div></td>
<td><div class="table-cell">@geolocations?.Timestamp</div></td>
<td><div class="table-cell">@geolocations?.LastUpdateTime</div></td>
</tr>
</tbody>
<thead>
<tr>
<th><div class="table-cell"><span class="table-text">移动距离</span></div></th>
<th><div class="table-cell"><span class="table-text">总移动距离</span></div></th>
<th><div class="table-cell"><span class="table-text">最后一次获取到的纬度</span></div></th>
<th><div class="table-cell"><span class="table-text">最后一次获取到的经度</span></div></th>
<th><div class="table-cell"><span class="table-text">状态</span></div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div class="table-cell">@geolocations?.CurrentDistance</div></td>
<td><div class="table-cell">@geolocations?.TotalDistance</div></td>
<td><div class="table-cell">@geolocations?.LastLat</div></td>
<td><div class="table-cell">@geolocations?.LastLong</div></td>
<td><div class="table-cell">@geolocations?.Status</div></td>
</tr>
</tbody>
</table>
</div>
</div>

4.2 GeolocationPage.razor.cs

扩展阅读:Chrome中模拟定位信息,清除定位信息

using Blazor100.Components;

namespace Blazor100.Pages;

/// <summary>
/// Geolocation 地理定位/移动距离追踪
/// <para></para>
/// 扩展阅读:Chrome中模拟定位信息,清除定位信息<para></para>
/// https://blog.csdn.net/u010844189/article/details/81163438
/// </summary>
public sealed partial class GeolocationPage
{ private string? status { get; set; }
private Geolocationitem? geolocations { get; set; }
private string message; private Task OnResult(Geolocationitem geolocations)
{
this.geolocations = geolocations;
StateHasChanged();
return Task.CompletedTask;
} private Task OnUpdateStatus(string status)
{
this.status = status;
StateHasChanged();
return Task.CompletedTask;
} private Task OnError(string message)
{
this.message = message;
StateHasChanged();
return Task.CompletedTask;
}
}

5. _Imports.razor加入一行引用组件的命名空间.

@using Blazor100.Components

6. 首页引用组件演示页 <GeolocationPage /> 或者 Shared/NavMenu.razor 添加导航

<div class="nav-item px-3">
<NavLink class="nav-link" href="geolocations">
定位
</NavLink>
</div>

7. F5运行程序

至此,使用JS隔离制作定位/持续定位组件大功告成! Happy coding!

Blazor组件自做系列

Blazor组件自做一 : 使用JS隔离封装viewerjs库

Blazor组件自做二 : 使用JS隔离制作手写签名组件

Blazor组件自做三 : 使用JS隔离封装ZXing扫码

Blazor组件自做四: 使用JS隔离封装signature_pad签名组件

Blazor组件自做五: 使用JS隔离封装Google地图

Blazor组件自做六: 使用JS隔离封装Baidu地图

Blazor组件自做七: 使用JS隔离制作定位/持续定位组件

Blazor组件自做八: 使用JS隔离封装屏幕键盘kioskboard.js组件<03-27>

项目源码 Github | Gitee

Blazor组件自做七 : 使用JS隔离制作定位/持续定位组件的更多相关文章

  1. Blazor组件自做二 : 使用JS隔离制作手写签名组件

    Blazor组件自做二 : 使用JS隔离制作手写签名组件 本文相关参考链接 JavaScript 模块中的 JavaScript 隔离 Viewer.js工程 Blazor组件自做一 : 使用JS隔离 ...

  2. Blazor组件自做八 : 使用JS隔离封装屏幕键盘kioskboard.js组件

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加kioskboard子文件夹,添加kioskboards.js文件 2.1 常规操作,懒加载js库, export function ...

  3. Blazor组件自做一 : 使用JS隔离封装viewerjs库

    Viewer.js库是一个实用的js库,用于图片浏览,放大缩小翻转幻灯片播放等实用操作 本文相关参考链接 JavaScript 模块中的 JavaScript 隔离 Viewer.js工程 Blazo ...

  4. Blazor组件自做三 : 使用JS隔离封装ZXing扫码

    Blazor组件自做三 : 使用JS隔离封装ZXing扫码 本文基础步骤参考前两篇文章 Blazor组件自做一 : 使用JS隔离封装viewerjs库 Blazor组件自做二 : 使用JS隔离制作手写 ...

  5. Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

    运行截图 演示地址 响应式演示 感谢szimek写的棒棒的signature_pad.js项目, 来源: https://github.com/szimek/signature_pad 正式开始 1. ...

  6. Blazor组件自做五 : 使用JS隔离封装Google地图

    Blazor组件自做五: 使用JS隔离封装Google地图 运行截图 演示地址 正式开始 1. 谷歌地图API 谷歌开发文档 开始学习 Maps JavaScript API 的最简单方法是查看一个简 ...

  7. Blazor组件自做六 : 使用JS隔离封装Baidu地图

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加baidu子文件夹,添加baidumap.js文件 2.1 跟上一篇类似,用代码方式异步加载API,脚本生成新的 body > ...

  8. 一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app

    一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app 转载 作者:jrainlau 链接:https://segmentfault.com/a/1190000005844155 ...

  9. (day67)组件、组件化、组件传参、JS补充(命名转换、for in 、数据转换)、css取消选中和模拟小手

    目录 一.初识组件 (一)概念 (二)特点 二.组件的分类 (一)根组件 (二)局部组件 (三)全局组件 二.数据组件化 三.组件的传参 (一)父传子 (二)子传父 四.JS补充 (一)与html命名 ...

随机推荐

  1. Map 集合

    <零基础学Java > Map 集合 在程序中需要存储具有映射关系就需要使用Map集合了. Map集合由Map接口.Map接口的实现类组成. Map 接口 Map接口没有Collectio ...

  2. iCloud开发: key-value Storage,CloudKit,iCloud Documents

    目录 iCloud开发 iCloud三种类型的存储方式 项目配置 1.iCloud 官网配置 2.本地Xcode配置 注意事项 一.key-value storage 1.获取默认store 2.写入 ...

  3. Kernel pwn 基础教程之 ret2usr 与 bypass_smep

    一.前言 在我们的pwn学习过程中,能够很明显的感觉到开发人员们为了阻止某些利用手段而增加的保护机制,往往这些保护机制又会引发出新的bypass技巧,像是我们非常熟悉的Shellcode与NX,NX与 ...

  4. SQLserver建表规则

    --执行环境:生产环境 / beta环境--备注:文件开头写上描述或者原因.项目USE database --例如 USE LZB GO CREATE TABLE Ymtable1 ( iOrderI ...

  5. 渗透测试系统kali配置源

    镜像下载.域名解析.时间同步请点击阿里云开源镜像站 kali是一款非常棒的渗透测试系统,但是他使用的源是国外的,这对于我们来说非常不友好,下载东西非常慢或者下载不了,更换国内源目的就是为了方便我们使用 ...

  6. 阿里云ECS服务器(G5)安装配置docker

    镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 前言 服务器配置:阿里云ECS服务器(G5服务器3年2核8G内存80G云盘),安装的是CentOS,内核信息:5.10.60-9.al8.x86_ ...

  7. UVA1389 Hard Life (01分数规划+最大流)

    UVA1389 Hard Life (01分数规划+最大流) Luogu 题目描述略 题解时间 $ (\frac{\Sigma EdgeCount}{\Sigma PointCount})_{max} ...

  8. 【freertos】004-任务创建与删除及其实现细节

    前言 后面都是已动态内存任务为例来分析. 注意: 由于当前学习是在linux上跑的freertos,对于freertos底层相关接口,从demo工程来看,都是posix标准相关. 鉴于freertos ...

  9. 您使用了哪些 starter maven 依赖项?

    使用了下面的一些依赖项 spring-boot-starter-activemq spring-boot-starter-security 这有助于增加更少的依赖关系,并减少版本的冲突.

  10. memcached 与 redis 的区别?

    1.Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储.而 memcache 只支持简单数据类型,需要客户端自己处理复 杂对象 2.R ...