Blazor组件自做七 : 使用JS隔离制作定位/持续定位组件
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
{
///
/// 定位数据类
///
public class Geolocationitem
{
///
/// 状态
///
///
[DisplayName("状态")]
public string? Status { get; set; }
///
/// 纬度
///
///
[DisplayName("纬度")]
public decimal Latitude { get; set; }
///
/// 经度
///
///
[DisplayName("经度")]
public decimal Longitude { get; set; }
///
/// 准确度(米)
/// 将以m指定维度和经度值与实际位置的差距,置信度为95%.
///
[DisplayName("准确度(米)")]
public decimal Accuracy { get; set; }
///
/// 时间戳
///
[DisplayName("时间戳")]
public long Timestamp { get; set; }
///
/// 时间
///
[DisplayName("时间")]
public DateTime LastUpdateTime { get => UnixTimeStampToDateTime(Timestamp); }
///
/// 移动距离
///
[DisplayName("移动距离")]
public decimal CurrentDistance { get; set; } = 0.0M;
///
/// 总移动距离
///
[DisplayName("总移动距离")]
public decimal TotalDistance { get; set; } = 0.0M;
///
/// 最后一次获取到的纬度
///
[DisplayName("最后一次获取到的纬度")]
public decimal LastLat { get; set; }
///
/// 最后一次获取到的经度
///
[DisplayName("最后一次获取到的经度")]
public decimal LastLong { get; set; }
///
///
///
///
///
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
@if (ShowButtons)
{
if (WatchID == null)
{
}
else
{
}
}
3.2 Geolocation.razor.cs 组件代码
cs代码
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Diagnostics.CodeAnalysis;
namespace Blazor100.Components;
///
/// Geolocation 组件基类
///
/// 扩展阅读:Chrome中模拟定位信息,清除定位信息
/// https://blog.csdn.net/u010844189/article/details/81163438
///
public partial class Geolocation
{
[Inject] IJSRuntime? JS { get; set; }
///
/// 获得/设置 定位
///
[Parameter]
[NotNull]
public string? GeolocationInfo { get; set; }
///
/// 获得/设置 获取位置按钮文字 默认为 获取位置
///
[Parameter]
[NotNull]
public string? GetLocationButtonText { get; set; } = "获取位置";
///
/// 获得/设置 获取持续定位监听器ID
///
[Parameter]
public long? WatchID { get; set; }
///
/// 获得/设置 获取移动距离追踪按钮文字 默认为 移动距离追踪
///
[Parameter]
[NotNull]
public string? WatchPositionButtonText { get; set; } = "移动距离追踪";
///
/// 获得/设置 获取停止追踪按钮文字 默认为 停止追踪
///
[Parameter]
[NotNull]
public string? ClearWatchPositionButtonText { get; set; } = "停止追踪";
///
/// 获得/设置 是否显示默认按钮界面
///
[Parameter]
public bool ShowButtons { get; set; } = true;
///
///
///
protected ElementReference GeolocationElement { get; set; }
///
/// 获得/设置 定位结果回调方法
///
[Parameter]
public Func? OnResult { get; set; }
///
/// 获得/设置 状态更新回调方法
///
[Parameter]
public Func? OnUpdateStatus { get; set; }
private IJSObjectReference? module;
private DotNetObjectReference? InstanceGeo { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
try
{
if (firstRender)
{
module = await JS!.InvokeAsync("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();
}
}
///
/// 获取定位
///
public virtual async Task GetLocation()
{
try
{
await module!.InvokeVoidAsync("getLocation", InstanceGeo);
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
}
///
/// 持续定位
///
public virtual async Task WatchPosition()
{
try
{
await module!.InvokeVoidAsync("getLocation", InstanceGeo, false);
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
}
///
/// 持续定位
///
public virtual async Task ClearWatch()
{
await module!.InvokeVoidAsync("clearWatchLocation", InstanceGeo, WatchID);
WatchID = null;
}
///
/// 定位完成回调方法
///
///
///
[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);
}
}
///
/// 获得/设置 错误回调方法
///
[Parameter]
public Func? OnError { get; set; }
///
/// 状态更新回调方法
///
///
///
[JSInvokable]
public async Task UpdateStatus(string status)
{
if (OnUpdateStatus != null) await OnUpdateStatus.Invoke(status);
}
///
/// 监听器ID回调方法
///
///
///
[JSInvokable]
public Task UpdateWatchID(long watchID)
{
this.WatchID = watchID;
return Task.CompletedTask;
}
}
4. Pages文件夹添加GeolocationPage.razor文件,用于演示组件调用.
4.1 GeolocationPage.razor
razor代码
@page "/geolocations"
定位/持续定位 Geolocation
@message
@status
纬度
经度
准确度(米)
时间戳
时间
@geolocations?.Latitude
@geolocations?.Longitude
@geolocations?.Accuracy
@geolocations?.Timestamp
@geolocations?.LastUpdateTime
移动距离
总移动距离
最后一次获取到的纬度
最后一次获取到的经度
状态
@geolocations?.CurrentDistance
@geolocations?.TotalDistance
@geolocations?.LastLat
@geolocations?.LastLong
@geolocations?.Status
4.2 GeolocationPage.razor.cs
扩展阅读:Chrome中模拟定位信息,清除定位信息
using Blazor100.Components;
namespace Blazor100.Pages;
///
/// Geolocation 地理定位/移动距离追踪
///
/// 扩展阅读:Chrome中模拟定位信息,清除定位信息
/// https://blog.csdn.net/u010844189/article/details/81163438
///
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. 首页引用组件演示页
或者 Shared/NavMenu.razor
添加导航
7. F5运行程序
至此,使用JS隔离制作定位/持续定位组件大功告成! Happy coding!
Blazor组件自做系列