From 64b92c9d376ea7df13145d698d513278e4eb9e94 Mon Sep 17 00:00:00 2001
From: coder-new <2578417052@qq.com>
Date: Wed, 10 Apr 2024 17:29:10 +0800
Subject: [PATCH] =?UTF-8?q?docs=F0=9F=93=9D:=20=E6=96=B0=E5=A2=9E=E7=AC=94?=
=?UTF-8?q?=E8=AE=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/README.md | 2 +-
src/web/javascript/event-binding.md | 57 +++
src/web/mobile-dev/uniapp/api.md | 319 +++++++++++++
src/web/mobile-dev/uniapp/attention.md | 19 +
src/web/mobile-dev/uniapp/component.md | 418 ++++++++++++++++++
.../{uniapp-template.md => template.md} | 10 +-
src/web/practical-skills/api.md | 198 +++++++++
7 files changed, 1017 insertions(+), 6 deletions(-)
create mode 100644 src/web/javascript/event-binding.md
create mode 100644 src/web/mobile-dev/uniapp/api.md
create mode 100644 src/web/mobile-dev/uniapp/attention.md
create mode 100644 src/web/mobile-dev/uniapp/component.md
rename src/web/mobile-dev/uniapp/{uniapp-template.md => template.md} (94%)
create mode 100644 src/web/practical-skills/api.md
diff --git a/src/README.md b/src/README.md
index aa52550..ce66ccf 100644
--- a/src/README.md
+++ b/src/README.md
@@ -43,7 +43,7 @@ projects:
footer:
- 蜀ICP备2023023297号-2
+ 蜀ICP备2023023297号
copyright: MIT Licensed | Copyright © 2020-present 杨不旧
diff --git a/src/web/javascript/event-binding.md b/src/web/javascript/event-binding.md
new file mode 100644
index 0000000..676d881
--- /dev/null
+++ b/src/web/javascript/event-binding.md
@@ -0,0 +1,57 @@
+---
+title: JavaScript事件绑定
+date: 2024-02-12
+category:
+ - javascript
+---
+
+
+
+
+## 事件类型
+
+JavaScript事件类型有以下几种:
+1. 鼠标事件(Mouse Events):包括点击、双击、鼠标移入移出、鼠标按下释放等。
+2. 键盘事件(Keyboard Events):包括按键按下、按键释放、按键组合等。
+3. 表单事件(Form Events):包括表单提交、表单重置等。
+4. 焦点事件(Focus Events):包括元素获得焦点、元素失去焦点等。
+5. 触摸事件(Touch Events):包括触摸开始、触摸移动、触摸结束等。
+6. 窗口事件(Window Events):包括窗口打开、窗口关闭、窗口刷新等。
+
+### 鼠标事件
+
+鼠标单击左键-`click`、鼠标单击右键、鼠标双击-`dbclick`
+鼠标移入-`mouseover`、鼠标悬停-`mouseover`、鼠标移出-`mouseleave`
+
+
+### 键盘事件
+
+键盘按下-`keydown`、键盘松开-`keyup`、`keypress`
+
+### 表单事件
+
+`input`、`change`、`submit`、`blur`、`focus`、`reset`
+
+### 触摸事件
+开始触摸-`touchstart`、触摸移动-`touchmove`、触摸结束-`touchend`、触摸打算-`touchcancel`
+
+轻按-`tap`、长按-`longtap`
+
+columnchange: 'columnchange',
+linechange: 'linechange',
+error: 'error',
+scrolltoupper: 'scrolltoupper',
+scrolltolower: 'scrolltolower',
+scroll: 'scroll'
+
+
+## 事件委托
+
+### 什么是事件委托
+
+JavaScript事件委托(Event delegation)又叫事件代理,是一种在父元素上监听事件,然后通过事件冒泡机制来处理子元素的事件的技术。通过事件委托,可以避免为每个子元素都绑定事件处理程序,提高性能并简化代码。
+
+### 事件委托基本原理
+
+事件委托的基本原理是将事件处理程序绑定在父元素上,然后通过事件冒泡捕获到子元素的事件触发。这样,无论子元素是现有的还是动态生成的,它们的事件都会被父元素捕获并处理。
+
diff --git a/src/web/mobile-dev/uniapp/api.md b/src/web/mobile-dev/uniapp/api.md
new file mode 100644
index 0000000..6984137
--- /dev/null
+++ b/src/web/mobile-dev/uniapp/api.md
@@ -0,0 +1,319 @@
+---
+title: 自封装的hook
+date: 2024-02-25
+category:
+ - 移动开发
+---
+
+
+### 根据path对文件进行签名
+:::details 查看代码
+```ts
+import SparkMD5 from "spark-md5";
+export const md5File = (
+ path: string,
+): Promise<{
+ md5: string;
+ size: number;
+}> => {
+ return new Promise((resolve, reject) => {
+ plus.io.requestFileSystem(
+ plus.io.PRIVATE_WWW,
+ (fs: PlusIoFileSystem) => {
+ fs.root?.getFile(
+ path,
+ { create: false },
+ (fileEntry: PlusIoFileEntry) => {
+ fileEntry.file(
+ (file: PlusIoFile) => {
+ const fileReader: PlusIoFileReader = new plus.io.FileReader();
+ fileReader.onloadend = (evt: PlusIoFileEvent) => {
+ resolve({
+ // @ts-ignore
+ md5: calculateBase64Hash(evt.target?.result),
+ size: file.size || 0,
+ });
+ };
+ fileReader.readAsDataURL(file, "utf-8");
+ },
+ fileError => {
+ reject("获取文件对象失败:" + fileError);
+ },
+ );
+ },
+ fileEntryError => {
+ reject("读取文件失败:" + fileEntryError);
+ },
+ );
+ },
+ fsError => {
+ reject("读取文件失败:" + fsError);
+ },
+ );
+ });
+};
+
+/**
+ * 根据文件的base64生成md5
+ *
+ * @param base64 文件的base64
+ * @returns 文件的md5码
+ */
+const calculateBase64Hash = (base64: string): string => {
+ const spark = new SparkMD5();
+ spark.appendBinary(base64);
+ return spark.end();
+};
+```
+:::
+
+### 根据path判断文件是否存在
+:::details 查看代码
+```ts
+/**
+ * 根据文件路径异步判断该路径下的文件是否存在
+ *
+ * @param path 文件路径
+ * @returns 返回true代表该文件存在,返回false代表该文件不存在
+ */
+export const checkFileExists = (path: string): Promise => {
+ return new Promise(resolve => {
+ if (path) {
+ // #ifdef APP-PLUS
+ plus.io.requestFileSystem(
+ plus.io.PRIVATE_DOC,
+ (fs: PlusIoFileSystem) => {
+ fs.root?.getFile(
+ path,
+ { create: false },
+ () => {
+ resolve(true);
+ },
+ error => {
+ resolve(false);
+ },
+ );
+ },
+ () => {
+ resolve(false);
+ },
+ );
+ // #endif
+ // #ifdef H5
+ resolve(false);
+ // #endif
+ } else {
+ resolve(false);
+ }
+ });
+};
+```
+:::
+
+### 获取手机缓存文件总共多大
+:::details 查看代码
+```ts
+/**
+ * 异步获取手机缓存有多大
+ *
+ */
+export const calculateDeviceCacheSize = (): Promise => {
+ return new Promise(resolve => {
+ // #ifdef APP-PLUS
+ let isGetSuccess: boolean = false;
+ plus.io.requestFileSystem(
+ plus.io.PRIVATE_DOC,
+ (fs: PlusIoFileSystem) => {
+ /**
+ * 允许最大时间查询文件大小,避免一些情况下卡住无法返回文件的大小
+ */
+ let timer: NodeJS.Timeout = setTimeout(() => {
+ !isGetSuccess &&
+ resolve({
+ flag: 1,
+ data: formatBytes(0),
+ });
+ clearTimeout(timer);
+ }, 2000);
+ fs.root?.getMetadata(
+ (directoryEntry: PlusIoMetadata) => {
+ isGetSuccess = true;
+ resolve({
+ flag: 1,
+ data: formatBytes(Number(directoryEntry.size)),
+ });
+ clearTimeout(timer);
+ },
+ getError => {
+ resolve({
+ flag: 0,
+ errMsg: getError,
+ });
+ },
+ true,
+ );
+ },
+ error => {
+ resolve({
+ flag: 0,
+ errMsg: error,
+ });
+ },
+ );
+ // #endif
+ // #ifdef H5
+ resolve({
+ flag: 0,
+ errMsg: "仅支持在APP上获取缓存文件大小",
+ });
+ // #endif
+ });
+};
+```
+:::
+### 清除手机缓存文件
+:::details 查看代码
+```ts
+/**
+ * 异步清除设备缓存文件
+ */
+export const clearDeviceCache = (): Promise => {
+ return new Promise(resolve => {
+ // #ifdef APP-PLUS
+ uni.showLoading({
+ title: "清除设备缓存中",
+ });
+ plus.io.requestFileSystem(
+ plus.io.PRIVATE_DOC,
+ (fs: PlusIoFileSystem) => {
+ fs.root?.removeRecursively(
+ () => {
+ uni.hideLoading();
+ resolve({
+ flag: 1,
+ errMsg: "",
+ });
+ },
+ failRes => {
+ uni.hideLoading();
+ resolve({
+ flag: 0,
+ errMsg: failRes.message,
+ });
+ },
+ );
+ },
+ error => {
+ uni.hideLoading();
+ resolve({
+ flag: 0,
+ errMsg: error.message,
+ });
+ },
+ );
+ // #endif
+ // #ifdef H5
+ resolve({
+ flag: 0,
+ errMsg: "仅在APP下支持清除缓存功能",
+ });
+
+ // #endif
+ });
+};
+```
+:::
+### 根据bytes转换单位
+:::details 查看代码
+```ts
+/**
+ * 将字节大小格式化如:1KB、1MB、1GB等
+ *
+ * @param bytes 字节大小
+ * @returns 转换单位类似为:1KB、1MB、1GB等
+ */
+export const formatBytes = (bytes: number): string => {
+ if (bytes === 0) return "0 MB";
+ const k = 1024;
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
+};
+```
+:::
+
+
+### 下载安装包
+:::details 查看代码
+```ts
+export const downloadAPK = (url: string, errorCallback: (...args: any) => void) => {
+ if (plus.os.name?.toLocaleLowerCase() == "ios") {
+ plus.runtime.openURL(url);
+ } else {
+ const waitingToast: PlusNativeUIWaitingObj = plus.nativeUI.showWaiting("正在初始化下载...");
+ let downloadTask: PlusDownloaderDownload = plus.downloader.createDownload(
+ url + useJsonToUrl({ version: userInfoStore.userConf.latestAppVersion, appId: defaultConfig.appid }),
+ {
+ method: "GET",
+ timeout: 3,
+ retryInterval: 1,
+ retry: 2,
+ },
+ (download: PlusDownloaderDownload, status: number) => {
+ plus.downloader.clear();
+ waitingToast.setTitle("下载完成,等待安装...");
+ if (status == 200) {
+ download.filename &&
+ plus.runtime.install(
+ plus.io.convertLocalFileSystemURL(download.filename),
+ {
+ force: true,
+ },
+ () => {
+ plus.nativeUI.closeWaiting();
+ },
+ installErrorMsg => {
+ plus.nativeUI.closeWaiting();
+ errorCallback("安装错误:" + installErrorMsg);
+ },
+ );
+ } else {
+ plus.nativeUI.closeWaiting();
+ errorCallback("下载最新APP版本失败,请稍后重试或联系APP开发者");
+ }
+ },
+ );
+
+ try {
+ downloadTask.setRequestHeader("Authorization", userInfoStore.token.authorization);
+ downloadTask.start();
+ downloadTask.addEventListener("statechanged", (task: PlusDownloaderDownload) => {
+ switch (task.state) {
+ case 1:
+ waitingToast.setTitle("正在连接服务器...");
+ break;
+ case 2:
+ waitingToast.setTitle("服务器连接成功,开始下载...");
+ break;
+ case 3:
+ if (task.downloadedSize && task.totalSize) {
+ const percent = parseInt(((parseFloat(task.downloadedSize.toString()) / parseFloat(task.totalSize.toString())) * 100).toString());
+ waitingToast.setTitle("正在下载(" + percent + "%)...");
+ }
+ break;
+ case 4:
+ break;
+ }
+ });
+ } catch (err) {
+ plus.nativeUI.closeWaiting();
+ errorCallback("下载失败,请稍后重试");
+ }
+ }
+};
+```
+:::
+
+
+
+
diff --git a/src/web/mobile-dev/uniapp/attention.md b/src/web/mobile-dev/uniapp/attention.md
new file mode 100644
index 0000000..feb1e56
--- /dev/null
+++ b/src/web/mobile-dev/uniapp/attention.md
@@ -0,0 +1,19 @@
+---
+title: 开发APP注意事项
+date: 2024-01-21
+category:
+ - 移动开发
+---
+
+
+1. uniapp中没有File文件对象,html5plus中的文件数据对象并不是前端中的File文件对象,需要注意区分。
+2. uniapp中没有`URL.createObjectURL`方法。
+3. uniapp中的`display:grid`是不起作用的,若想配置高度动画变换,需要设置定高、`overflow:hidden`,同时计算隐藏那部分的高度,当显示的时候就是设置的高度 + 计算隐藏的高度 就等于整个容器的高度,但该方法不适合用于内部高度变化比较多的地方。
+
+
+### 组件实例
+在一些如果想通过组件实例获取webview组件的时候,同样,也不能嵌套太深,可以在跟组件实例化:`getCurrentInstance()`,然后通过`inject`、`provide`
+
+### 弹框
+1. 弹框弹出的时候,滚动穿透问题。具体表现为:当在弹出的弹框在滑动时,页面也会随着滚动,但设置`@touchmove.stop.prevent="()=>{}"`后,此时滑动页面不会随着滑动了,但是如果弹框内的内容想滚动时,就会滚动不了。
+2. 一些弹框组件使用的位置组件嵌套不能太深,太深遮罩层无法覆盖整个页面。此时可以建议用`inject`和`provide`,将组件实例注入到子组件中,子组件想使用弹框可以用`inject`进行接收。
\ No newline at end of file
diff --git a/src/web/mobile-dev/uniapp/component.md b/src/web/mobile-dev/uniapp/component.md
new file mode 100644
index 0000000..e314e9b
--- /dev/null
+++ b/src/web/mobile-dev/uniapp/component.md
@@ -0,0 +1,418 @@
+---
+title: 自封装的组件
+date: 2024-01-21
+category:
+ - 移动开发
+---
+
+
+### 录音组件
+
+:::details 查看代码
+
+::: tabs
+@tab useVModel
+```ts
+import { computed } from "vue";
+export const useVModel = (props: AnyObject, propName: string, emit: (...args: any) => void) => {
+ return computed({
+ get() {
+ return new Proxy(props, {
+ set(obj, name, val) {
+ emit("update:" + propName, {
+ ...obj,
+ [name]: val,
+ });
+ return true;
+ },
+ });
+ },
+ set(val) {
+ emit("update:" + propName, val);
+ },
+ });
+};
+
+```
+
+@tab 使用示例
+```vue
+
+
+
+
+
+```
+
+@tab 组件源码
+```vue
+
+
+ {}">
+
+ 录制音频
+
+
+
+
+ 录制状态:
+ {{ recordingStatus.text }}
+
+
+ 最长可录制时长:
+ {{ recordingStatus.totalSeconds }}s
+
+ 录制时长:{{ recordingStatus.recordSeconds === 0 ? "-" : recordingStatus.recordSeconds + "s" }}
+
+
+
+
+
+ 开始录音
+
+
+
+
+
+ 录音完成
+
+
+
+
+
+
+
+
+
+
+
+```
+:::
+
+### 动画组件
+
+仿vue原生Transition组件
+
+**注意事项:需要下载animate.css库,同时注意引入的时候不能命名为Transition进行使用,会与vue原生的Transition组件冲突,而原生组件在APP中是不支持的**
+:::details 查看代码
+```vue
+
+
+
+
+
+
+
+
+
+
+```
+:::
\ No newline at end of file
diff --git a/src/web/mobile-dev/uniapp/uniapp-template.md b/src/web/mobile-dev/uniapp/template.md
similarity index 94%
rename from src/web/mobile-dev/uniapp/uniapp-template.md
rename to src/web/mobile-dev/uniapp/template.md
index 26900b8..5049559 100644
--- a/src/web/mobile-dev/uniapp/uniapp-template.md
+++ b/src/web/mobile-dev/uniapp/template.md
@@ -1,5 +1,5 @@
---
-title: uni-app模板
+title: 配置模板
date: 2024-02-21
category:
- 移动开发
@@ -20,16 +20,16 @@ category:
}
```
-2. 代码分包。代码分包可以减少app首次加载启动时所耗费的时间,需要在`manifest.json`和`pages.json`两个文件同时配置才会生效:
+1. 代码分包。代码分包可以减少app首次加载启动时所耗费的时间,需要在`manifest.json`和`pages.json`两个文件同时配置才会生效:
- [manifest.json参考配置](https://uniapp.dcloud.net.cn/collocation/manifest.html#app-vue-optimization)
- [pages.json参考配置](https://uniapp.dcloud.net.cn/collocation/pages.html#subpackages),注意`pages.json`是需要配置`subPackages`和`preloadRule`的。
- 代码分包不止可以运用在app上,小程序上也是可以的,具体看官方的介绍
-3. 开发app竖屏锁定,不运行app页面跟随页面旋转而进行旋转。这个需要在`pages.json`下的`globalstyle/pageOrientation`下进行配置,[pageOrientation配置参考](https://uniapp.dcloud.net.cn/collocation/pages.html#globalstyle)
+2. 开发app竖屏锁定,不运行app页面跟随页面旋转而进行旋转。这个需要在`pages.json`下的`globalstyle/pageOrientation`下进行配置,[pageOrientation配置参考](https://uniapp.dcloud.net.cn/collocation/pages.html#globalstyle)
-4. `rpxCalcBaseDeviceWidth`、`rpxCalcBaseDeviceWidth`、`rpxCalcIncludeWidth`。[三个值的介绍](https://uniapp.dcloud.net.cn/collocation/pages.html#globalstyle),一般默认即可,或者可能设计师给的稿纸宽度不是375,此时可以设置一下`rpxCalcBaseDeviceWidth`,这三个值方便我们进行响应式的布局,另外`dynamicRpx`根据实际情况是否设置为true,为true会根据屏幕大小变化进而重新进行页面布局
+3. `rpxCalcBaseDeviceWidth`、`rpxCalcBaseDeviceWidth`、`rpxCalcIncludeWidth`。[三个值的介绍](https://uniapp.dcloud.net.cn/collocation/pages.html#globalstyle),一般默认即可,或者可能设计师给的稿纸宽度不是375,此时可以设置一下`rpxCalcBaseDeviceWidth`,这三个值方便我们进行响应式的布局,另外`dynamicRpx`根据实际情况是否设置为true,为true会根据屏幕大小变化进而重新进行页面布局
-5. `transformPx`,[transformPx配置参考](https://uniapp.dcloud.net.cn/collocation/manifest.html#%E9%85%8D%E7%BD%AE%E9%A1%B9%E5%88%97%E8%A1%A8)设为true时,方便我们将px转化为rpx,从而方便我们进行响应式的布局,当然,也可以部根据第四五点所介绍的uniapp内置的响应式布局配置,你还可以使用第三方的插件,如`postcss-px-to-viewport`,仅在开发环境使用即可,它可以将你所指定的单位和稿纸宽度全部转换成你所期待的单位。如果你的项目是ts声明,需要手动书写声明文件,也可以参考以下声明文件:
+4. `transformPx`,[transformPx配置参考](https://uniapp.dcloud.net.cn/collocation/manifest.html#%E9%85%8D%E7%BD%AE%E9%A1%B9%E5%88%97%E8%A1%A8)设为true时,方便我们将px转化为rpx,从而方便我们进行响应式的布局,当然,也可以部根据第四五点所介绍的uniapp内置的响应式布局配置,你还可以使用第三方的插件,如`postcss-px-to-viewport`,仅在开发环境使用即可,它可以将你所指定的单位和稿纸宽度全部转换成你所期待的单位。如果你的项目是ts声明,需要手动书写声明文件,也可以参考以下声明文件:
```ts
declare module "postcss-px-to-viewport" {
type Unit = "px" | "rem" | "cm" | "em" | "rpx";
diff --git a/src/web/practical-skills/api.md b/src/web/practical-skills/api.md
new file mode 100644
index 0000000..adaa6d9
--- /dev/null
+++ b/src/web/practical-skills/api.md
@@ -0,0 +1,198 @@
+---
+title: 开发中常用API集合
+date: 2021-12-12
+category:
+ - 实用技巧
+---
+
+### 判断某个坐标是否在电子围栏内
+
+:::details 查看代码
+```ts
+/**
+ * 判断当前位置是否在电子围栏内(注意坐标类型都是wgs84)
+ * @param currentPoint 当前位置坐标,如:[longitude, latitude]
+ * @param polygonPoint 电子围栏坐标,如:[ [longitude, latitude], [longitude, latitude] ]
+ */
+export const useIsPointInPolygon = (userLocation: [number, number], fence: [number, number][]): boolean => {
+ const userLongitude = userLocation[0];
+ const userLatitude = userLocation[1];
+
+ // 通过射线法判断点是否在多边形内部
+ let isIn = false;
+ for (let i = 0, j = fence.length - 1; i < fence.length; j = i++) {
+ const fenceLongitude1 = fence[i][0];
+ const fenceLatitude1 = fence[i][1];
+ const fenceLongitude2 = fence[j][0];
+ const fenceLatitude2 = fence[j][1];
+
+ // 判断点与多边形的边是否相交
+ const intersect =
+ fenceLatitude1 > userLatitude !== fenceLatitude2 > userLatitude &&
+ userLongitude < ((fenceLongitude2 - fenceLongitude1) * (userLatitude - fenceLatitude1)) / (fenceLatitude2 - fenceLatitude1) + fenceLongitude1;
+ if (intersect) isIn = !isIn;
+ }
+
+ console.log("isIn", isIn);
+
+ return isIn;
+};
+
+```
+:::
+
+### wgs84与gcj02坐标类型互转
+
+:::details 查看代码
+```ts
+const x_PI: number = (3.14159265358979324 * 3000.0) / 180.0;
+const PI: number = 3.1415926535897932384626;
+const a: number = 6378245.0; //卫星椭球坐标投影到平面地图坐标系的投影因子。
+const ee: number = 0.00669342162296594323; //椭球的偏心率。
+
+/**
+ * 判断是否是中国范围内的坐标
+ * @param {array} coordinate 传入坐标,格式:[经度, 纬度]
+ * @returns {boolean} 是否在中国范围内
+ */
+const out_of_china = (coordinate: Coordinate): boolean => {
+ return coordinate[0] < 72.004 || coordinate[0] > 137.8347 || coordinate[1] < 0.8293 || coordinate[1] > 55.8271 || false;
+};
+
+/**
+ * 转为纬度
+ * @param {number} lng 经度
+ * @param {number} lat 纬度
+ * @returns {number} 转化后的纬度
+ */
+const transformlat = (lng: number, lat: number): number => {
+ let ret: number = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
+ ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
+ ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0;
+ ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0;
+ return ret;
+};
+/**
+ * 转为经度
+ * @param {number} lng 经度
+ * @param {number} lat 纬度
+ * @returns {number} 转化后的经度
+ */
+const transformlng = (lng: number, lat: number): number => {
+ let ret: number = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
+ ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
+ ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0;
+ ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0;
+ return ret;
+};
+
+/**
+ * wgs84 和 gcj02 坐标互转
+ * @param {array} coordinate 传入坐标,格式:[经度, 纬度]
+ * @param {string} type 默认值为:"wgs84-gcj02",即wgs84转gcj02,可选值为:"gcj02-wgs84"
+ * @returns {array} 转化结果,格式:[经度, 纬度]
+ */
+export const useCoordinateTransform = (coordinate: Coordinate, type: string = "wgs84-gcj02"): Coordinate => {
+ if (out_of_china(coordinate)) return coordinate;
+ if (type === "wgs84-gcj02") {
+ var dlat = transformlat(coordinate[0] - 105.0, coordinate[1] - 35.0);
+ var dlng = transformlng(coordinate[0] - 105.0, coordinate[1] - 35.0);
+ var radlat = (coordinate[1] / 180.0) * PI;
+ var magic = Math.sin(radlat);
+ magic = 1 - ee * magic * magic;
+ var sqrtmagic = Math.sqrt(magic);
+ dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+ dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
+ var mglat = coordinate[1] + dlat;
+ var mglng = coordinate[0] + dlng;
+
+ return [mglng, mglat];
+ } else if (type === "gcj02-wgs84") {
+ var dlat = transformlat(coordinate[0] - 105.0, coordinate[1] - 35.0);
+ var dlng = transformlng(coordinate[0] - 105.0, coordinate[1] - 35.0);
+ var radlat = (coordinate[1] / 180.0) * PI;
+ var magic = Math.sin(radlat);
+ magic = 1 - ee * magic * magic;
+ var sqrtmagic = Math.sqrt(magic);
+ dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+ dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
+ mglat = coordinate[1] + dlat;
+ mglng = coordinate[0] + dlng;
+ return [coordinate[0] * 2 - mglng, coordinate[1] * 2 - mglat];
+ }
+ return coordinate;
+};
+
+```
+:::
+
+
+### useVModel
+:::details 查看代码
+```ts
+import { computed } from "vue";
+export const useVModel = (props: AnyObject, propName: string, emit: (...args: any) => void) => {
+ return computed({
+ get() {
+ return new Proxy(props, {
+ set(obj, name, val) {
+ emit("update:" + propName, {
+ ...obj,
+ [name]: val,
+ });
+ return true;
+ },
+ });
+ },
+ set(val) {
+ emit("update:" + propName, val);
+ },
+ });
+};
+
+```
+:::
+
+
+### useSelecterNodeInfo
+
+:::details 查看代码
+```ts
+import { getCurrentInstance } from "vue";
+export const useSelecterNodeInfo = (selector: string): Promise => {
+ const query = uni.createSelectorQuery().in(getCurrentInstance());
+ return new Promise(resolve => {
+ query
+ .select(selector)
+ .boundingClientRect((nodeRef: UniApp.NodeInfo | UniApp.NodeInfo[]) => {
+ resolve(nodeRef);
+ })
+ .exec();
+ });
+};
+
+```
+:::
+
+### useGetStringSize
+
+:::details 查看代码
+```ts
+export const useGetStringSize = (str: string): number => {
+ let len: number = 0;
+ for (let i = 0; i < str.length; i++) {
+ const code = str.charCodeAt(i);
+ if (code <= 0x007f) {
+ len += 1;
+ } else if (code <= 0x07ff) {
+ len += 2;
+ } else if (code <= 0xffff) {
+ len += 3;
+ } else {
+ len += 4;
+ }
+ }
+ return len;
+};
+```
+:::
\ No newline at end of file