فهرست منبع

下载增加动效

李万涛 1 سال پیش
والد
کامیت
19682f5751
75فایلهای تغییر یافته به همراه6934 افزوده شده و 27 حذف شده
  1. 25 18
      index.html
  2. 3 0
      main.js
  3. 54 8
      pages/robot/index.vue
  4. 1 1
      pages/user/signin.vue
  5. 6 0
      uni_modules/c-progress-circle/changelog.md
  6. 185 0
      uni_modules/c-progress-circle/components/c-progress-circle/c-progress-circle.vue
  7. 82 0
      uni_modules/c-progress-circle/package.json
  8. 98 0
      uni_modules/c-progress-circle/readme.md
  9. 2 0
      uni_modules/l-circularProgress/changelog.md
  10. 202 0
      uni_modules/l-circularProgress/components/l-circularProgress/l-circularProgress.vue
  11. 83 0
      uni_modules/l-circularProgress/package.json
  12. 54 0
      uni_modules/l-circularProgress/readme.md
  13. 25 0
      uni_modules/lime-circle/changelog.md
  14. 82 0
      uni_modules/lime-circle/components/l-circle/animation/bezier.ts
  15. 2 0
      uni_modules/lime-circle/components/l-circle/animation/ease.ts
  16. 99 0
      uni_modules/lime-circle/components/l-circle/animation/index.ts
  17. 0 0
      uni_modules/lime-circle/components/l-circle/circle.js
  18. 31 0
      uni_modules/lime-circle/components/l-circle/getCanvas.ts
  19. 116 0
      uni_modules/lime-circle/components/l-circle/index.scss
  20. 309 0
      uni_modules/lime-circle/components/l-circle/l-circle.vue
  21. 56 0
      uni_modules/lime-circle/components/l-circle/props.ts
  22. 59 0
      uni_modules/lime-circle/components/l-circle/type.ts
  23. 28 0
      uni_modules/lime-circle/components/l-circle/useTransition.ts
  24. 50 0
      uni_modules/lime-circle/components/l-circle/utils.ts
  25. 9 0
      uni_modules/lime-circle/components/l-circle/vue.ts
  26. 76 0
      uni_modules/lime-circle/components/lime-circle/lime-circle.vue
  27. 0 0
      uni_modules/lime-circle/hybrid/html/circle.min.js
  28. 216 0
      uni_modules/lime-circle/hybrid/html/index.html
  29. 0 0
      uni_modules/lime-circle/hybrid/html/uni.webview.1.5.3.js
  30. 87 0
      uni_modules/lime-circle/package.json
  31. 114 0
      uni_modules/lime-circle/readme.md
  32. 12 0
      uni_modules/lime-shared/addUnit/index.ts
  33. 63 0
      uni_modules/lime-shared/arrayBufferToFile/index.ts
  34. 13 0
      uni_modules/lime-shared/base64ToArrayBuffer/index.ts
  35. 76 0
      uni_modules/lime-shared/base64ToPath/index.ts
  36. 58 0
      uni_modules/lime-shared/canIUseCanvas2d/index.ts
  37. 24 0
      uni_modules/lime-shared/changelog.md
  38. 11 0
      uni_modules/lime-shared/clamp/index.ts
  39. 50 0
      uni_modules/lime-shared/cloneDeep/index.ts
  40. 11 0
      uni_modules/lime-shared/closest/index.ts
  41. 149 0
      uni_modules/lime-shared/createAnimation/index.ts
  42. 61 0
      uni_modules/lime-shared/createImage/index.ts
  43. 18 0
      uni_modules/lime-shared/debounce/index.ts
  44. 1056 0
      uni_modules/lime-shared/exif/index.ts
  45. 13 0
      uni_modules/lime-shared/fillZero/index.ts
  46. 13 0
      uni_modules/lime-shared/getClassStr/index.ts
  47. 6 0
      uni_modules/lime-shared/getCurrentPage/index.ts
  48. 14 0
      uni_modules/lime-shared/getLocalFilePath/index.ts
  49. 71 0
      uni_modules/lime-shared/getRect/index.ts
  50. 15 0
      uni_modules/lime-shared/getStyleStr/index.ts
  51. 8 0
      uni_modules/lime-shared/hasOwn/index.ts
  52. 43 0
      uni_modules/lime-shared/index.ts
  53. 5 0
      uni_modules/lime-shared/isBase64/index.ts
  54. 2 0
      uni_modules/lime-shared/isBrowser/index.ts
  55. 5 0
      uni_modules/lime-shared/isDef/index.ts
  56. 4 0
      uni_modules/lime-shared/isFunction/index.ts
  57. 5 0
      uni_modules/lime-shared/isNumber/index.ts
  58. 5 0
      uni_modules/lime-shared/isNumeric/index.ts
  59. 4 0
      uni_modules/lime-shared/isObject/index.ts
  60. 6 0
      uni_modules/lime-shared/isPromise/index.ts
  61. 5 0
      uni_modules/lime-shared/isString/index.ts
  62. 83 0
      uni_modules/lime-shared/package.json
  63. 64 0
      uni_modules/lime-shared/pathToBase64/index.ts
  64. 2320 0
      uni_modules/lime-shared/piexif/index.ts
  65. 27 0
      uni_modules/lime-shared/platform/index.ts
  66. 23 0
      uni_modules/lime-shared/raf/index.ts
  67. 14 0
      uni_modules/lime-shared/random/index.ts
  68. 21 0
      uni_modules/lime-shared/range/index.ts
  69. 191 0
      uni_modules/lime-shared/readme.md
  70. 139 0
      uni_modules/lime-shared/selectComponent/index.ts
  71. 5 0
      uni_modules/lime-shared/sleep/index.ts
  72. 19 0
      uni_modules/lime-shared/throttle/index.ts
  73. 5 0
      uni_modules/lime-shared/toArray/index.ts
  74. 6 0
      uni_modules/lime-shared/toNumber/index.ts
  75. 37 0
      uni_modules/lime-shared/unitConvert/index.ts

+ 25 - 18
index.html

xqd
@@ -1,20 +1,27 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <script>
-      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
-        CSS.supports('top: constant(a)'))
-      document.write(
-        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
-        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
-    </script>
-    <title></title>
-    <!--preload-links-->
-    <!--app-context-->
-  </head>
-  <body>
-    <div id="app"><!--app-html--></div>
-    <script type="module" src="/main.js"></script>
-  </body>
-</html>
+	<head>
+		<meta charset="UTF-8" />
+		<script>
+			var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+				CSS.supports('top: constant(a)'))
+			document.write(
+				'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+				(coverSupport ? ', viewport-fit=cover' : '') + '" />')
+		</script>
+		<!-- 		<script src="
+	https://cdn.jsdelivr.net/npm/vant@2.6.0/lib/vant.min.js
+	"></script>
+		<link href="
+	https://cdn.jsdelivr.net/npm/vant@2.6.0/lib/index.min.css
+	" rel="stylesheet"> -->
+
+		<title></title>
+		<!--preload-links-->
+		<!--app-context-->
+	</head>
+	<body>
+		<div id="app"><!--app-html--></div>
+		<script type="module" src="/main.js"></script>
+	</body>
+</html>

+ 3 - 0
main.js

xqd
@@ -18,6 +18,9 @@ require('./utils/request/index')(app)
 let vuexStore = require('@/store/$t.mixin.js');
 async function bootstrap() {
 	App.mpType = "app";
+
+	// 可以通过下面的方式手动注册
+	// Vue.use(vant.Lazyload);
 	//引入路由
 	Vue.use(router);
 	// 引入全局uView

+ 54 - 8
pages/robot/index.vue

xqd xqd xqd xqd xqd xqd xqd
@@ -92,13 +92,25 @@
 			</view>
 
 
-			<!-- <button @click="test">{{percentage216}}</button> -->
 
 
+			<u-popup :show="show" @close="close" @open="open" mode="center">
+				<view
+					style="background: rgba(0, 0, 0, 0);width: 300rpx;height: 300rpx;display: flex;align-items: center;flex-direction: column;justify-content: center;color:#26b3a0;">
+					<l-circularProgress :isBgShow="true" :lineWidth="10" boxWidth="100" boxHeight="100"
+						progressColor="#26b3a0" fontColor="#26b3a0" gradualColor="#26b3a0"
+						:percent="progress"></l-circularProgress>
+					<view class="">
+						视频下载中...
+					</view>
+				</view>
+			</u-popup>
+
 			<u-picker :defaultIndex='[0]' :closeOnClickOverlay="true" :showIcon="true" :show="showMoreMenu"
 				:columns="columns" title="更多" confirmColor="" @cancel="showMoreMenu=false" @confirm="handleConfirm"
 				@close="handleClosePicker"></u-picker>
 
+
 			<wike-tabbar :onTabbar="true" :isShowAnimate="true"></wike-tabbar>
 		</view>
 	</view>
@@ -116,6 +128,9 @@
 	export default {
 		data() {
 			return {
+				show: false,
+
+
 				showMoreMenu: false,
 				columns: [
 					['删除', '下载视频', '复制链接']
@@ -130,7 +145,11 @@
 				isPc: false,
 				timer: null,
 				// percentage: 0,
-				reqTimes: 0
+				reqTimes: 0,
+
+				progress: 0,
+
+				isDowload: false
 			};
 		},
 		computed: {
@@ -210,6 +229,13 @@
 			this.getIndexData()
 		},
 		methods: {
+			open() {
+				// console.log('open');
+			},
+			close() {
+				this.show = false
+				// console.log('close');
+			},
 			handleClosePicker() {
 				this.showMoreMenu = false
 				// this.columns = [
@@ -407,8 +433,15 @@
 					} else {
 						if (detectDeviceType()) {
 							let _this = this
+
+							if (_this.isDowload) {
+								uni.showToast({
+									icon: 'none',
+									title: '请等待上一个视频下载完成后,再下载',
+								});
+							}
 							// PC端
-							this.delItem.url = 'http://www.liwantao.top/test.mp4'
+							// this.delItem.url = 'http://www.liwantao.top/test.mp4'
 
 							// downLoad3(this.delItem.url, '数字人' + this.delItem.name + '视频', 'video/mp4')
 
@@ -425,6 +458,11 @@
 									}
 								},
 								fail: (err) => {
+									_this.show = false;
+									_this.isDowload = false
+									_this.progress = 0
+
+									// console.log(_this.show, _this.isDowload, _this.progress, err);
 									uni.showToast({
 										icon: 'none',
 										mask: true,
@@ -435,12 +473,20 @@
 
 							downloadTask.onProgressUpdate((res) => {
 								if (res.progress > 0) {
-									// this.isShowProgress = true;
+									_this.show = true;
+									_this.isDowload = true
+								}
+								_this.progress = res.progress;
+								if (res.progress == 100) {
+									_this.show = false;
+									_this.isDowload = false
+									_this.progress = 0
+
+									uni.showToast({
+										icon: 'success',
+										title: _this.delItem.name + ',下载成功!',
+									});
 								}
-								// this.progress = res.progress;
-								console.log('下载进度:' + res.progress);
-								console.log('已下载长度:' + res.totalBytesWritten);
-								console.log('文件总长度:' + res.totalBytesExpectedToWrite);
 							})
 						} else {
 							// 其他移动端浏览器

+ 1 - 1
pages/user/signin.vue

xqd
@@ -1,7 +1,7 @@
 <template>
 	<view>
 		<view style="color: #000;">
-			<tn-nav-bar backgroundColor="#fff" :bottomShadow="false">登录</tn-nav-bar>
+			<tn-nav-bar backgroundColor="#fff" :bottomShadow="false" :isBack="false">登录</tn-nav-bar>
 			<view :style="{height: tobheight+'px'}"></view>
 		</view>
 		<block>

+ 6 - 0
uni_modules/c-progress-circle/changelog.md

xqd
@@ -0,0 +1,6 @@
+## 1.0.2(2023-04-13)
+新增boderWidth属性 可设置边框宽度
+## 1.0.1(2023-03-27)
+兼容vue2
+## 1.0.0(2023-01-05)
+初次提交

+ 185 - 0
uni_modules/c-progress-circle/components/c-progress-circle/c-progress-circle.vue

xqd
@@ -0,0 +1,185 @@
+<template>
+	<view class='container' :style="{width:size,height:size}">
+		<view class="left">
+			<view class="leftcircle" :style="leftcircleStyle">
+			</view>
+		</view>
+		<view class="right">
+			<view class="rightcircle" :style="rightcircleStyle"></view>
+		</view>
+		<view class="cneter-box">
+			<slot>
+				{{progress*100}}%
+			</slot>
+		</view>
+	</view>
+</template>
+<script>
+	/**
+	 * c-lockScreen 环形进度条
+	 * @property {Number} progress 进度 0-1 默认0
+	 * @property {String} color 进度条颜色 默认#3ec3c1
+	 * @property {String} size 进度条尺寸 单位rpx 默认200rpx
+	 * @property {String} boderWidth 边框宽度 单位rpx 默认200rpx
+	 * */
+	 export default {
+	 	props:{
+			progress: {
+				type: Number,
+				default: 0
+			},
+			color: {
+				type: String,
+				default: '#3ec3c1'
+			},
+			size: {
+				type: String,
+				default: "200rpx"
+			},
+			boderWidth:{
+				type: String,
+				default: "200rpx"
+			}
+		},
+		computed:{
+			leftcircleStyle(){
+				return `border-width: calc(${this.boderWidth} * 0.1);
+				border-bottom-color:${this.color};
+				border-left-color:${this.color};
+				transform: rotate(${this.leftAngle});
+				`
+			},
+			rightcircleStyle(){
+				return `border-width: calc(${this.boderWidth} * 0.1);
+				border-top-color:${this.color};
+				border-right-color:${this.color};
+				transform: rotate(${this.rightAngle});`
+			},
+			rightAngle(){
+				if (this.progress >= 0.5) {
+					return 45+'deg'
+				} else {
+					return -135 + 180 * this.progress * 2+'deg'
+				}
+			},
+			leftAngle(){
+				if (this.progress < 0.5) {
+					return -135+'deg'
+				} else {
+					return -315 + 180 * this.progress * 2+'deg'
+				}
+			}
+		}
+	 }
+</script>
+<style lang="scss" scoped>
+	view{
+		box-sizing: border-box;
+	}
+	.container {
+		display: flex;
+		position: relative;
+		// width: v-bind(size);
+		// height: v-bind(size);
+		.left {
+			width: 50%;
+			height: 100%;
+			position: relative;
+			overflow: hidden;
+			top: 0;
+			left: 0;
+
+			.leftcircle {
+				width: 200%;
+				height: 100%;
+				border-color:white;
+				border-style: solid;
+				position: absolute;
+				border-radius: 50%;
+				left: 0px;
+				top: 0px;
+				transition: transform 0.2s;
+				//vue3
+				// border-width: calc(v-bind(size) * 0.1);
+				// border-bottom-color:v-bind(color);
+				// border-left-color:v-bind(color);
+				// transform: rotate(v-bind(leftAngle));
+
+				// animation-name: circle_left;
+				// animation-duration: 2s;
+				// animation-timing-function: linear;
+				// animation-iteration-count: infinite;
+
+			}
+
+		}
+
+		.right {
+			width: 50%;
+			height: 100%;
+			position: relative;
+			overflow: hidden;
+			top: 0;
+			right: 0;
+		}
+
+		.rightcircle {
+			width: 200%;
+			height: 100%;
+			border-radius: 50%;
+			border-color:white;
+			border-style: solid;
+			position: absolute;
+			
+			right: 0px;
+			top: 0px;
+			// animation-name: circle_right;
+			// animation-duration: 2s;
+			// animation-timing-function: linear;
+			// animation-iteration-count: infinite;
+			transition: transform 0.2s;
+			//vue3
+			// border-width: calc(v-bind(size) * 0.1);
+			// border-top-color:v-bind(color);
+			// border-right-color:v-bind(color);
+			// transform: rotate(v-bind(rightAngle));
+		}
+		.cneter-box{
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			font-size: 24rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+		}
+	}
+
+
+
+
+
+
+	// @keyframes circle_right {
+	// 	0% {
+	// 		transform: rotate(-135deg);
+	// 	}
+
+	// 	50%,
+	// 	100% {
+	// 		transform: rotate(45deg);
+	// 	}
+	// }
+
+	// @keyframes circle_left {
+
+	// 	0%,
+	// 	50% {
+	// 		transform: rotate(-135deg);
+	// 	}
+
+	// 	100% {
+	// 		transform: rotate(45deg);
+	// 	}
+	// }
+</style>

+ 82 - 0
uni_modules/c-progress-circle/package.json

xqd
@@ -0,0 +1,82 @@
+{
+  "id": "c-progress-circle",
+  "displayName": "c-design c-progress-circle",
+  "version": "1.0.2",
+  "description": "环形进度条",
+  "keywords": [
+    "c-progress-circle",
+    "环形进度条"
+],
+  "repository": "https://gitee.com/wangziwl/c-designc.git",
+  "engines": {
+    "HBuilderX": "^3.6.15"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "u",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+          "QQ": "u",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 98 - 0
uni_modules/c-progress-circle/readme.md

xqd
@@ -0,0 +1,98 @@
+# c-progress-circle
+
+### 
+- c-progress-circle 环形进度条
+
+### c-design交流群号:330647926
+
+### 示例预览
+[https://cloud.vuedata.wang/cdesign/#/pages/progress-circle/progress-circle](https://cloud.vuedata.wang/cdesign/#/pages/progress-circle/progress-circle)
+
+### 一、使用示例
+```html
+<template>
+	<view class="content">
+		<c-progress-circle :progress='progress' :color='color' :size='size' :boderWidth="boderWidth"></c-progress-circle>
+		<view class="btnBox">
+			<button size="mini" @click="add">+</button>
+			<view style="width: 100rpx;text-align: center;">
+				{{progress}}
+			</view>
+			<button size="mini" @click="reduce" type="default">-</button>
+		</view>
+		<view class="btnBox" style="padding-left: 100rpx;">边框颜色</view>
+		<view class="btnBox">
+			<button size="mini" @click="color='red'">红色</button>
+			<button size="mini" @click="color='green'">绿色</button>
+			<button size="mini" @click="color='orange'">橙色</button>
+		</view>
+		<view class="btnBox" style="padding-left: 100rpx;">进度条尺寸</view>
+		<view class="btnBox">
+			<button size="mini" @click="size='200rpx'">200rpx</button>
+			<button size="mini" @click="size='400rpx'">400rpx</button>
+			<button size="mini" @click="size='600rpx'">600rpx</button>
+		</view>
+		<view class="btnBox" style="padding-left: 100rpx;">边框宽度</view>
+		<view class="btnBox">
+			<button  size="mini" @click="boderWidth='200rpx'">200rpx</button>
+			<button  size="mini" @click="boderWidth='400rpx'">400rpx</button>
+			<button  size="mini" @click="boderWidth='600rpx'">600rpx</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				progress: 0.2,
+				color:'red',
+				size:'200rpx',
+				boderWidth:'200rpx'
+			}
+		},
+		methods: {
+			add() {
+				if(this.progress<1){
+					this.progress +=0.1
+					this.progress=this.progress.toFixed(1)*1
+				}
+			},
+			reduce(){
+				if(this.progress>0){
+					this.progress -=0.1
+					this.progress=this.progress.toFixed(1)*1	
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.content{
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		padding-top: 20rpx;
+		font-size: 28rpx;
+	}
+	.btnBox{
+		width: 100%;
+		display: flex;
+		align-items: center;
+		margin-top:30rpx;
+	}
+</style>
+```
+
+
+
+
+### 二、属性介绍
+
+| 字段			| 类型		| 必填	| 默认值				| 描述								|
+| -----------	| --------	| ----	| ----------------------| -------------------------------	|
+| progress		| Number	| 否	|  0					| 进度 0-1							|
+| color			| String	| 否	|  #3ec3c1				| 进度条颜色						|
+| size			| String	| 否	|  200rpx				| 进度条尺寸 单位rpx				|
+| boderWidth	| String	| 否	|  200rpx				| 边框宽度 单位rpx					|

+ 2 - 0
uni_modules/l-circularProgress/changelog.md

xqd
@@ -0,0 +1,2 @@
+## 1.0.0(2023-05-13)
+1.0.0

+ 202 - 0
uni_modules/l-circularProgress/components/l-circularProgress/l-circularProgress.vue

xqd
@@ -0,0 +1,202 @@
+<template>
+  <view class="circular-container" :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }">
+    <canvas class="circular-bg" :canvas-id="bgCanvasId" :id="bgCanvasId"
+      :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }" v-if="isBgShow"></canvas>
+    <canvas class="circular-progress" :canvas-id="canvasId" :id="canvasId"
+      :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }"></canvas>
+    <slot></slot>
+  </view>
+</template>
+<script>
+  export default {
+    name: 'LCircularProgress',
+    props: {
+      // 宽度
+      boxWidth: {
+       	type: Number,
+        default: 200
+      },
+      //高度
+      boxHeight: {
+        type: Number,
+        default: 200
+      },
+      //进度条线条宽度
+      lineWidth: {
+        type: Number,
+        default: 4
+      },
+      //是否显示背景进度条
+      isBgShow: {
+        type: Boolean,
+        default: true
+      },
+      //背景进度条颜色
+      bgColor: {
+      	type: String,
+      	default: '#CCCCCC'
+      },
+      // 进度条类型 circular圆形 halfCircular 半圆
+      type:{
+        type:String,
+        default:'circular'
+      },
+      //进度百分比 
+      percent: {
+      	type: Number,
+      	default: 0
+      },
+      //进度条颜色
+      progressColor: {
+      	type: String,
+      	default: '#5677fc'
+      },
+      //进度条渐变颜色
+      gradualColor: {
+      	type: String,
+      	default: ''
+      },
+      //是否显示进度文字
+      fontShow: {
+      	type: Boolean,
+      	default: true
+      },
+      //进度字体大小
+      fontSize: {
+      	type: Number,
+      	default: 12
+      },
+      //进度字体颜色
+      fontColor: {
+      	type: String,
+      	default: '#5677fc'
+      },
+    },
+    watch: {
+    	percent(val) {
+    		this.draw();
+    	}
+    },
+    mounted() {
+      this.draw(true)
+    },
+    data() {
+      return {
+        canvasId: 'canvasId',
+        bgCanvasId: 'bgCanvasId',
+        startPercent: 0,
+        progressContext: null,
+        linearGradient: null,
+      }
+    },
+    methods:{
+      draw(init) {
+      	let start = this.startPercent;
+      	start = start > this.percent ? 0 : start;
+      	if (this.isBgShow && init) { //初始化需判断 防止后续手动增加再次绘制背景
+      		this.drawBgCircular();
+      	}
+      	this.drawCircular(start);
+      },
+      //背景
+      drawBgCircular() {
+      	let ctx = uni.createCanvasContext(this.bgCanvasId, this);
+      	let lineWidth = Number(this.lineWidth)
+      	ctx.setLineWidth(lineWidth);
+      	ctx.setStrokeStyle(this.bgColor);
+      	//终止弧度
+        let end = null,start = null
+        if(this.type == 'circular'){
+          start = -Math.PI / 2
+          end = Math.PI - start;
+        }else{
+          start = -Math.PI
+          end = 0;
+        }
+      	
+      	this.drawArc(ctx,start,end);
+      },
+      //创建弧线
+      drawArc(ctx,start,end) {
+      	ctx.setLineCap('round');
+      	ctx.beginPath();
+      	let radius = this.boxWidth / 2;
+      	let lineWidth = Number(this.lineWidth)
+      	ctx.arc(radius, radius, radius - lineWidth, start, end, false);
+      	ctx.stroke();
+      	ctx.draw();
+      },
+      //进度圆环
+      drawCircular(startPercent) {
+      	let ctx = this.progressContext;
+      	let gradient = this.linearGradient;
+      	if (!ctx) {
+      		ctx = uni.createCanvasContext(this.canvasId, this);
+      		let boxWidth = Number(this.boxWidth)
+      		gradient = ctx.createLinearGradient(0, 0, boxWidth, 0);
+      		gradient.addColorStop('0', this.progressColor);
+      		if (this.gradualColor) {
+      			gradient.addColorStop('1', this.gradualColor);
+      		}
+      		this.progressContext = ctx;
+      		this.linearGradient = gradient;
+      	}
+      	let lineWidth = Number(this.lineWidth)
+      	ctx.setLineWidth(lineWidth);
+      	ctx.setStrokeStyle(gradient);
+      	let time = this.percent == 0 || this.duration < 50 ? 0 : this.duration / this.percent;
+      	if (this.percent > 0) {
+      		startPercent = this.duration < 50 ? this.percent - 1 : startPercent;
+      		startPercent++;
+      	}
+      	if (this.fontShow) {
+      		let fontSize = Number(this.fontSize)
+      		ctx.setFontSize(fontSize);
+      		ctx.setFillStyle(this.fontColor);
+      		ctx.setTextAlign('center');
+      		ctx.setTextBaseline('middle');
+      		let percent = startPercent + '%';
+      		let radius = this.boxWidth / 2;
+      		ctx.fillText(percent, radius, radius);
+      	}
+      	if (this.percent == 0 ) {
+      		ctx.draw();
+      	} else {
+          //终止弧度
+          let end = null,start = null
+          if(this.type == 'circular'){
+            start = -Math.PI / 2
+            end = ((2 * Math.PI) / 100) * startPercent + start;
+          }else{
+            start = -Math.PI
+            end = (Math.PI / 100) * startPercent + start;
+          }
+      		let eAngle = ((2 * Math.PI) / 100) * startPercent + this.sAngle;
+      		this.drawArc(ctx,start,end);
+      	}
+      	setTimeout(() => {
+      		this.startPercent = startPercent;
+      		if (startPercent >= this.percent) {
+      			this.$emit('end', {
+      				percent: startPercent + '%'
+      			});
+      		} else {
+      			this.drawCircular(startPercent);
+      		}
+      	}, time);
+      },
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .circular-container {
+    position: relative;
+
+    .circular-progress {
+      position: absolute;
+      left: 0;
+      top: 0;
+      z-index: 999;
+    }
+  }
+</style>

+ 83 - 0
uni_modules/l-circularProgress/package.json

xqd
@@ -0,0 +1,83 @@
+{
+  "id": "l-circularProgress",
+  "displayName": "环形进度条 半圆进度条 圆形进度条",
+  "version": "1.0.0",
+  "description": "l-circularProgress 圆形或半圆形进度条在开发中常遇到 如有需要可下载 仅测过小程序其他自测",
+  "keywords": [
+    "环形进度条",
+    "半圆进度条",
+    "圆形进度条",
+    "渐变进度条"
+],
+  "repository": "",
+"engines": {
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        }
+      }
+    }
+  }
+}

+ 54 - 0
uni_modules/l-circularProgress/readme.md

xqd
@@ -0,0 +1,54 @@
+# l-circularProgress
+传入的值单位都是px 需自行转化适配
+例如:宽度{
+  需要传入的宽度px = 想要的宽度rpx / 750 * wx.getSystemInfoSync().windowWidth
+}
+
+# ## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<l-circularProgress :isBgShow="false" :lineWidth="10" boxWidth="100" boxHeight="100" progressColor="#ff7900" fontColor="#5677fc" gradualColor="#5677fc"
+	  :percent="80"></l-circularProgress>
+	<l-circularProgress progressColor="#EB0559" :lineWidth="10" fontColor="#EB0999" gradualColor="#f57900" :percent="100">
+	</l-circularProgress>
+	<l-circularProgress :fontShow="false" :percent="66" type="halfCircular" :lineWidth="10" gradualColor="#f57900" progressColor="#19be6b" fontColor="#19be6b"
+	  bgColor="rgba(25,190,107,0.1)" @end="end">
+	  <view class="text">
+	    <view>{{num}}/6</view>
+	    <view class="">已签到/应签到</view>
+	  </view>
+	</l-circularProgress>
+</template>
+```
+## 组件属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+|:---:|:---:|:---:|---|
+| boxWidth | Number | 100 | 宽度 |
+| boxHeight | Number | 100 | 高度 |
+| lineWidth | Number | 4 | 进度条线条宽度 |
+| isBgShow | Boolean | true | 是否显示背景进度条 |
+| bgColor | String | #CCCCCC | 背景进度条颜色 |
+| type | String | circular | 进度条类型 circular圆形 halfCircular 半圆 |
+| percent | Number | 0 | 进度百分比 |
+| progressColor | String | #5677fc | 进度条颜色 |
+| gradualColor | String | '' | 进度条渐变颜色 跟progressColor一起使用 |
+| fontShow | Boolean | true | 是否显示进度文字 |
+| fontSize | Number | 12 | 进度字体大小 |
+| fontColor | String | #5677fc | 进度字体颜色 |
+
+
+## 组件事件
+
+| 名称 | 触发时机 |
+|:---:|---|
+| end | 动画结束 |
+
+
+## 插槽
+
+| 名称 | 触发时机 |
+|:---:|---|
+| slot | 自定义文字 |

+ 25 - 0
uni_modules/lime-circle/changelog.md

xqd
@@ -0,0 +1,25 @@
+## 0.1.2(2023-06-13)
+- feat: 增加 canvas 实现方式
+## 0.1.1(2023-06-08)
+- feat: 去掉`units.ts`改用`lime-shared`
+## 0.1.0(2023-04-13)
+- chore: 修改文档中的错误
+## 0.0.9(2023-04-13)
+- chore: 文档增加vue2使用方法示例
+## 0.0.8(2023-04-04)
+- chore: 去掉 script-setup 语法糖
+- chore: 文档增加 vue2 使用方法
+## 0.0.7(2022-11-09)
+- feat: 破坏性更新,由原来的canvas改为css3实现
+## 0.0.6(2022-11-04)
+- feat: 增加使用uni-popup示例
+## 0.0.5(2022-11-03)
+- fix: 修复 有毛点问题(因清空尺寸不对)
+## 0.0.4(2022-11-02)
+- feat: 增加  `delay` props 延时渲染,用于把组件放在弹窗时,无法正确获取尺寸时。
+## 0.0.3(2022-11-02)
+更新文档
+## 0.0.2(2022-11-01)
+更新文档
+## 0.0.1(2022-11-01)
+首次上传

+ 82 - 0
uni_modules/lime-circle/components/l-circle/animation/bezier.ts

xqd
@@ -0,0 +1,82 @@
+export function cubicBezier(p1x: number, p1y: number, p2x: number, p2y: number) {
+  const ZERO_LIMIT = 1e-6;
+  // Calculate the polynomial coefficients,
+  // implicit first and last control points are (0,0) and (1,1).
+  const ax = 3 * p1x - 3 * p2x + 1;
+  const bx = 3 * p2x - 6 * p1x;
+  const cx = 3 * p1x;
+
+  const ay = 3 * p1y - 3 * p2y + 1;
+  const by = 3 * p2y - 6 * p1y;
+  const cy = 3 * p1y;
+
+  function sampleCurveDerivativeX(t: number) {
+    // `ax t^3 + bx t^2 + cx t` expanded using Horner's rule
+    return (3 * ax * t + 2 * bx) * t + cx;
+  }
+
+  function sampleCurveX(t: number) {
+    return ((ax * t + bx) * t + cx) * t;
+  }
+
+  function sampleCurveY(t: number) {
+    return ((ay * t + by) * t + cy) * t;
+  }
+
+  // Given an x value, find a parametric value it came from.
+  function solveCurveX(x: number) {
+    let t2 = x;
+    let derivative: number;
+    let x2: number;
+
+    // https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
+    // first try a few iterations of Newton's method -- normally very fast.
+    // http://en.wikipedia.org/wikiNewton's_method
+    for (let i = 0; i < 8; i++) {
+      // f(t) - x = 0
+      x2 = sampleCurveX(t2) - x;
+      if (Math.abs(x2) < ZERO_LIMIT) {
+        return t2;
+      }
+      derivative = sampleCurveDerivativeX(t2);
+      // == 0, failure
+      /* istanbul ignore if */
+      if (Math.abs(derivative) < ZERO_LIMIT) {
+        break;
+      }
+      t2 -= x2 / derivative;
+    }
+
+    // Fall back to the bisection method for reliability.
+    // bisection
+    // http://en.wikipedia.org/wiki/Bisection_method
+    let t1 = 1;
+    /* istanbul ignore next */
+    let t0 = 0;
+
+    /* istanbul ignore next */
+    t2 = x;
+    /* istanbul ignore next */
+    while (t1 > t0) {
+      x2 = sampleCurveX(t2) - x;
+      if (Math.abs(x2) < ZERO_LIMIT) {
+        return t2;
+      }
+      if (x2 > 0) {
+        t1 = t2;
+      } else {
+        t0 = t2;
+      }
+      t2 = (t1 + t0) / 2;
+    }
+
+    // Failure
+    return t2;
+  }
+
+  function solve(x: number) {
+    return sampleCurveY(solveCurveX(x));
+  }
+
+  return solve;
+}

+ 2 - 0
uni_modules/lime-circle/components/l-circle/animation/ease.ts

xqd
@@ -0,0 +1,2 @@
+import {cubicBezier} from './bezier';
+export let ease = cubicBezier(0.25, 0.1, 0.25, 1);

+ 99 - 0
uni_modules/lime-circle/components/l-circle/animation/index.ts

xqd
@@ -0,0 +1,99 @@
+// @ts-nocheck
+const TICK = Symbol('tick');
+const TICK_HANDLER = Symbol('tick-handler');
+const ANIMATIONS = Symbol('animations');
+const START_TIMES = Symbol('start-times');
+const PAUSE_START = Symbol('pause-start');
+const PAUSE_TIME = Symbol('pause-time');
+const _raf = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(cb: Function) {return setTimeout(cb, 1000/60)}
+const _caf = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame: function(id: any) {clearTimeout(id)}
+
+export class Timeline {
+	state: string
+	constructor() {
+		this.state = 'Initiated';
+		this[ANIMATIONS] = new Set();
+		this[START_TIMES] = new Map();
+	}
+	start() {
+		if (!(this.state === 'Initiated')) return;
+		this.state = 'Started';
+
+		let startTime = Date.now();
+		this[PAUSE_TIME] = 0;
+		this[TICK] = () => {
+			let now = Date.now();
+			for (let animation of this[ANIMATIONS]) {
+				let t: number;
+
+				if (this[START_TIMES].get(animation) < startTime) {
+					t = now - startTime - animation.delay - this[PAUSE_TIME];
+				} else {
+					t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME];
+				}
+
+				if (t > animation.duration) {
+					this[ANIMATIONS].delete(animation);
+					t = animation.duration;
+				}
+				if (t > 0) animation.run(t);
+			}
+			this[TICK_HANDLER] = _raf(this[TICK]);
+		};
+		this[TICK]();
+	}
+	pause() {
+		if (!(this.state === 'Started')) return;
+		this.state = 'Paused';
+
+		this[PAUSE_START] = Date.now();
+		_caf(this[TICK_HANDLER]);
+	}
+	resume() {
+		if (!(this.state === 'Paused')) return;
+		this.state = 'Started';
+
+		this[PAUSE_TIME] += Date.now() - this[PAUSE_START];
+		this[TICK]();
+	}
+	reset() {
+		this.pause();
+		this.state = 'Initiated';
+		this[PAUSE_TIME] = 0;
+		this[PAUSE_START] = 0;
+		this[ANIMATIONS] = new Set();
+		this[START_TIMES] = new Map();
+		this[TICK_HANDLER] = null;
+	}
+	add(animation: any, startTime?: number) {
+		if (arguments.length < 2) startTime = Date.now();
+		this[ANIMATIONS].add(animation);
+		this[START_TIMES].set(animation, startTime);
+	}
+}
+
+export class Animation {
+	startValue: number
+	endValue: number
+	duration: number
+	timingFunction: (t: number) => number
+	delay: number
+	template: (t: number) => void
+	constructor(startValue: number, endValue: number, duration: number, delay: number, timingFunction: (t: number) => number, template: (v: number) => void) {
+		timingFunction = timingFunction || (v => v);
+		template = template || (v => v);
+		
+		this.startValue = startValue;
+		this.endValue = endValue;
+		this.duration = duration;
+		this.timingFunction = timingFunction;
+		this.delay = delay;
+		this.template = template;
+	}
+
+	run(time: number) {
+		let range = this.endValue - this.startValue;
+		let progress = this.timingFunction(time / this.duration);
+		this.template(this.startValue + range * progress)
+	}
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
uni_modules/lime-circle/components/l-circle/circle.js


+ 31 - 0
uni_modules/lime-circle/components/l-circle/getCanvas.ts

xqd
@@ -0,0 +1,31 @@
+
+// @ts-nocheck
+import type { ComponentInternalInstance } from './vue'
+import { getRect } from '@/uni_modules/lime-shared/getRect'
+import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
+export let isCanvas2d = canIUseCanvas2d()
+
+export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) {
+	let { context } = options
+	// #ifdef MP || VUE2
+	if (context.proxy) context = context.proxy
+	// #endif
+	return getRect('#' + canvasId, {context, type: isCanvas2d ? 'fields': 'boundingClientRect'}).then(({node}) => {
+		if(node){
+			return node
+		} else {
+			isCanvas2d = false
+			const ctx = uni.createCanvasContext(canvasId, context)
+			return {
+				getContext(type: string) {
+					if(type == '2d') {
+						return ctx
+					}
+				},
+			}
+			// #ifdef H5
+			// canvas.value = context.proxy.$el.querySelector('#'+ canvasId)
+			// #endif
+		}
+	})
+}

+ 116 - 0
uni_modules/lime-circle/components/l-circle/index.scss

xqd
@@ -0,0 +1,116 @@
+/* #ifndef APP-NVUE */
+// @import '@/uni_modules/lime-ui/style/index.scss';
+@supports(background: conic-gradient(#000, #fff)) {
+	.check {
+		height: 1rpx;
+		position: absolute;
+	}
+}
+@property --l-circle-percent {
+  syntax: '<percentage>';
+  initial-value: 25%;
+  inherits: false;
+}
+$fill-2: var(--l-fill-2, rgba(0, 0, 0, 0.06));
+$circle-trail-cap-size: var(--l-circle-trail-cap-size, 6px);
+$circle-trail-cap-color: currentColor;//var(--l-circle-trail-cap-color, currentColor);
+$circle-stroke-cap-size: var(--l-circle-stroke-cap-size, 6px);
+$circle-stroke-cap-start-color: var(--l-circle-stroke-cap-start-color, $fill-2);
+$circle-stroke-cap-end-color: var(--l-circle-stroke-cap-end-color, $fill-2);
+:root {
+	display: inline-block;
+}
+/*  #endif */
+.l-circle {
+	position: relative;
+	display: inline-block;
+	/* #ifndef APP-NVUE */
+	&__canvas {
+		width: 100%;
+		height: 100%;
+	}
+	/*  #endif */
+	&__inner{
+		position: absolute;
+		left: 0;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		align-items: center;
+		justify-content: center;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/*  #endif */
+	}
+	/* #ifdef APP-NVUE */
+	&__view {
+		flex: 1;
+	}
+	/*  #endif */
+	/* #ifndef APP-NVUE */
+	&__trail,&__stroke,&__stroke-line {
+		position: absolute;
+		width: 100%;
+		height: 100%;
+		border-radius: 50%;
+	}
+	&__stroke-line {
+		z-index: 2;
+		background: var(--l-background);
+	}
+	/*  #endif */
+}
+
+/* #ifndef APP-NVUE */
+.is-round {
+	.l-circle {
+		&__trail {
+			.cap {
+				position: absolute;
+				display: block;
+				width: $circle-trail-cap-size;
+				height: $circle-trail-cap-size;
+				background-color: $circle-trail-cap-color;
+				z-index: 10;
+				border-radius: 50%;
+				&.start {
+					left: var(--l-circle-trail-cap-start-x, 14%);
+					top: var(--l-circle-trail-cap-start-y, 81%);
+				}
+				&.end {
+					left: var(--l-circle-trail-cap-end-x, 14%);
+					top: var(--l-circle-trail-cap-end-y, 81%);
+				}
+			}
+		}
+		&__stroke .cap{
+			position: absolute;
+			display: block;
+			width: $circle-stroke-cap-size;
+			height: $circle-stroke-cap-size;
+			background-color: $circle-stroke-cap-start-color;
+			/* z-index: 10; */
+			border-radius: 50%;
+			transition: opacity 300ms ease-out;
+			opacity: var(--l-circle-stroke-cap-opacity, 0);
+			&.start {
+				left: var(--l-circle-stroke-cap-start-x, 14%);
+				top: var(--l-circle-stroke-cap-start-y, 81%);
+				background-color: $circle-stroke-cap-start-color;
+			}
+			&.end {
+				left: var(--l-circle-stroke-cap-end-x, 14%);
+				top: var(--l-circle-stroke-cap-end-y, 81%);
+				z-index: -1;
+				background-color: $circle-stroke-cap-end-color;
+			}
+		}
+	}
+}
+.clockwise {
+	transform: translateY(var(--l-circle-offset-top, 0)) scale(-1,1);
+}
+.clockwise .l-circle__inner{
+	transform: scale(-1,1);
+}
+/*  #endif */

+ 309 - 0
uni_modules/lime-circle/components/l-circle/l-circle.vue

xqd
@@ -0,0 +1,309 @@
+<template>
+	<view class="l-circle" :class="[{clockwise: !clockwise && !useCanvas}, ['is-' + lineCap]]" :style="[styles]">
+		<!-- #ifndef APP-NVUE -->
+		<view class="check"></view>
+		<view v-if="!useCanvas" class="l-circle__trail" :style="[trailStyles]">
+			<text class="cap start"></text>
+			<text class="cap end"></text>
+		</view>
+		<view v-if="!useCanvas" class="l-circle__stroke" :style="[strokeStyles]">
+			<view class="l-circle__stroke-line"></view>
+			<text class="cap start" v-if="current"></text>
+			<text class="cap end" v-if="current"></text>
+		</view>
+		<canvas v-if="useCanvas" type="2d" :canvas-id="canvasId" :id="canvasId" class="l-circle__canvas" ></canvas>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<web-view 
+			@pagefinish="finished = true" 
+			@error="onerror"
+			@onPostMessage="onMessage"
+			class="l-circle__view" 
+			ref="webview"
+			src="/uni_modules/lime-circle/hybrid/html/index.html"></web-view>
+		<!-- #endif -->
+		<view class="l-circle__inner">
+			<slot></slot>
+		</view>
+	</view>
+</template>
+
+<script lang="ts">
+	// @ts-nocheck
+	import { ref, computed, watch, reactive, defineComponent, onMounted ,onUnmounted, getCurrentInstance, nextTick } from './vue';
+	import { useTransition } from './useTransition';
+	import CircleProps from './props';
+	import { getCanvas , isCanvas2d} from './getCanvas';
+	import {Circle} from './circle'
+	import { addUnit } from '@/uni_modules/lime-shared/addUnit';
+	import { unitConvert } from '@/uni_modules/lime-shared/unitConvert';
+	import { isString } from '@/uni_modules/lime-shared/isString';
+	import { getRect } from '@/uni_modules/lime-shared/getRect';
+	// import { useTransition } from '@/uni_modules/lime-use';
+	
+	export default defineComponent({
+		name: 'l-circle',
+		props: CircleProps,
+		emits: ['update:current'],
+		setup(props, {emit}) {
+			const context = getCurrentInstance()
+			const useCanvas = ref(props.canvas)
+			const canvasId = `l-circle-${context.uid}`;
+			let circleCanvas = null
+			
+			const RADIAN = Math.PI / 180
+			const ratio = computed(() => 100 / props.max)
+			const percent = ref<number>(0)
+			const angle = computed(() => props.dashborad ? 135 : -90)
+			const isShowCap = computed(() => {
+				const { dashborad } = props
+				return current.value > 0 && (dashborad ? true : current.value < props.max)
+			})
+
+			const offsetTop = ref<number | string>(0)
+			const strokeEndCap = reactive({
+				x: '0',
+				y: '0'
+			})
+
+			const styles = computed(() => ({
+				width: addUnit(props.size),
+				height:addUnit(props.size),
+				// #ifdef APP-NVUE
+				transform: !useCanvas.value && `translateY(${offsetTop.value})`,
+				// #endif
+				// #ifndef APP-NVUE
+				'--l-circle-offset-top':  !useCanvas.value && offsetTop.value,
+				// #endif
+			}))
+			const classes = computed(() => {
+				const { clockwise, lineCap } = props
+				// {
+				// 	clockwise: !clockwise && !useCanvas.value,
+				// 	[`is-${lineCap}`]: lineCap
+				// }
+				return lineCap ? `is-${lineCap} ` : ' ' + !clockwise && !useCanvas.value && `clockwise`
+			})
+			
+			// css render 
+			const trailStyles = computed(() => {
+				const { size, trailWidth, trailColor, dashborad } = props
+				const circle = getCircle(size, trailWidth)
+				const mask = `radial-gradient(transparent ${circle.r - 0.5}px, #000 ${circle.r}px)`
+
+				let background = ''
+				let capStart = { x: '', y: '' }
+				let capEnd = capStart
+
+				if (dashborad) {
+					background = `conic-gradient(from 225deg, ${trailColor} 0%, ${trailColor} 75%, transparent 75%, transparent 100%)`
+					capStart = calcPosition(circle.c, 135)
+					capEnd = calcPosition(circle.c, 45)
+					offsetTop.value = (unitConvert(size) - (unitConvert(capStart.y) + unitConvert(trailWidth) / 2)) / 4 + 'px'
+				} else {
+					background = `${trailColor}`
+				}
+
+				return {
+					color: trailColor,
+					mask,
+					'-webkit-mask': mask,
+					background,
+					'--l-circle-trail-cap-start-x': capStart.x,
+					'--l-circle-trail-cap-start-y': capStart.y,
+					'--l-circle-trail-cap-end-x': capEnd.x,
+					'--l-circle-trail-cap-end-y': capEnd.y,
+					// '--l-circle-trail-cap-color': trailColor,
+					'--l-circle-trail-cap-size': addUnit(unitConvert(trailWidth))
+				}
+			})
+			const strokeStyles = computed(() => {
+				const { size, strokeWidth, strokeColor, dashborad, max } = props
+				const circle = getCircle(size, strokeWidth)
+				const percent = dashborad ? current.value * 0.75 * ratio.value : current.value * ratio.value;
+				const mask = `radial-gradient(transparent ${circle.r - 0.5}px, #000 ${circle.r}px)`
+				const cap = calcPosition(circle.c, angle.value)
+
+				let startColor = '';
+				let endColor = '';
+				let gradient = `conic-gradient(${dashborad ? 'from 225deg,' : ''} transparent 0%,`;
+				let gradientEnd = `transparent var(--l-circle-percent), transparent ${dashborad ? '75%' : '100%'})`
+
+				if (isString(strokeColor)) {
+					gradient += ` ${strokeColor} 0%, ${strokeColor} var(--l-circle-percent), ${gradientEnd}`
+					startColor = endColor = strokeColor
+				} else {
+					const len = strokeColor.length
+					for (let i = 0; i < len; i++) {
+						const color = strokeColor[i] as string
+						if (i === 0) {
+							gradient += `${color} 0%,`
+							startColor = color
+						} else {
+							gradient += `${color} calc(var(--l-circle-percent) * ${(i + 1) / len}),`
+						}
+						if (i == len - 1) {
+							endColor = color
+						}
+					}
+					gradient += gradientEnd
+				}
+				return {
+					mask,
+					'-webkit-mask': mask,
+					'--l-background': gradient,
+					// background: isString(strokeColor) ? strokeColor : strokeColor[0],
+					// background: gradient,
+					// transition: `--l-circle-percent ${duration}ms`,
+					'--l-circle-percent': `${percent / ratio.value == max ? percent + 0.1 : percent}%`,
+					'--l-circle-stroke-cap-start-color': startColor,
+					'--l-circle-stroke-cap-end-color': endColor,
+					'--l-circle-stroke-cap-start-x': cap.x,
+					'--l-circle-stroke-cap-start-y': cap.y,
+					'--l-circle-stroke-cap-end-x': strokeEndCap.x,
+					'--l-circle-stroke-cap-end-y': strokeEndCap.y,
+					'--l-circle-stroke-cap-size': addUnit(unitConvert(strokeWidth)),
+					'--l-circle-stroke-cap-opacity': isShowCap.value ? 1 : 0
+				}
+			})
+			const calcStrokeCap = () => {
+				const { size, strokeWidth, dashborad, max } = props
+				const circle = getCircle(size, strokeWidth)
+				const arc = dashborad ? 180 / 2 * 3 : 180 * 2
+				const step = arc / max * current.value + angle.value
+				const cap = calcPosition(circle.c, step)
+
+				strokeEndCap.x = cap.x
+				strokeEndCap.y = cap.y
+			}
+
+			const calcPosition = (r : number, angle : number) => {
+				return {
+					x: r + r * Math.cos(angle * RADIAN) + 'px',
+					y: r + r * Math.sin(angle * RADIAN) + 'px'
+				}
+			}
+
+			const getCircle = (size : number | string, lineWidth : number | string) => {
+				const s = unitConvert(size)
+				const w = unitConvert(lineWidth)
+				const c = (s - w) / 2
+				const r = s / 2 - w
+				return {
+					s, w, c, r
+				}
+			}
+			// css render end
+			const [current, stopTransition] = useTransition(percent, {
+				duration: props.duration,
+			})
+			const stopPercent = watch(() => props.percent, (v) => {
+				percent.value = v
+				circleCanvas && circleCanvas.play(v)
+			})
+			
+			const stopCurrent = watch(current, (v) => {
+				if(!useCanvas.value) {
+					calcStrokeCap()
+				} 
+				emit('update:current', v.toFixed(2))
+			})
+			const getProps = () => {
+				const {strokeWidth, trailWidth} = props
+				return Object.assign({}, props, {trailWidth: unitConvert(trailWidth), strokeWidth: unitConvert(strokeWidth)})
+			}
+			// #ifdef APP-NVUE
+			const finished = ref(false)
+			const init = ref(false)
+			const webview = ref(null)
+			const onerror = () => {
+				
+			}
+			const onMessage = (e: any) => {
+				const {detail:{data: [res]}} = e;
+				if(res.event == 'init') {
+					useCanvas.value = res.data.useCanvas // && props.canvas
+					init.value = true;
+					circleCanvas = {
+						setOption(props: any) {
+							webview.value.evalJs(`setOption(${JSON.stringify(props)})`)
+						},
+						play(v: number) {
+							webview.value.evalJs(`play(${v})`)
+						}
+					}
+				}
+				if(res.event == 'progress') {
+					current.value = res.data
+				}
+			}
+			let stopFinnished = watch(init, () => {
+				stopFinnished()
+				if(useCanvas.value) {
+					circleCanvas.setOption(getProps())
+					circleCanvas.play(props.percent)
+					stopTransition()
+				} else {
+					webview.value.evalJs(`setClass('.l-circle', 'is-round', ${props.lineCap == 'round'})`)
+					webview.value.evalJs(`setClass('.l-circle', 'clockwise', ${props.clockwise})`)
+					stopFinnished = watch([trailStyles, strokeStyles], (v) => {
+						webview.value.evalJs(`setStyle(0,${JSON.stringify(v[0])})`)
+						webview.value.evalJs(`setStyle(1,${JSON.stringify(v[1])})`)
+					}, { immediate: true })
+				}
+				percent.value = props.percent
+			})
+			
+			// #endif
+			// #ifndef APP-NVUE
+			onMounted(() => {
+				getRect('.check', {context}).then(res => {
+					// alert(CSS.supports('background', 'conic-gradient(#000, #fff)'))
+					useCanvas.value = !(res.height > 0 && !props.canvas)
+					if(useCanvas.value) {
+						stopTransition()
+						setTimeout(() => {
+							getCanvas(canvasId, {context}).then(res => {
+								circleCanvas = new Circle(res, {
+									size: unitConvert(props.size),
+									run: (v: number) => current.value = v,
+									pixelRatio: isCanvas2d ? uni.getSystemInfoSync().pixelRatio : 1,
+								})
+								circleCanvas.setOption(getProps())
+								circleCanvas.play(props.percent)
+							})
+						},50)
+					}
+					percent.value = props.percent
+				})
+			})
+			// #endif
+			onUnmounted(() => {
+				stopPercent()
+				stopCurrent()
+				stopTransition()
+				// #ifdef APP-NVUE
+				stopFinnished && stopFinnished()
+				// #endif
+			})
+			return {
+				useCanvas,
+				canvasId,
+				classes,
+				styles,
+				trailStyles,
+				strokeStyles,
+				current,
+				// #ifdef APP-NVUE
+				webview,
+				onerror,
+				onMessage,
+				finished
+				// #endif
+			}
+		}
+	})
+</script>
+<style lang="scss">
+	@import './index';
+</style>

+ 56 - 0
uni_modules/lime-circle/components/l-circle/props.ts

xqd
@@ -0,0 +1,56 @@
+// @ts-nocheck
+// import { CircleProps} from './type';
+// import { PropType } from 'vue';
+
+export default {
+	percent: {
+		type:  Number,
+		default: 0
+	},
+	size: {
+		type: String,
+		default: '120px'
+	},
+	lineCap: {
+		type: String,
+		default: 'round'
+	},
+	strokeWidth: {
+		type: [String, Number],
+		default: 6
+	},
+	strokeColor: {
+		type: [String , Array],
+		default: '#2db7f5'
+	},
+	trailWidth: {
+		type: [String, Number],
+		default: 6
+	},
+	trailColor: {
+		type: String,
+		default: '#ddd'
+	},
+	dashborad: Boolean,
+	clockwise: {
+		type: Boolean,
+		default: true
+	},
+	duration: {
+		type: Number,
+		default: 300
+	},
+	max: {
+		type: Number,
+		default: 100
+	},
+	/**缺口角度 暂未实现*/
+	// gapDegree: {
+	// 	type: Number,
+	// },
+	/**成功配置 暂未实现*/
+	// success: {
+	// 	type: Object
+	// },
+	canvas: Boolean
+}

+ 59 - 0
uni_modules/lime-circle/components/l-circle/type.ts

xqd
@@ -0,0 +1,59 @@
+// @ts-nocheck
+
+export interface CircleProps {
+	/**
+	* 进度环尺寸
+	* @default 120pxs
+	*/
+	size: string
+	/**
+	 * 百分比
+	 * @default 0
+	 */
+	percent: number
+	/**
+	 *  顶端形态
+	 * @default 'round' 
+	 */
+	lineCap: string
+	/**
+	 * 进度环线宽
+	 * @default 6
+	 */
+	strokeWidth: number | string
+	/**
+	 * 进度环颜色
+	 * @default #2db7f5
+	 */
+	strokeColor: string | string[]
+	/**
+	 * 轨道环线宽
+	 * @default 6
+	 */
+	trailWidth: number | string
+	/**
+	 * 轨道环颜色
+	 * @default #eaeef2
+	 */
+	trailColor: string
+	/**
+	 * 是否显示为仪表盘
+	 * @default false
+	 */
+	dashboard: boolean
+	/**
+	 * 是否为顺时针
+	 * @default true
+	 */
+	clockwise: boolean
+	/**
+	 * 持续时间
+	 * @default 300
+	 */
+	duration: number
+	/**
+	 *  总长度
+	 * @default 100
+	 */
+	max: number
+}

+ 28 - 0
uni_modules/lime-circle/components/l-circle/useTransition.ts

xqd
@@ -0,0 +1,28 @@
+// @ts-nocheck
+import {ease} from './animation/ease';
+import {Timeline, Animation} from './animation/';
+import {ref, watch, Ref} from './vue'
+	
+export function useTransition(percent: Ref<number>, options: {duration: number}) {
+	const current = ref(0)
+	const {immediate, duration} = options
+	let tl = new Timeline();
+	
+	const stop = watch(() => percent.value, (v) => {
+		tl.start();
+		tl.add(
+		  new Animation(
+		    current.value,
+		    v,
+		    duration,
+		    0,
+		    ease,
+		    v => {
+				current.value = v < 0.0001 ? 0: v
+			}
+		  )
+		);
+	}, {immediate})
+	
+	return [current, stop]
+}

+ 50 - 0
uni_modules/lime-circle/components/l-circle/utils.ts

xqd
@@ -0,0 +1,50 @@
+// @ts-nocheck
+export function isDef(value: unknown): boolean {
+  return value !== undefined && value !== null;
+}
+
+export function isNumber(value: string) {
+  return /^\d+(\.\d+)?$/.test(value);
+}
+
+/**
+ * 换成 px number
+ */
+
+export function toPx(value: string | number) {
+	// 如果是数字
+	if (typeof value === 'number') {
+		return value
+	}
+	// 如果是字符串数字
+	if (isNumber(value)) {
+		return Number(value)
+	}
+	// 如果有单位
+	if (typeof value === 'string') {
+		const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
+		const results = reg.exec(value);
+		if (!value || !results) {
+			return 0;
+		}
+		const unit = results[3];
+		value = parseFloat(value);
+		if (unit === 'rpx') {
+			return uni.upx2px(value);
+		} 
+		if (unit === 'px') {
+			return value * 1;
+		} 
+	}
+	return 0
+}
+
+
+export function addUnit(value?: string | number): string | undefined {
+  if (!isDef(value)) {
+    return undefined;
+  }
+
+  value = String(value);
+  return isNumber(value) ? `${value}px` : value;
+}

+ 9 - 0
uni_modules/lime-circle/components/l-circle/vue.ts

xqd
@@ -0,0 +1,9 @@
+// @ts-nocheck
+// export * from '@/uni_modules/lime-vue'
+
+// #ifdef VUE3
+export * from 'vue';
+// #endif
+// #ifndef VUE3
+export * from '@vue/composition-api';
+// #endif

+ 76 - 0
uni_modules/lime-circle/components/lime-circle/lime-circle.vue

xqd
@@ -0,0 +1,76 @@
+<template>
+	<demo-block title="进度环" type="ultra">
+		<demo-block title="基础">
+			<l-circle canvas v-model:current="modelValue1" :percent="target1" size="200rpx">
+				<text>{{modelValue1}}%</text>
+			</l-circle>
+			
+			<l-circle v-model:current="modelValue1" :percent="target1" :dashborad="false"  :clockwise="false" :stroke-color="['#0000ff', '#ff0000', '#ffa500']" size="200rpx">
+				<text>{{modelValue1}}%</text>
+			</l-circle>
+			<l-circle v-model:current="modelValue1"  strokeWidth="20" trailWidth="20" :percent="target1" :dashborad="true"  :stroke-color="['#0000ff', '#ff0000']" size="200rpx">
+				<text>{{modelValue1}}%</text>
+			</l-circle>
+			<button @tap="onClick(20)">+</button>
+			<button @tap="onClick(-20)">-</button>
+		</demo-block>
+		<demo-block title="倒计时">
+			<l-circle v-model:current="modelValue2" :percent="target4" :max="60" :dashborad="false" :clockwise="true" >
+				<text>{{modelValue2}}</text>
+			</l-circle>
+			<button @tap="onPlay">播放</button>
+			<button @tap="onStop">暂停</button>
+		</demo-block>
+	</demo-block>
+</template>
+<script>
+	import {ref, defineComponent} from '../l-circle/vue';
+	export default defineComponent({
+		setup() {
+			const target1 = ref(50)
+			const modelValue1 = ref(0)
+			
+			const target4 = ref(60)
+			const modelValue2 = ref(0)
+			
+			const onClick = number => {
+				target1.value = Math.max(Math.min(100, target1.value + number), 0)
+			}
+			
+			let timer = null
+			let isPlay = false
+			const countDown  = () => {
+				target4.value -= 1
+				if(target4.value) {
+					timer = setTimeout(countDown,1000)
+				}
+			}
+			const onStop = () => {
+				isPlay = false
+				clearTimeout(timer)
+			}
+			const onPlay = () => {
+				if(isPlay) return
+				isPlay = true
+				timer = setTimeout(countDown,1000)
+			}
+			
+			
+			
+			return {
+				target1,
+				target4,
+				modelValue1,
+				modelValue2,
+				
+				onClick,
+				onStop,
+				onPlay
+			}
+		}
+	})
+	
+</script>
+<style>
+	
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
uni_modules/lime-circle/hybrid/html/circle.min.js


+ 216 - 0
uni_modules/lime-circle/hybrid/html/index.html

xqd
@@ -0,0 +1,216 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="UTF-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<meta name="viewport" content="width=device-width, initial-scale=1.0">
+		<title>lime-circle</title>
+		<style>
+			html,
+			body {
+				padding: 0;
+				margin: 0;
+				width: 100%;
+				height: 100%;
+			}
+
+			.l-circle {
+				position: relative;
+				width: 100%;
+				height: 100%;
+			}
+
+			.l-circle__trail,
+			.l-circle__stroke {
+				position: absolute;
+				width: 100%;
+				height: 100%;
+				border-radius: 50%;
+			}
+
+			.l-circle__inner {
+				position: absolute;
+				left: 0;
+				right: 0;
+				top: 0;
+				bottom: 0;
+				align-items: center;
+				justify-content: center;
+				display: flex;
+			}
+
+			.is-round .l-circle__trail .cap {
+				position: absolute;
+				display: block;
+				width: var(--l-trail-cap-size, 6px);
+				height: var(--l-trail-cap-size, 6px);
+				background-color: var(--l-trail-cap-color, #ddd);
+				z-index: 10;
+				border-radius: 50%;
+			}
+
+			.is-round .l-circle__trail .cap.start {
+				left: var(--l-trail-cap-start-x, 14%);
+				top: var(--l-trail-cap-start-y, 81%);
+			}
+
+			.is-round .l-circle__trail .cap.end {
+				left: var(--l-trail-cap-end-x, 14%);
+				top: var(--l-trail-cap-end-y, 81%);
+			}
+
+			.is-round .l-circle__stroke .cap {
+				position: absolute;
+				display: block;
+				width: var(--l-stroke-cap-size, 6px);
+				height: var(--l-stroke-cap-size, 6px);
+				background-color: var(--l-stroke-cap-start-color, #ddd);
+				border-radius: 50%;
+				transition: opacity 350ms ease-out;
+				opacity: var(--l-stroke-cap-opacity, 0);
+			}
+
+			.is-round .l-circle__stroke .cap.start {
+				left: var(--l-stroke-cap-start-x, 14%);
+				top: var(--l-stroke-cap-start-y, 81%);
+				background-color: var(--l-stroke-cap-start-color, #ddd);
+			}
+
+			.is-round .l-circle__stroke .cap.end {
+				left: var(--l-stroke-cap-end-x, 14%);
+				top: var(--l-stroke-cap-end-y, 81%);
+				z-index: -1;
+				background-color: var(--l-stroke-cap-end-color, #ddd);
+			}
+
+			.clockwise .l-circle__inner {
+				transform: scale(-1, 1);
+			}
+		</style>
+	</head>
+	<body>
+		<canvas id="limeCanvas" class="l-circle"></canvas>
+		<div id="limeCss" class="l-circle">
+			<div class="l-circle__trail">
+				<i class="cap start"></i>
+				<i class="cap end"></i>
+			</div>
+			<div class="l-circle__stroke">
+				<i class="cap start"></i>
+				<i class="cap end"></i>
+			</div>
+		</div>
+		<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
+		<script type="text/javascript" src="./circle.min.js"></script>
+		<script>
+			const useCanvas = !CSS.supports('background', 'conic-gradient(#000, #fff)');
+			const canvas = document.querySelector('#limeCanvas');
+			const css = document.querySelector('#limeCss');
+			let circle = null
+			function toStyle(obj) {
+				let str = ''
+				for (const key in obj) {
+					str += `${key}:${obj[key]};`
+				}
+				return str
+			}
+
+			function setStyle(type, style) {
+				if (!type) {
+					const trail = document.querySelector('.l-circle__trail')
+					trail.style = toStyle(style)
+				} else {
+					const stroke = document.querySelector('.l-circle__stroke')
+					stroke.style = toStyle(style)
+				}
+				// trail.style.background = 'red'
+				// trail.setAttribute()
+				// trail.style.setProperty('--color','#cd0000');
+			}
+			// type 0删除 1增加
+			function setClass(target, className, type) {
+				const box = document.querySelector(target);
+				if (!type) {
+					box.classList.remove(className)
+				} else {
+					box.classList.add(className)
+				}
+			}
+
+			function postMessage(data) {
+				uni.postMessage({
+					data
+				});
+			};
+
+			function emit(event, data) {
+				postMessage({
+					event,
+					data
+				});
+			};
+
+			function setOption(data) {
+				if (circle) circle.setOption(data)
+			}
+
+			function clear() {
+				if (circle) circle.clear()
+			}
+
+			function play(v) {
+				if (circle) circle.play(v)
+			}
+
+			function stop() {
+				if (circle) circle.stop()
+			}
+
+			function dispose() {
+				if (circle) circle.dispose()
+			}
+
+			if (useCanvas) {
+				css.remove()
+				circle = new lime.Circle(canvas, {
+					pixelRatio: window.devicePixelRatio,
+					run: (current) => {
+						emit('progress', current);
+					}
+				})
+			} else {
+				canvas.remove()
+				setStyle(0, {
+					"mask": "radial-gradient(transparent 65.5px, #000 66px)",
+					"-webkit-mask": "radial-gradient(transparent 65.5px, #000 66px)",
+					"background": "conic-gradient(from 225deg, #ddd 0%, #ddd 75%, transparent 75%, transparent 100%)",
+					"--l-trail-cap-start-x": "20.209632098128225px",
+					"--l-trail-cap-start-y": "117.79036790187178px",
+					"--l-trail-cap-end-x": "117.79036790187178px",
+					"--l-trail-cap-end-y": "117.79036790187178px",
+					"--l-trail-cap-color": "#ddd",
+					"--l-trail-cap-size": "6px"
+				})
+
+				setStyle(1, {
+					"mask": "radial-gradient(transparent 57.5px, #000 58px)",
+					"-webkit-mask": "radial-gradient(transparent 57.5px, #000 58px)",
+					"background": "conic-gradient( transparent 0%, #2db7f5 0%, #2db7f5 var(--l-circle-percent), transparent var(--l-circle-percent), transparent 100%)",
+					"--l-circle-percent": "0%",
+					"--l-circle-stroke-cap-start-color": "#2db7f5",
+					"--l-circle-stroke-cap-end-color": "#2db7f5",
+					"--l-circle-stroke-cap-start-x": "61.00000000000001px",
+					"--l-circle-stroke-cap-start-y": "0px",
+					"--l-circle-stroke-cap-end-x": "0",
+					"--l-circle-stroke-cap-end-y": "0",
+					"--l-circle-stroke-cap-size": "6px",
+					"--l-circle-stroke-cap-opacity": 0
+				})
+			}
+
+			emit('init', {
+				useCanvas
+			})
+		</script>
+	</body>
+</html>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
uni_modules/lime-circle/hybrid/html/uni.webview.1.5.3.js


+ 87 - 0
uni_modules/lime-circle/package.json

xqd
@@ -0,0 +1,87 @@
+{
+  "id": "lime-circle",
+  "displayName": "进度环-圆形进度条-LimeUI",
+  "version": "0.1.2",
+  "description": "uniapp vue3 自动切换css和canvas的圆形进度环, 弧形进度条, 环形进度条, 有渐变色, 仪表盘等配置, 基本全端兼容, vue2只要配置@vue/composition-api",
+  "keywords": [
+    "进度环",
+    "弧形进度条",
+    "圆形进度条",
+    "渐变进度条",
+    "仪表盘进度条"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.6.4"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+		"lime-shared"
+	],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+          "QQ": "u",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 114 - 0
uni_modules/lime-circle/readme.md

xqd
@@ -0,0 +1,114 @@
+# lime-circle 进度环
+- 基于uniapp vue3 提供css3和canvas两种渲染方式的的环形进度条
+- Q群 1169785031
+
+## 使用
+- 导入插件后直接使用
+
+### 基础使用
+- 默认使用css实现的方式,在不支持css方案的手机会自动切为canvas
+
+```html
+<l-circle v-model:current="modelVale" :percent="target">
+	<text>{{modelVale}}%</text>
+</l-circle>
+```
+```js
+const target = ref(50)
+const modelVale = ref(0)
+```
+
+### canvas渲染
+- 也可以主动设置 `canvas` 使用canvas方式渲染
+```html
+<l-circle v-model:current="modelVale" :percent="target" canvas>
+	<text>{{modelVale}}%</text>
+</l-circle>
+```
+```js
+const target = ref(50)
+const modelVale = ref(0)
+```
+
+### 查看示例
+导入后直接使用这个标签查看演示效果,
+```html
+// 代码位于 uni_modules/lime-circle/compoents/lime-circle
+<lime-circle />
+```
+
+
+### 插件标签
+- 默认 l-circle 为 component
+- 默认 lime-circle 为 demo
+
+### 关于vue2的使用方式
+- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
+- 关键代码是: 在main.js中 在vue2部分加上这一段即可,官方是把它单独成了一个文件.
+
+```js
+// main.js vue2
+import Vue from 'vue'
+import VueCompositionAPI from '@vue/composition-api'
+Vue.use(VueCompositionAPI)
+```
+另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录
+```cmd
+// \HBuilderX\plugins\compile-typescript
+yarn add typescript -D
+- or - 
+npm install typescript -D
+```
+
+#### 使用
+```html
+<l-circle :current.sync="modelVale" :percent="target">
+	<text>{{modelVale}}%</text>
+</l-circle>
+<button @tap="onClick(20)">+</button>
+<button @tap="onClick(-20)">-</button>
+```
+```js
+export default {
+	data() {
+		return {
+			modelVale: 0,
+			target: 50
+		}
+	},
+	methods: {
+		onClick(number) {
+			this.target = Math.max(Math.min(100, this.target + number), 0)
+		}
+	}
+}
+```
+
+
+## API
+
+### Props
+
+| 参数                       | 说明                                                         | 类型             | 默认值       |
+| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
+| percent                   | 进度环目标值                                                    | <em>number</em>  | `0`        |
+| v-model:current           | 进度环当前值                                                    | <em>number</em>  | `-`        |
+| size                      | 进度环尺寸                                                     | <em>string</em>  | `120px`     |
+| lineCap           		| 进度条顶端形态 , 可选值 `butt` `round`                           | <em>string</em>  | `round`      |
+| strokeWidth           	| 进度条宽度                   									 | <em>number</em>  | `6`      |
+| strokeColor           	| 进度条颜色, 若为数组即为渐变色(渐变色值仅支持hex)           	| <em>string、string[]</em>  | `#2db7f5`  |
+| trailWidth             	| 轨道环线宽度          					                     	| <em>number</em>  | `6`  |
+| trailColor             	| 轨道环线颜色         					                     	| <em>string</em>  | `#eaeef2`  |
+| dashboard             	| 是否为仪表盘样式        					                     | <em>boolean</em>  | `false`  |
+| clockwise             	| 是否为顺时针      					                    		 | <em>boolean</em>  | `true`  |
+| duration             		| 变化过渡时间, ms      					                    	| <em>number</em>  | `300`  |
+| max             		    | 总长度   					                    	            | <em>number</em>  | `100`  |
+| canvas             		| 是否使用canvas渲染  					                    	| <em>boolean</em>  | `false`  |
+
+
+
+## 打赏
+
+如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。  
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

+ 12 - 0
uni_modules/lime-shared/addUnit/index.ts

xqd
@@ -0,0 +1,12 @@
+// @ts-nocheck
+import {isNumeric} from '../isNumeric'
+import {isDef} from '../isDef'
+/** 加单位 */
+export function addUnit(value?: string | number): string | undefined {
+  if (!isDef(value)) {
+    return undefined;
+  }
+
+  value = String(value);
+  return isNumeric(value) ? `${value}px` : value;
+}

+ 63 - 0
uni_modules/lime-shared/arrayBufferToFile/index.ts

xqd
@@ -0,0 +1,63 @@
+// @ts-nocheck
+import {platform} from '../platform'
+/**
+ * buffer转路径
+ * @param {Object} buffer
+ */
+// @ts-nocheck
+export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> {
+	return new Promise((resolve, reject) => {
+		// #ifdef MP
+		const fs = uni.getFileSystemManager()
+		//自定义文件名
+		if (!name && !format) {
+			reject(new Error('ERROR_NAME_PARSE'))
+		}
+		const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
+		let pre = platform()
+		const filePath = `${pre.env.USER_DATA_PATH}/${fileName}`
+		fs.writeFile({
+			filePath,
+			data: buffer, 
+			success() {
+				resolve(filePath)
+			},
+			fail(err) {
+				console.error(err)
+				reject(err)
+			}
+		})
+		// #endif
+
+		// #ifdef H5
+		const file = new File([buffer], name, {
+		     type: format,
+		});
+		resolve(file)
+		// #endif
+
+		// #ifdef APP-PLUS
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		const base64 = uni.arrayBufferToBase64(buffer)
+		bitmap.loadBase64Data(base64, () => {
+			if (!name && !format) {
+				reject(new Error('ERROR_NAME_PARSE'))
+			}
+			const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
+			const filePath = `_doc/uniapp_temp/${fileNmae}`
+			bitmap.save(filePath, {},
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				},
+				(error) => {
+					bitmap.clear()
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			reject(error)
+		})
+		// #endif
+	})
+}

+ 13 - 0
uni_modules/lime-shared/base64ToArrayBuffer/index.ts

xqd
@@ -0,0 +1,13 @@
+// @ts-nocheck
+// 未完成
+export function base64ToArrayBuffer(base64 : string) {
+	const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
+	if (!format) {
+		new Error('ERROR_BASE64SRC_PARSE')
+	}
+	if(uni.base64ToArrayBuffer) {
+		return uni.base64ToArrayBuffer(bodyData)
+	} else {
+		
+	}
+}

+ 76 - 0
uni_modules/lime-shared/base64ToPath/index.ts

xqd
@@ -0,0 +1,76 @@
+// @ts-nocheck
+import {platform} from '../platform'
+/**
+ * base64转路径
+ * @param {Object} base64
+ */
+export function base64ToPath(base64: string, filename?: string):Promise<string> {
+	const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
+	console.log('format', format)
+	return new Promise((resolve, reject) => {
+		// #ifdef MP
+		const fs = uni.getFileSystemManager()
+		//自定义文件名
+		if (!filename && !format) {
+			reject(new Error('ERROR_BASE64SRC_PARSE'))
+		}
+		// const time = new Date().getTime();
+		const name = filename || `${new Date().getTime()}.${format}`;
+		let pre = platform()
+		const filePath = `${pre.env.USER_DATA_PATH}/${name}`
+		fs.writeFile({
+			filePath,
+			data: base64.split(',')[1], 
+			encoding: 'base64',
+			success() {
+				resolve(filePath)
+			},
+			fail(err) {
+				console.error(err)
+				reject(err)
+			}
+		})
+		// #endif
+
+		// #ifdef H5
+		// mime类型
+		let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
+		//base64 解码
+		let byteString = atob(base64.split(',')[1]);
+		//创建缓冲数组
+		let arrayBuffer = new ArrayBuffer(byteString.length);
+		//创建视图
+		let intArray = new Uint8Array(arrayBuffer);
+		for (let i = 0; i < byteString.length; i++) {
+			intArray[i] = byteString.charCodeAt(i);
+		}
+		resolve(URL.createObjectURL(new Blob([intArray], {
+			type: mimeString
+		})))
+		// #endif
+
+		// #ifdef APP-PLUS
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		bitmap.loadBase64Data(base64, () => {
+			if (!filename && !format) {
+				reject(new Error('ERROR_BASE64SRC_PARSE'))
+			}
+			// const time = new Date().getTime();
+			const name = filename || `${new Date().getTime()}.${format}`;
+			const filePath = `_doc/uniapp_temp/${name}`
+			bitmap.save(filePath, {},
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				},
+				(error) => {
+					bitmap.clear()
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			reject(error)
+		})
+		// #endif
+	})
+}

+ 58 - 0
uni_modules/lime-shared/canIUseCanvas2d/index.ts

xqd
@@ -0,0 +1,58 @@
+// @ts-nocheck
+// #ifdef MP-ALIPAY
+interface My {
+	SDKVersion: string
+}
+declare var my: My
+// #endif
+
+function compareVersion(v1:string, v2:string) {
+  let a1 = v1.split('.');
+  let a2 = v2.split('.');
+  const len = Math.max(a1.length, a2.length);
+
+  while (a1.length < len) {
+    a1.push('0');
+  }
+  while (a2.length < len) {
+    a2.push('0');
+  }
+
+  for (let i = 0; i < len; i++) {
+    const num1 = parseInt(a1[i], 10);
+    const num2 = parseInt(a2[i], 10);
+
+    if (num1 > num2) {
+      return 1;
+    }
+    if (num1 < num2) {
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+function gte(version: string) {
+	let {SDKVersion} = uni.getSystemInfoSync();
+	// #ifdef MP-ALIPAY
+	SDKVersion = my.SDKVersion
+	// #endif
+	return compareVersion(SDKVersion, version) >= 0;
+}
+
+/** 环境是否支持canvas 2d */
+export function canIUseCanvas2d() {
+	// #ifdef MP-WEIXIN
+	return gte('2.9.0');
+	// #endif
+	// #ifdef MP-ALIPAY
+	return gte('2.7.0');
+	// #endif
+	// #ifdef MP-TOUTIAO
+	return gte('1.78.0');
+	// #endif
+	// #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO
+	return false
+	// #endif
+}

+ 24 - 0
uni_modules/lime-shared/changelog.md

xqd
@@ -0,0 +1,24 @@
+## 0.1.2(2023-07-17)
+- feat: 增加 `getClassStr`
+## 0.1.1(2023-07-06)
+- feat: 增加 `isNumeric`, 区别于 `isNumber`
+## 0.1.0(2023-06-30)
+- fix: `clamp`忘记导出了
+## 0.0.9(2023-06-27)
+- feat: 增加`arrayBufferToFile`
+## 0.0.8(2023-06-19)
+- feat: 增加`createAnimation`、`clamp`
+## 0.0.7(2023-06-08)
+- chore: 更新注释
+## 0.0.6(2023-06-08)
+- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖
+## 0.0.5(2023-06-03)
+- chore: 更新注释
+## 0.0.4(2023-05-22)
+- feat: 增加`range`,`exif`,`selectComponent`
+## 0.0.3(2023-05-08)
+- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
+## 0.0.2(2023-05-05)
+- chore: 更新文档
+## 0.0.1(2023-05-05)
+- 无

+ 11 - 0
uni_modules/lime-shared/clamp/index.ts

xqd
@@ -0,0 +1,11 @@
+// @ts-nocheck
+/**
+ * 夹在min和max之间的值,不能小于min,也不能大于max
+ * @参数 min number
+ * @参数 max number
+ * @参数 val number
+ * @返回 number
+ * */
+export function clamp(min:number, max:number, val:number):number {
+  return Math.max(min, Math.min(max, val))
+}

+ 50 - 0
uni_modules/lime-shared/cloneDeep/index.ts

xqd
@@ -0,0 +1,50 @@
+// @ts-nocheck
+/**
+ * 深拷贝
+ * @returns
+ */
+export function cloneDeep<T>(obj : any) : T {
+	if (obj === null) {
+		return null as unknown as T;
+	}
+	if (obj instanceof Set) {
+		return new Set([...obj]) as unknown as T;
+	}
+	if (obj instanceof Map) {
+		return new Map([...obj]) as unknown as T;
+	}
+	if (obj instanceof WeakMap) {
+		let weakMap = new WeakMap();
+		weakMap = obj;
+		return weakMap as unknown as T;
+	}
+	if (obj instanceof WeakSet) {
+		let weakSet = new WeakSet();
+		weakSet = obj;
+		return weakSet as unknown as T;
+	}
+	if (obj instanceof RegExp) {
+		return new RegExp(obj) as unknown as T;
+	}
+	if (typeof obj === 'undefined') {
+		return undefined as unknown as T;
+	}
+	if (Array.isArray(obj)) {
+		return obj.map(cloneDeep) as unknown as T;
+	}
+	if (obj instanceof Date) {
+		return new Date(obj.getTime()) as unknown as T;
+	}
+	if (typeof obj !== 'object') {
+		return obj;
+	}
+	const newObj : any = {};
+	for (const [key, value] of Object.entries(obj)) {
+		newObj[key] = cloneDeep(value);
+	}
+	const symbolkeys = Object.getOwnPropertySymbols(obj);
+	for (const key of symbolkeys) {
+		newObj[key] = cloneDeep(obj[key]);
+	}
+	return newObj;
+}

+ 11 - 0
uni_modules/lime-shared/closest/index.ts

xqd
@@ -0,0 +1,11 @@
+// @ts-nocheck
+/**
+ * 最近数值,目标值最近的数值
+ * @参数 arr number[] 
+ * @参数 target number
+ * */
+export function closest(arr: number[], target: number) {
+  return arr.reduce((pre, cur) =>
+    Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
+  );
+}

+ 149 - 0
uni_modules/lime-shared/createAnimation/index.ts

xqd
@@ -0,0 +1,149 @@
+// @ts-nocheck
+// nvue 需要在节点上设置ref或在export里传入
+// const animation = createAnimation({
+//   ref: this.$refs['xxx'],
+// 	 duration: 0,
+// 	 timingFunction: 'linear'
+// })
+// animation.opacity(1).translate(x, y).step({duration})
+// animation.export(ref)
+
+// 抹平nvue 与 uni.createAnimation的使用差距
+// 但是nvue动画太慢~~~无语
+
+
+
+
+// #ifdef APP-NVUE
+const nvueAnimation = uni.requireNativePlugin('animation')
+
+type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ'
+	| 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom'
+
+interface Styles {
+	[key : string] : any
+}
+	
+interface StepConfig {
+	duration?: number
+	timingFunction?: string
+	delay?: number
+	needLayout?: boolean
+	transformOrigin?: string
+}
+interface StepAnimate {
+	styles?: Styles
+	config?: StepConfig
+}
+interface StepAnimates {
+	[key: number]: StepAnimate
+}
+interface CreateAnimationOptions extends UniApp.CreateAnimationOptions {
+	ref?: string
+}
+
+type Callback = (time: number) => void
+const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
+	'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
+	'translateZ'
+]
+const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor']
+const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom']
+
+class LimeAnimation {
+	ref : any
+	context : any
+	options : UniApp.CreateAnimationOptions
+	// stack : any[] = []
+	next : number = 0
+	currentStepAnimates : StepAnimates = {}
+	duration : number = 0
+	constructor(options : CreateAnimationOptions) {
+		const {ref} = options
+		this.ref = ref
+		this.options = options
+	}
+	addAnimate(type : AnimationTypes, args: (string | number)[]) {
+		let aniObj = this.currentStepAnimates[this.next]
+		let stepAnimate:StepAnimate = {}
+		if (!aniObj) {
+			stepAnimate = {styles: {}, config: {}}
+		} else {
+			stepAnimate = aniObj
+		}
+
+		if (animateTypes1.includes(type)) {
+			if (!stepAnimate.styles.transform) {
+				stepAnimate.styles.transform = ''
+			}
+			let unit = ''
+			if (type === 'rotate') {
+				unit = 'deg'
+			}
+			stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) `
+		} else {
+			stepAnimate.styles[type] = `${args.join(',')}`
+		}
+		this.currentStepAnimates[this.next] = stepAnimate
+	}
+	animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) {
+		const el = ref || this.ref
+		if (!el) return
+		return new Promise((resolve) => {
+			const time = +new Date()
+			nvueAnimation.transition(el, {
+				styles,
+				...config
+			}, () => {
+				resolve(+new Date() - time)
+			})
+		})
+	}
+	nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) {
+		let obj = animates[step]
+		if (obj) {
+			let { styles, config } = obj
+			// this.duration += config.duration
+			this.animateRun(styles, config, ref).then((time: number) => {
+				step += 1
+				this.duration += time
+				this.nextAnimate(animates, step, ref, cb)
+			})
+		} else {
+			this.currentStepAnimates = {}
+			cb && cb(this.duration)
+		}
+	}
+	step(config:StepConfig = {}) {
+		this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
+		this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
+		this.next++
+		return this
+	}
+	export(ref: any, cb?: Callback) {
+		ref = ref || this.ref
+		if(!ref) return
+		this.duration = 0
+		this.next = 0
+		this.nextAnimate(this.currentStepAnimates, 0, ref, cb)
+		return null
+	}
+}
+
+
+animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
+	LimeAnimation.prototype[type] = function(...args: (string | number)[]) {
+		this.addAnimate(type, args)
+		return this
+	}
+})
+// #endif
+export function createAnimation(options : CreateAnimationOptions) {
+	// #ifndef APP-NVUE
+	// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
+	return uni.createAnimation({ ...options })
+	// #endif
+	// #ifdef APP-NVUE
+	return new LimeAnimation(options)
+	// #endif
+}

+ 61 - 0
uni_modules/lime-shared/createImage/index.ts

xqd
@@ -0,0 +1,61 @@
+// @ts-nocheck
+import {isBrowser} from '../isBrowser'
+class Image {
+	currentSrc: string | null = null
+	naturalHeight: number = 0
+	naturalWidth: number = 0
+	width: number = 0
+	height: number = 0
+	tagName: string = 'IMG'
+	path: string = ''
+	crossOrigin: string = ''
+	referrerPolicy: string = ''
+	onload: () => void = () => {}
+	onerror: () => void = () => {}
+	complete: boolean = false
+	constructor() {}
+	set src(src: string) {
+		console.log('src', src)
+		if(!src) {
+			return this.onerror()
+		}
+		src = src.replace(/^@\//,'/')
+		this.currentSrc = src
+		uni.getImageInfo({
+			src,
+			success: (res) => {
+				const localReg = /^\.|^\/(?=[^\/])/;
+				// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
+				res.path = localReg.test(src) ?  `/${res.path}` : res.path;
+				// #endif
+				this.complete = true
+				this.path = res.path
+				this.naturalWidth = this.width = res.width
+				this.naturalHeight = this.height = res.height
+				this.onload()
+			},
+			fail: () => {
+				this.onerror()
+			}
+		})
+	}
+	get src() {
+		return this.currentSrc
+	}
+}
+interface UniImage extends WechatMiniprogram.Image {
+	complete?: boolean
+	naturalHeight?: number
+	naturalWidth?: number
+}
+/** 创建用于 canvas 的 img */
+export function createImage(canvas?: any): HTMLImageElement | UniImage {
+	if(canvas && canvas.createImage) {
+		return (canvas as WechatMiniprogram.Canvas).createImage()
+	} else if(this.tagName == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){
+		return new Image()
+	} else if(isBrowser) {
+		return new window.Image()
+	}
+	return new Image()
+}

+ 18 - 0
uni_modules/lime-shared/debounce/index.ts

xqd
@@ -0,0 +1,18 @@
+// @ts-nocheck
+type Timeout = ReturnType<typeof setTimeout> | null;
+/**
+ * 防抖
+ * @param fn 回调函数
+ * @param wait 延迟时间
+ * @returns
+ */
+export function debounce(fn : (...args : any[]) => void, wait = 300) {
+	let timer : Timeout = null;
+	return function (this : any, ...args : any[]) {
+		if (timer) clearTimeout(timer);
+
+		timer = setTimeout(() => {
+			fn.apply(this, args);
+		}, wait);
+	};
+}

+ 1056 - 0
uni_modules/lime-shared/exif/index.ts

xqd
@@ -0,0 +1,1056 @@
+// @ts-nocheck
+import { base64ToArrayBuffer } from '../base64ToArrayBuffer';
+import { pathToBase64 } from '../pathToBase64';
+import { isBase64 } from '../isBase64';
+import { isString } from '../isString';
+
+interface File {
+	exifdata : any
+	iptcdata : any
+	xmpdata : any
+	src : string
+}
+class EXIF {
+	isXmpEnabled = false
+	debug = false
+	Tags = {
+		// version tags
+		0x9000: "ExifVersion", // EXIF version
+		0xA000: "FlashpixVersion", // Flashpix format version
+
+		// colorspace tags
+		0xA001: "ColorSpace", // Color space information tag
+
+		// image configuration
+		0xA002: "PixelXDimension", // Valid width of meaningful image
+		0xA003: "PixelYDimension", // Valid height of meaningful image
+		0x9101: "ComponentsConfiguration", // Information about channels
+		0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel
+
+		// user information
+		0x927C: "MakerNote", // Any desired information written by the manufacturer
+		0x9286: "UserComment", // Comments by user
+
+		// related file
+		0xA004: "RelatedSoundFile", // Name of related sound file
+
+		// date and time
+		0x9003: "DateTimeOriginal", // Date and time when the original image was generated
+		0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally
+		0x9290: "SubsecTime", // Fractions of seconds for DateTime
+		0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
+		0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
+
+		// picture-taking conditions
+		0x829A: "ExposureTime", // Exposure time (in seconds)
+		0x829D: "FNumber", // F number
+		0x8822: "ExposureProgram", // Exposure program
+		0x8824: "SpectralSensitivity", // Spectral sensitivity
+		0x8827: "ISOSpeedRatings", // ISO speed rating
+		0x8828: "OECF", // Optoelectric conversion factor
+		0x9201: "ShutterSpeedValue", // Shutter speed
+		0x9202: "ApertureValue", // Lens aperture
+		0x9203: "BrightnessValue", // Value of brightness
+		0x9204: "ExposureBias", // Exposure bias
+		0x9205: "MaxApertureValue", // Smallest F number of lens
+		0x9206: "SubjectDistance", // Distance to subject in meters
+		0x9207: "MeteringMode", // Metering mode
+		0x9208: "LightSource", // Kind of light source
+		0x9209: "Flash", // Flash status
+		0x9214: "SubjectArea", // Location and area of main subject
+		0x920A: "FocalLength", // Focal length of the lens in mm
+		0xA20B: "FlashEnergy", // Strobe energy in BCPS
+		0xA20C: "SpatialFrequencyResponse", //
+		0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
+		0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
+		0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
+		0xA214: "SubjectLocation", // Location of subject in image
+		0xA215: "ExposureIndex", // Exposure index selected on camera
+		0xA217: "SensingMethod", // Image sensor type
+		0xA300: "FileSource", // Image source (3 == DSC)
+		0xA301: "SceneType", // Scene type (1 == directly photographed)
+		0xA302: "CFAPattern", // Color filter array geometric pattern
+		0xA401: "CustomRendered", // Special processing
+		0xA402: "ExposureMode", // Exposure mode
+		0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual
+		0xA404: "DigitalZoomRation", // Digital zoom ratio
+		0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
+		0xA406: "SceneCaptureType", // Type of scene
+		0xA407: "GainControl", // Degree of overall image gain adjustment
+		0xA408: "Contrast", // Direction of contrast processing applied by camera
+		0xA409: "Saturation", // Direction of saturation processing applied by camera
+		0xA40A: "Sharpness", // Direction of sharpness processing applied by camera
+		0xA40B: "DeviceSettingDescription", //
+		0xA40C: "SubjectDistanceRange", // Distance to subject
+
+		// other tags
+		0xA005: "InteroperabilityIFDPointer",
+		0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image
+	}
+	TiffTags = {
+		0x0100: "ImageWidth",
+		0x0101: "ImageHeight",
+		0x8769: "ExifIFDPointer",
+		0x8825: "GPSInfoIFDPointer",
+		0xA005: "InteroperabilityIFDPointer",
+		0x0102: "BitsPerSample",
+		0x0103: "Compression",
+		0x0106: "PhotometricInterpretation",
+		0x0112: "Orientation",
+		0x0115: "SamplesPerPixel",
+		0x011C: "PlanarConfiguration",
+		0x0212: "YCbCrSubSampling",
+		0x0213: "YCbCrPositioning",
+		0x011A: "XResolution",
+		0x011B: "YResolution",
+		0x0128: "ResolutionUnit",
+		0x0111: "StripOffsets",
+		0x0116: "RowsPerStrip",
+		0x0117: "StripByteCounts",
+		0x0201: "JPEGInterchangeFormat",
+		0x0202: "JPEGInterchangeFormatLength",
+		0x012D: "TransferFunction",
+		0x013E: "WhitePoint",
+		0x013F: "PrimaryChromaticities",
+		0x0211: "YCbCrCoefficients",
+		0x0214: "ReferenceBlackWhite",
+		0x0132: "DateTime",
+		0x010E: "ImageDescription",
+		0x010F: "Make",
+		0x0110: "Model",
+		0x0131: "Software",
+		0x013B: "Artist",
+		0x8298: "Copyright"
+	}
+	GPSTags = {
+		0x0000: "GPSVersionID",
+		0x0001: "GPSLatitudeRef",
+		0x0002: "GPSLatitude",
+		0x0003: "GPSLongitudeRef",
+		0x0004: "GPSLongitude",
+		0x0005: "GPSAltitudeRef",
+		0x0006: "GPSAltitude",
+		0x0007: "GPSTimeStamp",
+		0x0008: "GPSSatellites",
+		0x0009: "GPSStatus",
+		0x000A: "GPSMeasureMode",
+		0x000B: "GPSDOP",
+		0x000C: "GPSSpeedRef",
+		0x000D: "GPSSpeed",
+		0x000E: "GPSTrackRef",
+		0x000F: "GPSTrack",
+		0x0010: "GPSImgDirectionRef",
+		0x0011: "GPSImgDirection",
+		0x0012: "GPSMapDatum",
+		0x0013: "GPSDestLatitudeRef",
+		0x0014: "GPSDestLatitude",
+		0x0015: "GPSDestLongitudeRef",
+		0x0016: "GPSDestLongitude",
+		0x0017: "GPSDestBearingRef",
+		0x0018: "GPSDestBearing",
+		0x0019: "GPSDestDistanceRef",
+		0x001A: "GPSDestDistance",
+		0x001B: "GPSProcessingMethod",
+		0x001C: "GPSAreaInformation",
+		0x001D: "GPSDateStamp",
+		0x001E: "GPSDifferential"
+	}
+	// EXIF 2.3 Spec
+	IFD1Tags = {
+		0x0100: "ImageWidth",
+		0x0101: "ImageHeight",
+		0x0102: "BitsPerSample",
+		0x0103: "Compression",
+		0x0106: "PhotometricInterpretation",
+		0x0111: "StripOffsets",
+		0x0112: "Orientation",
+		0x0115: "SamplesPerPixel",
+		0x0116: "RowsPerStrip",
+		0x0117: "StripByteCounts",
+		0x011A: "XResolution",
+		0x011B: "YResolution",
+		0x011C: "PlanarConfiguration",
+		0x0128: "ResolutionUnit",
+		0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
+		0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
+		0x0211: "YCbCrCoefficients",
+		0x0212: "YCbCrSubSampling",
+		0x0213: "YCbCrPositioning",
+		0x0214: "ReferenceBlackWhite"
+	}
+	StringValues = {
+		ExposureProgram: {
+			0: "Not defined",
+			1: "Manual",
+			2: "Normal program",
+			3: "Aperture priority",
+			4: "Shutter priority",
+			5: "Creative program",
+			6: "Action program",
+			7: "Portrait mode",
+			8: "Landscape mode"
+		},
+		MeteringMode: {
+			0: "Unknown",
+			1: "Average",
+			2: "CenterWeightedAverage",
+			3: "Spot",
+			4: "MultiSpot",
+			5: "Pattern",
+			6: "Partial",
+			255: "Other"
+		},
+		LightSource: {
+			0: "Unknown",
+			1: "Daylight",
+			2: "Fluorescent",
+			3: "Tungsten (incandescent light)",
+			4: "Flash",
+			9: "Fine weather",
+			10: "Cloudy weather",
+			11: "Shade",
+			12: "Daylight fluorescent (D 5700 - 7100K)",
+			13: "Day white fluorescent (N 4600 - 5400K)",
+			14: "Cool white fluorescent (W 3900 - 4500K)",
+			15: "White fluorescent (WW 3200 - 3700K)",
+			17: "Standard light A",
+			18: "Standard light B",
+			19: "Standard light C",
+			20: "D55",
+			21: "D65",
+			22: "D75",
+			23: "D50",
+			24: "ISO studio tungsten",
+			255: "Other"
+		},
+		Flash: {
+			0x0000: "Flash did not fire",
+			0x0001: "Flash fired",
+			0x0005: "Strobe return light not detected",
+			0x0007: "Strobe return light detected",
+			0x0009: "Flash fired, compulsory flash mode",
+			0x000D: "Flash fired, compulsory flash mode, return light not detected",
+			0x000F: "Flash fired, compulsory flash mode, return light detected",
+			0x0010: "Flash did not fire, compulsory flash mode",
+			0x0018: "Flash did not fire, auto mode",
+			0x0019: "Flash fired, auto mode",
+			0x001D: "Flash fired, auto mode, return light not detected",
+			0x001F: "Flash fired, auto mode, return light detected",
+			0x0020: "No flash function",
+			0x0041: "Flash fired, red-eye reduction mode",
+			0x0045: "Flash fired, red-eye reduction mode, return light not detected",
+			0x0047: "Flash fired, red-eye reduction mode, return light detected",
+			0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
+			0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
+			0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
+			0x0059: "Flash fired, auto mode, red-eye reduction mode",
+			0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
+			0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
+		},
+		SensingMethod: {
+			1: "Not defined",
+			2: "One-chip color area sensor",
+			3: "Two-chip color area sensor",
+			4: "Three-chip color area sensor",
+			5: "Color sequential area sensor",
+			7: "Trilinear sensor",
+			8: "Color sequential linear sensor"
+		},
+		SceneCaptureType: {
+			0: "Standard",
+			1: "Landscape",
+			2: "Portrait",
+			3: "Night scene"
+		},
+		SceneType: {
+			1: "Directly photographed"
+		},
+		CustomRendered: {
+			0: "Normal process",
+			1: "Custom process"
+		},
+		WhiteBalance: {
+			0: "Auto white balance",
+			1: "Manual white balance"
+		},
+		GainControl: {
+			0: "None",
+			1: "Low gain up",
+			2: "High gain up",
+			3: "Low gain down",
+			4: "High gain down"
+		},
+		Contrast: {
+			0: "Normal",
+			1: "Soft",
+			2: "Hard"
+		},
+		Saturation: {
+			0: "Normal",
+			1: "Low saturation",
+			2: "High saturation"
+		},
+		Sharpness: {
+			0: "Normal",
+			1: "Soft",
+			2: "Hard"
+		},
+		SubjectDistanceRange: {
+			0: "Unknown",
+			1: "Macro",
+			2: "Close view",
+			3: "Distant view"
+		},
+		FileSource: {
+			3: "DSC"
+		},
+
+		Components: {
+			0: "",
+			1: "Y",
+			2: "Cb",
+			3: "Cr",
+			4: "R",
+			5: "G",
+			6: "B"
+		}
+	}
+	enableXmp() {
+		this.isXmpEnabled = true
+	}
+	disableXmp() {
+		this.isXmpEnabled = false;
+	}
+	/**
+	 * 获取图片数据
+	 * @param img 图片地址
+	 * @param callback 回调 返回图片数据
+	 * */
+	getData(img : any, callback : Function) {
+		// if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete)
+		// 	return false;
+		let file : File = {
+			src: '',
+			exifdata: null,
+			iptcdata: null,
+			xmpdata: null,
+		}
+		if (isBase64(img)) {
+			file.src = img
+		} else if (img.path) {
+			file.src = img.path
+		} else if (isString(img)) {
+			file.src = img
+		} else {
+			return false;
+		}
+
+
+		if (!imageHasData(file)) {
+			getImageData(file, callback);
+		} else {
+			if (callback) {
+				callback.call(file);
+			}
+		}
+		return true;
+	}
+	/**
+	 * 获取图片tag
+	 * @param img 图片数据
+	 * @param tag tag 类型
+	 * */
+	getTag(img : File, tag : string) {
+		if (!imageHasData(img)) return;
+		return img.exifdata[tag];
+	}
+	getIptcTag(img : File, tag : string) {
+		if (!imageHasData(img)) return;
+		return img.iptcdata[tag];
+	}
+	getAllTags(img : File) {
+		if (!imageHasData(img)) return {};
+		let a,
+			data = img.exifdata,
+			tags = {};
+		for (a in data) {
+			if (data.hasOwnProperty(a)) {
+				tags[a] = data[a];
+			}
+		}
+		return tags;
+	}
+	getAllIptcTags(img : File) {
+		if (!imageHasData(img)) return {};
+		let a,
+			data = img.iptcdata,
+			tags = {};
+		for (a in data) {
+			if (data.hasOwnProperty(a)) {
+				tags[a] = data[a];
+			}
+		}
+		return tags;
+	}
+	pretty(img : File) {
+		if (!imageHasData(img)) return "";
+		let a,
+			data = img.exifdata,
+			strPretty = "";
+		for (a in data) {
+			if (data.hasOwnProperty(a)) {
+				if (typeof data[a] == "object") {
+					if (data[a] instanceof Number) {
+						strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a]
+							.denominator + "]\r\n";
+					} else {
+						strPretty += a + " : [" + data[a].length + " values]\r\n";
+					}
+				} else {
+					strPretty += a + " : " + data[a] + "\r\n";
+				}
+			}
+		}
+		return strPretty;
+	}
+	readFromBinaryFile(file: ArrayBuffer) {
+		return findEXIFinJPEG(file);
+	}
+}
+
+export const exif = new EXIF()
+// export function getData(img, callback) {
+// 	const exif = new EXIF()
+// 	exif.getData(img, callback)
+// }
+
+// export default {getData}
+const ExifTags = exif.Tags
+const TiffTags = exif.TiffTags
+const IFD1Tags = exif.IFD1Tags
+const GPSTags = exif.GPSTags
+const StringValues = exif.StringValues
+
+
+function imageHasData(img : File) : boolean {
+	return !!(img.exifdata);
+}
+
+function objectURLToBlob(url : string, callback : Function) {
+	try {
+		const http = new XMLHttpRequest();
+		http.open("GET", url, true);
+		http.responseType = "blob";
+		http.onload = function (e) {
+			if (this.status == 200 || this.status === 0) {
+				callback(this.response);
+			}
+		};
+		http.send();
+	} catch (e) {
+		console.warn(e)
+	}
+}
+
+
+function getImageData(img : File, callback : Function) {
+	function handleBinaryFile(binFile: ArrayBuffer) {
+		const data = findEXIFinJPEG(binFile);
+		img.exifdata = data ?? {};
+		const iptcdata = findIPTCinJPEG(binFile);
+		img.iptcdata = iptcdata ?? {};
+		if (exif.isXmpEnabled) {
+			const xmpdata = findXMPinJPEG(binFile);
+			img.xmpdata = xmpdata ?? {};
+		}
+		if (callback) {
+			callback.call(img);
+		}
+	}
+
+	if (img.src) {
+		if (/^data\:/i.test(img.src)) { // Data URI
+			// var arrayBuffer = base64ToArrayBuffer(img.src);
+			handleBinaryFile(base64ToArrayBuffer(img.src));
+
+		} else if (/^blob\:/i.test(img.src) && typeof FileReader !== 'undefined') { // Object URL
+			var fileReader = new FileReader();
+			fileReader.onload = function (e) {
+				handleBinaryFile(e.target.result);
+			};
+			objectURLToBlob(img.src, function (blob : Blob) {
+				fileReader.readAsArrayBuffer(blob);
+			});
+		} else if (typeof XMLHttpRequest !== 'undefined') {
+			var http = new XMLHttpRequest();
+			http.onload = function () {
+				if (this.status == 200 || this.status === 0) {
+					handleBinaryFile(http.response);
+				} else {
+					throw "Could not load image";
+				}
+				http = null;
+			};
+			http.open("GET", img.src, true);
+			http.responseType = "arraybuffer";
+			http.send(null);
+		} else {
+			pathToBase64(img.src).then(res => {
+				handleBinaryFile(base64ToArrayBuffer(res));
+			})
+		}
+	} else if (typeof FileReader !== 'undefined' && self.FileReader && (img instanceof self.Blob || img instanceof self.File)) {
+		var fileReader = new FileReader();
+		fileReader.onload = function (e : any) {
+			if (exif.debug) console.log("Got file of length " + e.target.result.byteLength);
+			handleBinaryFile(e.target.result);
+		};
+
+		fileReader.readAsArrayBuffer(img);
+	}
+}
+
+function findEXIFinJPEG(file: ArrayBuffer) {
+	const dataView = new DataView(file);
+
+	if (exif.debug) console.log("Got file of length " + file.byteLength);
+	if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+		if (exif.debug) console.log("Not a valid JPEG");
+		return false; // not a valid jpeg
+	}
+
+	let offset = 2,
+		length = file.byteLength,
+		marker;
+
+	while (offset < length) {
+		if (dataView.getUint8(offset) != 0xFF) {
+			if (exif.debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(
+				offset));
+			return false; // not a valid marker, something is wrong
+		}
+
+		marker = dataView.getUint8(offset + 1);
+		if (exif.debug) console.log(marker);
+
+		// we could implement handling for other markers here,
+		// but we're only looking for 0xFFE1 for EXIF data
+
+		if (marker == 225) {
+			if (exif.debug) console.log("Found 0xFFE1 marker");
+
+			return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
+
+			// offset += 2 + file.getShortAt(offset+2, true);
+
+		} else {
+			offset += 2 + dataView.getUint16(offset + 2);
+		}
+
+	}
+
+}
+
+function findIPTCinJPEG(file: ArrayBuffer) {
+	const dataView = new DataView(file);
+
+	if (exif.debug) console.log("Got file of length " + file.byteLength);
+	if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+		if (exif.debug) console.log("Not a valid JPEG");
+		return false; // not a valid jpeg
+	}
+
+	let offset = 2,
+		length = file.byteLength;
+
+
+	const isFieldSegmentStart = function (dataView, offset: number) {
+		return (
+			dataView.getUint8(offset) === 0x38 &&
+			dataView.getUint8(offset + 1) === 0x42 &&
+			dataView.getUint8(offset + 2) === 0x49 &&
+			dataView.getUint8(offset + 3) === 0x4D &&
+			dataView.getUint8(offset + 4) === 0x04 &&
+			dataView.getUint8(offset + 5) === 0x04
+		);
+	};
+
+	while (offset < length) {
+
+		if (isFieldSegmentStart(dataView, offset)) {
+
+			// Get the length of the name header (which is padded to an even number of bytes)
+			var nameHeaderLength = dataView.getUint8(offset + 7);
+			if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
+			// Check for pre photoshop 6 format
+			if (nameHeaderLength === 0) {
+				// Always 4
+				nameHeaderLength = 4;
+			}
+
+			var startOffset = offset + 8 + nameHeaderLength;
+			var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
+
+			return readIPTCData(file, startOffset, sectionLength);
+
+			break;
+
+		}
+
+
+		// Not the marker, continue searching
+		offset++;
+
+	}
+
+}
+
+const IptcFieldMap = {
+	0x78: 'caption',
+	0x6E: 'credit',
+	0x19: 'keywords',
+	0x37: 'dateCreated',
+	0x50: 'byline',
+	0x55: 'bylineTitle',
+	0x7A: 'captionWriter',
+	0x69: 'headline',
+	0x74: 'copyright',
+	0x0F: 'category'
+};
+
+function readIPTCData(file: ArrayBuffer, startOffset: number, sectionLength: number) {
+	const dataView = new DataView(file);
+	let data = {};
+	let fieldValue, fieldName, dataSize, segmentType, segmentSize;
+	let segmentStartPos = startOffset;
+	while (segmentStartPos < startOffset + sectionLength) {
+		if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) {
+			segmentType = dataView.getUint8(segmentStartPos + 2);
+			if (segmentType in IptcFieldMap) {
+				dataSize = dataView.getInt16(segmentStartPos + 3);
+				segmentSize = dataSize + 5;
+				fieldName = IptcFieldMap[segmentType];
+				fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize);
+				// Check if we already stored a value with this name
+				if (data.hasOwnProperty(fieldName)) {
+					// Value already stored with this name, create multivalue field
+					if (data[fieldName] instanceof Array) {
+						data[fieldName].push(fieldValue);
+					} else {
+						data[fieldName] = [data[fieldName], fieldValue];
+					}
+				} else {
+					data[fieldName] = fieldValue;
+				}
+			}
+
+		}
+		segmentStartPos++;
+	}
+	return data;
+}
+
+function readTags(file: DataView, tiffStart: number, dirStart: number, strings: any[], bigEnd: number) {
+	let entries = file.getUint16(dirStart, !bigEnd),
+		tags = {},
+		entryOffset, tag;
+
+	for (let i = 0; i < entries; i++) {
+		entryOffset = dirStart + i * 12 + 2;
+		tag = strings[file.getUint16(entryOffset, !bigEnd)];
+		if (!tag && exif.debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
+		tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
+	}
+	return tags;
+}
+
+function readTagValue(file: DataView, entryOffset: number, tiffStart: number, dirStart: number, bigEnd: number) {
+	let type = file.getUint16(entryOffset + 2, !bigEnd),
+		numValues = file.getUint32(entryOffset + 4, !bigEnd),
+		valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart,
+		offset,
+		vals, val, n,
+		numerator, denominator;
+
+	switch (type) {
+		case 1: // byte, 8-bit unsigned int
+		case 7: // undefined, 8-bit byte, value depending on field
+			if (numValues == 1) {
+				return file.getUint8(entryOffset + 8, !bigEnd);
+			} else {
+				offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					vals[n] = file.getUint8(offset + n);
+				}
+				return vals;
+			}
+
+		case 2: // ascii, 8-bit byte
+			offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+			return getStringFromDB(file, offset, numValues - 1);
+
+		case 3: // short, 16 bit int
+			if (numValues == 1) {
+				return file.getUint16(entryOffset + 8, !bigEnd);
+			} else {
+				offset = numValues > 2 ? valueOffset : (entryOffset + 8);
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					vals[n] = file.getUint16(offset + 2 * n, !bigEnd);
+				}
+				return vals;
+			}
+
+		case 4: // long, 32 bit int
+			if (numValues == 1) {
+				return file.getUint32(entryOffset + 8, !bigEnd);
+			} else {
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd);
+				}
+				return vals;
+			}
+
+		case 5: // rational = two long values, first is numerator, second is denominator
+			if (numValues == 1) {
+				numerator = file.getUint32(valueOffset, !bigEnd);
+				denominator = file.getUint32(valueOffset + 4, !bigEnd);
+				val = new Number(numerator / denominator);
+				val.numerator = numerator;
+				val.denominator = denominator;
+				return val;
+			} else {
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					numerator = file.getUint32(valueOffset + 8 * n, !bigEnd);
+					denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd);
+					vals[n] = new Number(numerator / denominator);
+					vals[n].numerator = numerator;
+					vals[n].denominator = denominator;
+				}
+				return vals;
+			}
+
+		case 9: // slong, 32 bit signed int
+			if (numValues == 1) {
+				return file.getInt32(entryOffset + 8, !bigEnd);
+			} else {
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd);
+				}
+				return vals;
+			}
+
+		case 10: // signed rational, two slongs, first is numerator, second is denominator
+			if (numValues == 1) {
+				return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd);
+			} else {
+				vals = [];
+				for (n = 0; n < numValues; n++) {
+					vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 *
+						n, !bigEnd);
+				}
+				return vals;
+			}
+	}
+}
+/**
+	 * Given an IFD (Image File Directory) start offset
+	 * returns an offset to next IFD or 0 if it's the last IFD.
+	 */
+function getNextIFDOffset(dataView: DataView, dirStart: number, bigEnd: number) {
+	//the first 2bytes means the number of directory entries contains in this IFD
+	var entries = dataView.getUint16(dirStart, !bigEnd);
+
+	// After last directory entry, there is a 4bytes of data,
+	// it means an offset to next IFD.
+	// If its value is '0x00000000', it means this is the last IFD and there is no linked IFD.
+
+	return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long
+}
+
+function readThumbnailImage(dataView: DataView, tiffStart: number, firstIFDOffset: number, bigEnd: number) {
+	// get the IFD1 offset
+	const IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart + firstIFDOffset, bigEnd);
+
+	if (!IFD1OffsetPointer) {
+		// console.log('******** IFD1Offset is empty, image thumb not found ********');
+		return {};
+	} else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen
+		// console.log('******** IFD1Offset is outside the bounds of the DataView ********');
+		return {};
+	}
+	// console.log('*******  thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
+
+	let thumbTags : any = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd)
+
+	// EXIF 2.3 specification for JPEG format thumbnail
+
+	// If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
+	// Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
+	// by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
+	// Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
+	// JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
+
+	if (thumbTags['Compression'] && typeof Blob !== 'undefined') {
+		// console.log('Thumbnail image found!');
+
+		switch (thumbTags['Compression']) {
+			case 6:
+				// console.log('Thumbnail image format is JPEG');
+				if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) {
+					// extract the thumbnail
+					var tOffset = tiffStart + thumbTags.JpegIFOffset;
+					var tLength = thumbTags.JpegIFByteCount;
+					thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], {
+						type: 'image/jpeg'
+					});
+				}
+				break;
+
+			case 1:
+				console.log("Thumbnail image format is TIFF, which is not implemented.");
+				break;
+			default:
+				console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']);
+		}
+	} else if (thumbTags['PhotometricInterpretation'] == 2) {
+		console.log("Thumbnail image format is RGB, which is not implemented.");
+	}
+	return thumbTags;
+}
+
+function getStringFromDB(buffer: DataView, start: number, length: number) {
+	let outstr = "";
+	for (let n = start; n < start + length; n++) {
+		outstr += String.fromCharCode(buffer.getUint8(n));
+	}
+	return outstr;
+}
+
+function readEXIFData(file: DataView, start: number) {
+	if (getStringFromDB(file, start, 4) != "Exif") {
+		if (exif.debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
+		return false;
+	}
+
+	let bigEnd,
+		tags, tag,
+		exifData, gpsData,
+		tiffOffset = start + 6;
+
+	// test for TIFF validity and endianness
+	if (file.getUint16(tiffOffset) == 0x4949) {
+		bigEnd = false;
+	} else if (file.getUint16(tiffOffset) == 0x4D4D) {
+		bigEnd = true;
+	} else {
+		if (exif.debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
+		return false;
+	}
+
+	if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) {
+		if (exif.debug) console.log("Not valid TIFF data! (no 0x002A)");
+		return false;
+	}
+
+	const firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
+
+	if (firstIFDOffset < 0x00000008) {
+		if (exif.debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4,
+			!bigEnd));
+		return false;
+	}
+
+	tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
+
+	if (tags.ExifIFDPointer) {
+		exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
+		for (tag in exifData) {
+			switch (tag) {
+				case "LightSource":
+				case "Flash":
+				case "MeteringMode":
+				case "ExposureProgram":
+				case "SensingMethod":
+				case "SceneCaptureType":
+				case "SceneType":
+				case "CustomRendered":
+				case "WhiteBalance":
+				case "GainControl":
+				case "Contrast":
+				case "Saturation":
+				case "Sharpness":
+				case "SubjectDistanceRange":
+				case "FileSource":
+					exifData[tag] = StringValues[tag][exifData[tag]];
+					break;
+
+				case "ExifVersion":
+				case "FlashpixVersion":
+					exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2],
+						exifData[tag][3]);
+					break;
+
+				case "ComponentsConfiguration":
+					exifData[tag] =
+						StringValues.Components[exifData[tag][0]] +
+						StringValues.Components[exifData[tag][1]] +
+						StringValues.Components[exifData[tag][2]] +
+						StringValues.Components[exifData[tag][3]];
+					break;
+			}
+			tags[tag] = exifData[tag];
+		}
+	}
+
+	if (tags.GPSInfoIFDPointer) {
+		gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
+		for (tag in gpsData) {
+			switch (tag) {
+				case "GPSVersionID":
+					gpsData[tag] = gpsData[tag][0] +
+						"." + gpsData[tag][1] +
+						"." + gpsData[tag][2] +
+						"." + gpsData[tag][3];
+					break;
+			}
+			tags[tag] = gpsData[tag];
+		}
+	}
+
+	// extract thumbnail
+	tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd);
+
+	return tags;
+}
+
+function findXMPinJPEG(file: ArrayBuffer) {
+
+	if (!('DOMParser' in self)) {
+		// console.warn('XML parsing not supported without DOMParser');
+		return;
+	}
+	const dataView = new DataView(file);
+
+	if (exif.debug) console.log("Got file of length " + file.byteLength);
+	if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+		if (exif.debug) console.log("Not a valid JPEG");
+		return false; // not a valid jpeg
+	}
+
+	let offset = 2,
+		length = file.byteLength,
+		dom = new DOMParser();
+
+	while (offset < (length - 4)) {
+		if (getStringFromDB(dataView, offset, 4) == "http") {
+			const startOffset = offset - 1;
+			const sectionLength = dataView.getUint16(offset - 2) - 1;
+			let xmpString = getStringFromDB(dataView, startOffset, sectionLength)
+			const xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8;
+			xmpString = xmpString.substring(xmpString.indexOf('<x:xmpmeta'), xmpEndIndex);
+
+			const indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10
+			//Many custom written programs embed xmp/xml without any namespace. Following are some of them.
+			//Without these namespaces, XML is thought to be invalid by parsers
+			xmpString = xmpString.slice(0, indexOfXmp) +
+				'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" ' +
+				'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
+				'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" ' +
+				'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" ' +
+				'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" ' +
+				'xmlns:exif="http://ns.adobe.com/exif/1.0/" ' +
+				'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" ' +
+				'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" ' +
+				'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" ' +
+				'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" ' +
+				'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" ' +
+				xmpString.slice(indexOfXmp)
+
+			var domDocument = dom.parseFromString(xmpString, 'text/xml');
+			return xml2Object(domDocument);
+		} else {
+			offset++;
+		}
+	}
+}
+
+
+function xml2json(xml: any) {
+	var json = {};
+
+	if (xml.nodeType == 1) { // element node
+		if (xml.attributes.length > 0) {
+			json['@attributes'] = {};
+			for (var j = 0; j < xml.attributes.length; j++) {
+				var attribute = xml.attributes.item(j);
+				json['@attributes'][attribute.nodeName] = attribute.nodeValue;
+			}
+		}
+	} else if (xml.nodeType == 3) { // text node
+		return xml.nodeValue;
+	}
+
+	// deal with children
+	if (xml.hasChildNodes()) {
+		for (var i = 0; i < xml.childNodes.length; i++) {
+			var child = xml.childNodes.item(i);
+			var nodeName = child.nodeName;
+			if (json[nodeName] == null) {
+				json[nodeName] = xml2json(child);
+			} else {
+				if (json[nodeName].push == null) {
+					var old = json[nodeName];
+					json[nodeName] = [];
+					json[nodeName].push(old);
+				}
+				json[nodeName].push(xml2json(child));
+			}
+		}
+	}
+
+	return json;
+}
+
+function xml2Object(xml: any) {
+	try {
+		var obj = {};
+		if (xml.children.length > 0) {
+			for (var i = 0; i < xml.children.length; i++) {
+				var item = xml.children.item(i);
+				var attributes = item.attributes;
+				for (var idx in attributes) {
+					var itemAtt = attributes[idx];
+					var dataKey = itemAtt.nodeName;
+					var dataValue = itemAtt.nodeValue;
+
+					if (dataKey !== undefined) {
+						obj[dataKey] = dataValue;
+					}
+				}
+				var nodeName = item.nodeName;
+
+				if (typeof (obj[nodeName]) == "undefined") {
+					obj[nodeName] = xml2json(item);
+				} else {
+					if (typeof (obj[nodeName].push) == "undefined") {
+						var old = obj[nodeName];
+
+						obj[nodeName] = [];
+						obj[nodeName].push(old);
+					}
+					obj[nodeName].push(xml2json(item));
+				}
+			}
+		} else {
+			obj = xml.textContent;
+		}
+		return obj;
+	} catch (e) {
+		console.log(e.message);
+	}
+}

+ 13 - 0
uni_modules/lime-shared/fillZero/index.ts

xqd
@@ -0,0 +1,13 @@
+// @ts-nocheck
+/**
+ * 补0
+ * @param number
+ * @returns
+ */
+
+export function fillZero(number: number, length:number = 2): string {
+	// if(isMillieconds) {
+	// 	return `${number}`.padStart(3, '0')
+	// }
+	return `${number}`.padStart(length, '0')
+}

+ 13 - 0
uni_modules/lime-shared/getClassStr/index.ts

xqd
@@ -0,0 +1,13 @@
+// @ts-nocheck
+/**
+ * 把 class 对象转成字符串
+ */
+export function getClassStr<T>(obj: T): string {
+  let classNames: string[] = [];
+  for (let key in obj) {
+    if ((obj as any).hasOwnProperty(key) && obj[key]) {
+      classNames.push(key);
+    }
+  }
+  return classNames.join(' ');
+}

+ 6 - 0
uni_modules/lime-shared/getCurrentPage/index.ts

xqd
@@ -0,0 +1,6 @@
+// @ts-nocheck
+/** 获取当前页 */
+export const getCurrentPage = () => {
+  const pages = getCurrentPages();
+  return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
+};

+ 14 - 0
uni_modules/lime-shared/getLocalFilePath/index.ts

xqd
@@ -0,0 +1,14 @@
+// @ts-nocheck
+export const getLocalFilePath = (path: string) => {
+	if(typeof plus == 'undefined') return path
+	if(/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path
+	if (/^\//.test(path)) {
+		const localFilePath = plus.io.convertAbsoluteFileSystem(path)
+		if (localFilePath !== path) {
+			return localFilePath
+		} else {
+			path = path.slice(1)
+		}
+	}
+	return '_www/' + path
+}

+ 71 - 0
uni_modules/lime-shared/getRect/index.ts

xqd
@@ -0,0 +1,71 @@
+// @ts-nocheck
+
+// #ifdef APP-NVUE
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+interface RectOptions {
+	/**
+	* 上下文
+	*/
+	context ?: any //ComponentInternalInstance
+	/**
+	* 所有节点 nvue不支持
+	*/
+	needAll ?: Boolean,
+	nodes ?: UniNamespace.NodesRef
+	type ?: keyof UniNamespace.NodesRef
+
+}
+/** 获取节点信息 */
+export function getRect(selector : string, options : RectOptions = {}) {
+	// #ifndef APP-NVUE
+	const typeDefault = 'boundingClientRect'
+	let { context, needAll, type = typeDefault } = options
+	// #endif
+
+	// #ifdef MP || VUE2
+	if (context.proxy) context = context.proxy
+	// #endif
+
+	return new Promise<any>((resolve, reject) => {
+		// #ifndef APP-NVUE
+		const dom = uni.createSelectorQuery().in(context)[needAll ? 'selectAll' : 'select'](selector);
+		const result = (rect) => {
+			if (rect) {
+				resolve(rect)
+			} else {
+				reject('no rect')
+			}
+		}
+		if (type == typeDefault) {
+			dom[type](result).exec()
+		} else {
+			dom[type]({
+				node: true,
+				size: true,
+				rect: true
+			}, result).exec()
+		}
+		// #endif
+		// #ifdef APP-NVUE
+		let { context } = options
+		if (/#|\./.test(selector) && context.refs) {
+			selector = selector.replace(/#|\./, '')
+			if (context.refs[selector]) {
+				selector = context.refs[selector]
+				if(Array.isArray(selector)) {
+					selector = selector[0]
+				}
+			}
+		}
+		dom.getComponentRect(selector, (res) => {
+			if (res.size) {
+				resolve(res.size)
+			} else {
+				reject('no rect')
+			}
+		})
+		// #endif
+	});
+};

+ 15 - 0
uni_modules/lime-shared/getStyleStr/index.ts

xqd
@@ -0,0 +1,15 @@
+// @ts-nocheck
+interface CSSProperties {
+	[key: string]: string | number
+}
+/** converting camel-cased strings to be lowercase and link it with Separato */
+export function toLowercaseSeparator(key: string) {
+  return key.replace(/([A-Z])/g, '-$1').toLowerCase();
+}
+
+export function getStyleStr(style: CSSProperties): string {
+  return Object.keys(style).filter(key => style[key] !== undefined && style[key] !== null && style[key] !== '')
+    .map((key: string) => `${toLowercaseSeparator(key)}: ${style[key]};`)
+    .join(' ');
+}
+

+ 8 - 0
uni_modules/lime-shared/hasOwn/index.ts

xqd
@@ -0,0 +1,8 @@
+// @ts-nocheck
+const hasOwnProperty = Object.prototype.hasOwnProperty
+/**
+ * 检查对象是否具有该属性
+ */
+export function hasOwn(obj: Object | Array<any>, key: string): boolean {
+  return hasOwnProperty.call(obj, key)
+}

+ 43 - 0
uni_modules/lime-shared/index.ts

xqd
@@ -0,0 +1,43 @@
+// @ts-nocheck
+// validator
+export * from './isString'
+export * from './isNumber'
+export * from './isNumeric'
+export * from './isDef'
+export * from './isFunction'
+export * from './isObject'
+export * from './isPromise'
+export * from './isBase64'
+
+export * from './hasOwn'
+
+// 单位转换
+export * from './addUnit'
+export * from './unitConvert'
+export * from './toNumber'
+
+export * from './random'
+export * from './range'
+export * from './fillZero'
+
+// image 
+export * from './base64ToPath'
+export * from './pathToBase64'
+export * from './exif'
+
+// canvas
+export * from './canIUseCanvas2d'
+
+// page
+export * from './getCurrentPage'
+
+// dom
+export * from './getRect'
+export * from './selectComponent'
+export * from './createAnimation'
+
+// delay
+export * from './sleep'
+export * from './debounce'
+export * from './throttle'
+

+ 5 - 0
uni_modules/lime-shared/isBase64/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**
+ * @param path base64
+ */
+export const isBase64 = (path: string) => /^data:image\/(\w+);base64/.test(path)

+ 2 - 0
uni_modules/lime-shared/isBrowser/index.ts

xqd
@@ -0,0 +1,2 @@
+// @ts-nocheck
+export const isBrowser = typeof window !== 'undefined';

+ 5 - 0
uni_modules/lime-shared/isDef/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**是否定义*/
+export function isDef(value: unknown): boolean {
+  return value !== undefined && value !== null;
+}

+ 4 - 0
uni_modules/lime-shared/isFunction/index.ts

xqd
@@ -0,0 +1,4 @@
+// @ts-nocheck
+/** 是否为函数 */
+export const isFunction = (val: unknown): val is Function =>
+  typeof val === 'function'

+ 5 - 0
uni_modules/lime-shared/isNumber/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**判断是否为数字类型*/
+export function isNumber(value: number|string) {
+  return typeof value === 'number' ///^(-)?\d+(\.\d+)?$/.test(value);
+}

+ 5 - 0
uni_modules/lime-shared/isNumeric/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**判断字符串是否表示一个数值*/
+export function isNumeric(value: string | number) {
+	return /^(-)?\d+(\.\d+)?$/.test(value);
+}

+ 4 - 0
uni_modules/lime-shared/isObject/index.ts

xqd
@@ -0,0 +1,4 @@
+// @ts-nocheck
+/** 是否为对象 */
+export const isObject = (val: unknown): val is Record<any, any> =>
+  val !== null && typeof val === 'object'

+ 6 - 0
uni_modules/lime-shared/isPromise/index.ts

xqd
@@ -0,0 +1,6 @@
+// @ts-nocheck
+import {isFunction} from '../isFunction'
+import {isObject} from '../isObject'
+export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
+  return isObject(val) && isFunction(val.then) && isFunction(val.catch)
+}

+ 5 - 0
uni_modules/lime-shared/isString/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**
+ * 判断参数是否为字符串
+ */
+export const isString = (val: unknown): val is string => typeof val === 'string'

+ 83 - 0
uni_modules/lime-shared/package.json

xqd
@@ -0,0 +1,83 @@
+{
+  "id": "lime-shared",
+  "displayName": "lime-shared",
+  "version": "0.1.2",
+  "description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等",
+  "keywords": [
+    "lime-shared",
+    "exif",
+    "selectComponent"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "sdk-js",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 64 - 0
uni_modules/lime-shared/pathToBase64/index.ts

xqd
@@ -0,0 +1,64 @@
+// @ts-nocheck
+
+// #ifdef APP-PLUS
+import {getLocalFilePath} from '../getLocalFilePath'
+// #endif
+
+/**
+ * 路径转base64
+ * @param {Object} string
+ */		
+export function pathToBase64(path: string):Promise<string> {
+	if (/^data:/.test(path)) return path
+	return new Promise((resolve, reject) => {
+		// #ifdef H5
+		let image = new Image();
+		image.setAttribute("crossOrigin", 'Anonymous');
+		image.onload = function() {
+			let canvas = document.createElement('canvas');
+			canvas.width = this.naturalWidth;
+			canvas.height = this.naturalHeight;
+			canvas.getContext('2d').drawImage(image, 0, 0);
+			let result = canvas.toDataURL('image/png')
+			resolve(result);
+			canvas.height = canvas.width = 0
+		}
+		image.src = path + '?v=' + Math.random()
+		image.onerror = (error) => {
+			reject(error);
+		};
+		// #endif
+
+		// #ifdef MP
+		if (uni.canIUse('getFileSystemManager')) {
+			uni.getFileSystemManager().readFile({
+				filePath: path,
+				encoding: 'base64',
+				success: (res) => {
+					resolve('data:image/png;base64,' + res.data)
+				},
+				fail: (error) => {
+					console.error({error, path})
+					reject(error)
+				}
+			})
+		}
+		// #endif
+
+		// #ifdef APP-PLUS
+		plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
+			entry.file((file: any) => {
+				const fileReader = new plus.io.FileReader()
+				fileReader.onload = (data) => {
+					resolve(data.target.result)
+				}
+				fileReader.onerror = (error) => {
+					console.error({error, path})
+					reject(error)
+				}
+				fileReader.readAsDataURL(file)
+			}, reject)
+		}, reject)
+		// #endif
+	})
+}

+ 2320 - 0
uni_modules/lime-shared/piexif/index.ts

xqd
@@ -0,0 +1,2320 @@
+// @ts-nocheck
+// 源于piexifjs
+import { cloneDeep } from '../cloneDeep'
+import { isString } from '../isString'
+const TAGS = {
+	'Image': {
+		11: {
+			'name': 'ProcessingSoftware',
+			'type': 'Ascii'
+		},
+		254: {
+			'name': 'NewSubfileType',
+			'type': 'Long'
+		},
+		255: {
+			'name': 'SubfileType',
+			'type': 'Short'
+		},
+		256: {
+			'name': 'ImageWidth',
+			'type': 'Long'
+		},
+		257: {
+			'name': 'ImageLength',
+			'type': 'Long'
+		},
+		258: {
+			'name': 'BitsPerSample',
+			'type': 'Short'
+		},
+		259: {
+			'name': 'Compression',
+			'type': 'Short'
+		},
+		262: {
+			'name': 'PhotometricInterpretation',
+			'type': 'Short'
+		},
+		263: {
+			'name': 'Threshholding',
+			'type': 'Short'
+		},
+		264: {
+			'name': 'CellWidth',
+			'type': 'Short'
+		},
+		265: {
+			'name': 'CellLength',
+			'type': 'Short'
+		},
+		266: {
+			'name': 'FillOrder',
+			'type': 'Short'
+		},
+		269: {
+			'name': 'DocumentName',
+			'type': 'Ascii'
+		},
+		270: {
+			'name': 'ImageDescription',
+			'type': 'Ascii'
+		},
+		271: {
+			'name': 'Make',
+			'type': 'Ascii'
+		},
+		272: {
+			'name': 'Model',
+			'type': 'Ascii'
+		},
+		273: {
+			'name': 'StripOffsets',
+			'type': 'Long'
+		},
+		274: {
+			'name': 'Orientation',
+			'type': 'Short'
+		},
+		277: {
+			'name': 'SamplesPerPixel',
+			'type': 'Short'
+		},
+		278: {
+			'name': 'RowsPerStrip',
+			'type': 'Long'
+		},
+		279: {
+			'name': 'StripByteCounts',
+			'type': 'Long'
+		},
+		282: {
+			'name': 'XResolution',
+			'type': 'Rational'
+		},
+		283: {
+			'name': 'YResolution',
+			'type': 'Rational'
+		},
+		284: {
+			'name': 'PlanarConfiguration',
+			'type': 'Short'
+		},
+		290: {
+			'name': 'GrayResponseUnit',
+			'type': 'Short'
+		},
+		291: {
+			'name': 'GrayResponseCurve',
+			'type': 'Short'
+		},
+		292: {
+			'name': 'T4Options',
+			'type': 'Long'
+		},
+		293: {
+			'name': 'T6Options',
+			'type': 'Long'
+		},
+		296: {
+			'name': 'ResolutionUnit',
+			'type': 'Short'
+		},
+		301: {
+			'name': 'TransferFunction',
+			'type': 'Short'
+		},
+		305: {
+			'name': 'Software',
+			'type': 'Ascii'
+		},
+		306: {
+			'name': 'DateTime',
+			'type': 'Ascii'
+		},
+		315: {
+			'name': 'Artist',
+			'type': 'Ascii'
+		},
+		316: {
+			'name': 'HostComputer',
+			'type': 'Ascii'
+		},
+		317: {
+			'name': 'Predictor',
+			'type': 'Short'
+		},
+		318: {
+			'name': 'WhitePoint',
+			'type': 'Rational'
+		},
+		319: {
+			'name': 'PrimaryChromaticities',
+			'type': 'Rational'
+		},
+		320: {
+			'name': 'ColorMap',
+			'type': 'Short'
+		},
+		321: {
+			'name': 'HalftoneHints',
+			'type': 'Short'
+		},
+		322: {
+			'name': 'TileWidth',
+			'type': 'Short'
+		},
+		323: {
+			'name': 'TileLength',
+			'type': 'Short'
+		},
+		324: {
+			'name': 'TileOffsets',
+			'type': 'Short'
+		},
+		325: {
+			'name': 'TileByteCounts',
+			'type': 'Short'
+		},
+		330: {
+			'name': 'SubIFDs',
+			'type': 'Long'
+		},
+		332: {
+			'name': 'InkSet',
+			'type': 'Short'
+		},
+		333: {
+			'name': 'InkNames',
+			'type': 'Ascii'
+		},
+		334: {
+			'name': 'NumberOfInks',
+			'type': 'Short'
+		},
+		336: {
+			'name': 'DotRange',
+			'type': 'Byte'
+		},
+		337: {
+			'name': 'TargetPrinter',
+			'type': 'Ascii'
+		},
+		338: {
+			'name': 'ExtraSamples',
+			'type': 'Short'
+		},
+		339: {
+			'name': 'SampleFormat',
+			'type': 'Short'
+		},
+		340: {
+			'name': 'SMinSampleValue',
+			'type': 'Short'
+		},
+		341: {
+			'name': 'SMaxSampleValue',
+			'type': 'Short'
+		},
+		342: {
+			'name': 'TransferRange',
+			'type': 'Short'
+		},
+		343: {
+			'name': 'ClipPath',
+			'type': 'Byte'
+		},
+		344: {
+			'name': 'XClipPathUnits',
+			'type': 'Long'
+		},
+		345: {
+			'name': 'YClipPathUnits',
+			'type': 'Long'
+		},
+		346: {
+			'name': 'Indexed',
+			'type': 'Short'
+		},
+		347: {
+			'name': 'JPEGTables',
+			'type': 'Undefined'
+		},
+		351: {
+			'name': 'OPIProxy',
+			'type': 'Short'
+		},
+		512: {
+			'name': 'JPEGProc',
+			'type': 'Long'
+		},
+		513: {
+			'name': 'JPEGInterchangeFormat',
+			'type': 'Long'
+		},
+		514: {
+			'name': 'JPEGInterchangeFormatLength',
+			'type': 'Long'
+		},
+		515: {
+			'name': 'JPEGRestartInterval',
+			'type': 'Short'
+		},
+		517: {
+			'name': 'JPEGLosslessPredictors',
+			'type': 'Short'
+		},
+		518: {
+			'name': 'JPEGPointTransforms',
+			'type': 'Short'
+		},
+		519: {
+			'name': 'JPEGQTables',
+			'type': 'Long'
+		},
+		520: {
+			'name': 'JPEGDCTables',
+			'type': 'Long'
+		},
+		521: {
+			'name': 'JPEGACTables',
+			'type': 'Long'
+		},
+		529: {
+			'name': 'YCbCrCoefficients',
+			'type': 'Rational'
+		},
+		530: {
+			'name': 'YCbCrSubSampling',
+			'type': 'Short'
+		},
+		531: {
+			'name': 'YCbCrPositioning',
+			'type': 'Short'
+		},
+		532: {
+			'name': 'ReferenceBlackWhite',
+			'type': 'Rational'
+		},
+		700: {
+			'name': 'XMLPacket',
+			'type': 'Byte'
+		},
+		18246: {
+			'name': 'Rating',
+			'type': 'Short'
+		},
+		18249: {
+			'name': 'RatingPercent',
+			'type': 'Short'
+		},
+		32781: {
+			'name': 'ImageID',
+			'type': 'Ascii'
+		},
+		33421: {
+			'name': 'CFARepeatPatternDim',
+			'type': 'Short'
+		},
+		33422: {
+			'name': 'CFAPattern',
+			'type': 'Byte'
+		},
+		33423: {
+			'name': 'BatteryLevel',
+			'type': 'Rational'
+		},
+		33432: {
+			'name': 'Copyright',
+			'type': 'Ascii'
+		},
+		33434: {
+			'name': 'ExposureTime',
+			'type': 'Rational'
+		},
+		34377: {
+			'name': 'ImageResources',
+			'type': 'Byte'
+		},
+		34665: {
+			'name': 'ExifTag',
+			'type': 'Long'
+		},
+		34675: {
+			'name': 'InterColorProfile',
+			'type': 'Undefined'
+		},
+		34853: {
+			'name': 'GPSTag',
+			'type': 'Long'
+		},
+		34857: {
+			'name': 'Interlace',
+			'type': 'Short'
+		},
+		34858: {
+			'name': 'TimeZoneOffset',
+			'type': 'Long'
+		},
+		34859: {
+			'name': 'SelfTimerMode',
+			'type': 'Short'
+		},
+		37387: {
+			'name': 'FlashEnergy',
+			'type': 'Rational'
+		},
+		37388: {
+			'name': 'SpatialFrequencyResponse',
+			'type': 'Undefined'
+		},
+		37389: {
+			'name': 'Noise',
+			'type': 'Undefined'
+		},
+		37390: {
+			'name': 'FocalPlaneXResolution',
+			'type': 'Rational'
+		},
+		37391: {
+			'name': 'FocalPlaneYResolution',
+			'type': 'Rational'
+		},
+		37392: {
+			'name': 'FocalPlaneResolutionUnit',
+			'type': 'Short'
+		},
+		37393: {
+			'name': 'ImageNumber',
+			'type': 'Long'
+		},
+		37394: {
+			'name': 'SecurityClassification',
+			'type': 'Ascii'
+		},
+		37395: {
+			'name': 'ImageHistory',
+			'type': 'Ascii'
+		},
+		37397: {
+			'name': 'ExposureIndex',
+			'type': 'Rational'
+		},
+		37398: {
+			'name': 'TIFFEPStandardID',
+			'type': 'Byte'
+		},
+		37399: {
+			'name': 'SensingMethod',
+			'type': 'Short'
+		},
+		40091: {
+			'name': 'XPTitle',
+			'type': 'Byte'
+		},
+		40092: {
+			'name': 'XPComment',
+			'type': 'Byte'
+		},
+		40093: {
+			'name': 'XPAuthor',
+			'type': 'Byte'
+		},
+		40094: {
+			'name': 'XPKeywords',
+			'type': 'Byte'
+		},
+		40095: {
+			'name': 'XPSubject',
+			'type': 'Byte'
+		},
+		50341: {
+			'name': 'PrintImageMatching',
+			'type': 'Undefined'
+		},
+		50706: {
+			'name': 'DNGVersion',
+			'type': 'Byte'
+		},
+		50707: {
+			'name': 'DNGBackwardVersion',
+			'type': 'Byte'
+		},
+		50708: {
+			'name': 'UniqueCameraModel',
+			'type': 'Ascii'
+		},
+		50709: {
+			'name': 'LocalizedCameraModel',
+			'type': 'Byte'
+		},
+		50710: {
+			'name': 'CFAPlaneColor',
+			'type': 'Byte'
+		},
+		50711: {
+			'name': 'CFALayout',
+			'type': 'Short'
+		},
+		50712: {
+			'name': 'LinearizationTable',
+			'type': 'Short'
+		},
+		50713: {
+			'name': 'BlackLevelRepeatDim',
+			'type': 'Short'
+		},
+		50714: {
+			'name': 'BlackLevel',
+			'type': 'Rational'
+		},
+		50715: {
+			'name': 'BlackLevelDeltaH',
+			'type': 'SRational'
+		},
+		50716: {
+			'name': 'BlackLevelDeltaV',
+			'type': 'SRational'
+		},
+		50717: {
+			'name': 'WhiteLevel',
+			'type': 'Short'
+		},
+		50718: {
+			'name': 'DefaultScale',
+			'type': 'Rational'
+		},
+		50719: {
+			'name': 'DefaultCropOrigin',
+			'type': 'Short'
+		},
+		50720: {
+			'name': 'DefaultCropSize',
+			'type': 'Short'
+		},
+		50721: {
+			'name': 'ColorMatrix1',
+			'type': 'SRational'
+		},
+		50722: {
+			'name': 'ColorMatrix2',
+			'type': 'SRational'
+		},
+		50723: {
+			'name': 'CameraCalibration1',
+			'type': 'SRational'
+		},
+		50724: {
+			'name': 'CameraCalibration2',
+			'type': 'SRational'
+		},
+		50725: {
+			'name': 'ReductionMatrix1',
+			'type': 'SRational'
+		},
+		50726: {
+			'name': 'ReductionMatrix2',
+			'type': 'SRational'
+		},
+		50727: {
+			'name': 'AnalogBalance',
+			'type': 'Rational'
+		},
+		50728: {
+			'name': 'AsShotNeutral',
+			'type': 'Short'
+		},
+		50729: {
+			'name': 'AsShotWhiteXY',
+			'type': 'Rational'
+		},
+		50730: {
+			'name': 'BaselineExposure',
+			'type': 'SRational'
+		},
+		50731: {
+			'name': 'BaselineNoise',
+			'type': 'Rational'
+		},
+		50732: {
+			'name': 'BaselineSharpness',
+			'type': 'Rational'
+		},
+		50733: {
+			'name': 'BayerGreenSplit',
+			'type': 'Long'
+		},
+		50734: {
+			'name': 'LinearResponseLimit',
+			'type': 'Rational'
+		},
+		50735: {
+			'name': 'CameraSerialNumber',
+			'type': 'Ascii'
+		},
+		50736: {
+			'name': 'LensInfo',
+			'type': 'Rational'
+		},
+		50737: {
+			'name': 'ChromaBlurRadius',
+			'type': 'Rational'
+		},
+		50738: {
+			'name': 'AntiAliasStrength',
+			'type': 'Rational'
+		},
+		50739: {
+			'name': 'ShadowScale',
+			'type': 'SRational'
+		},
+		50740: {
+			'name': 'DNGPrivateData',
+			'type': 'Byte'
+		},
+		50741: {
+			'name': 'MakerNoteSafety',
+			'type': 'Short'
+		},
+		50778: {
+			'name': 'CalibrationIlluminant1',
+			'type': 'Short'
+		},
+		50779: {
+			'name': 'CalibrationIlluminant2',
+			'type': 'Short'
+		},
+		50780: {
+			'name': 'BestQualityScale',
+			'type': 'Rational'
+		},
+		50781: {
+			'name': 'RawDataUniqueID',
+			'type': 'Byte'
+		},
+		50827: {
+			'name': 'OriginalRawFileName',
+			'type': 'Byte'
+		},
+		50828: {
+			'name': 'OriginalRawFileData',
+			'type': 'Undefined'
+		},
+		50829: {
+			'name': 'ActiveArea',
+			'type': 'Short'
+		},
+		50830: {
+			'name': 'MaskedAreas',
+			'type': 'Short'
+		},
+		50831: {
+			'name': 'AsShotICCProfile',
+			'type': 'Undefined'
+		},
+		50832: {
+			'name': 'AsShotPreProfileMatrix',
+			'type': 'SRational'
+		},
+		50833: {
+			'name': 'CurrentICCProfile',
+			'type': 'Undefined'
+		},
+		50834: {
+			'name': 'CurrentPreProfileMatrix',
+			'type': 'SRational'
+		},
+		50879: {
+			'name': 'ColorimetricReference',
+			'type': 'Short'
+		},
+		50931: {
+			'name': 'CameraCalibrationSignature',
+			'type': 'Byte'
+		},
+		50932: {
+			'name': 'ProfileCalibrationSignature',
+			'type': 'Byte'
+		},
+		50934: {
+			'name': 'AsShotProfileName',
+			'type': 'Byte'
+		},
+		50935: {
+			'name': 'NoiseReductionApplied',
+			'type': 'Rational'
+		},
+		50936: {
+			'name': 'ProfileName',
+			'type': 'Byte'
+		},
+		50937: {
+			'name': 'ProfileHueSatMapDims',
+			'type': 'Long'
+		},
+		50938: {
+			'name': 'ProfileHueSatMapData1',
+			'type': 'Float'
+		},
+		50939: {
+			'name': 'ProfileHueSatMapData2',
+			'type': 'Float'
+		},
+		50940: {
+			'name': 'ProfileToneCurve',
+			'type': 'Float'
+		},
+		50941: {
+			'name': 'ProfileEmbedPolicy',
+			'type': 'Long'
+		},
+		50942: {
+			'name': 'ProfileCopyright',
+			'type': 'Byte'
+		},
+		50964: {
+			'name': 'ForwardMatrix1',
+			'type': 'SRational'
+		},
+		50965: {
+			'name': 'ForwardMatrix2',
+			'type': 'SRational'
+		},
+		50966: {
+			'name': 'PreviewApplicationName',
+			'type': 'Byte'
+		},
+		50967: {
+			'name': 'PreviewApplicationVersion',
+			'type': 'Byte'
+		},
+		50968: {
+			'name': 'PreviewSettingsName',
+			'type': 'Byte'
+		},
+		50969: {
+			'name': 'PreviewSettingsDigest',
+			'type': 'Byte'
+		},
+		50970: {
+			'name': 'PreviewColorSpace',
+			'type': 'Long'
+		},
+		50971: {
+			'name': 'PreviewDateTime',
+			'type': 'Ascii'
+		},
+		50972: {
+			'name': 'RawImageDigest',
+			'type': 'Undefined'
+		},
+		50973: {
+			'name': 'OriginalRawFileDigest',
+			'type': 'Undefined'
+		},
+		50974: {
+			'name': 'SubTileBlockSize',
+			'type': 'Long'
+		},
+		50975: {
+			'name': 'RowInterleaveFactor',
+			'type': 'Long'
+		},
+		50981: {
+			'name': 'ProfileLookTableDims',
+			'type': 'Long'
+		},
+		50982: {
+			'name': 'ProfileLookTableData',
+			'type': 'Float'
+		},
+		51008: {
+			'name': 'OpcodeList1',
+			'type': 'Undefined'
+		},
+		51009: {
+			'name': 'OpcodeList2',
+			'type': 'Undefined'
+		},
+		51022: {
+			'name': 'OpcodeList3',
+			'type': 'Undefined'
+		}
+	},
+	'Exif': {
+		33434: {
+			'name': 'ExposureTime',
+			'type': 'Rational'
+		},
+		33437: {
+			'name': 'FNumber',
+			'type': 'Rational'
+		},
+		34850: {
+			'name': 'ExposureProgram',
+			'type': 'Short'
+		},
+		34852: {
+			'name': 'SpectralSensitivity',
+			'type': 'Ascii'
+		},
+		34855: {
+			'name': 'ISOSpeedRatings',
+			'type': 'Short'
+		},
+		34856: {
+			'name': 'OECF',
+			'type': 'Undefined'
+		},
+		34864: {
+			'name': 'SensitivityType',
+			'type': 'Short'
+		},
+		34865: {
+			'name': 'StandardOutputSensitivity',
+			'type': 'Long'
+		},
+		34866: {
+			'name': 'RecommendedExposureIndex',
+			'type': 'Long'
+		},
+		34867: {
+			'name': 'ISOSpeed',
+			'type': 'Long'
+		},
+		34868: {
+			'name': 'ISOSpeedLatitudeyyy',
+			'type': 'Long'
+		},
+		34869: {
+			'name': 'ISOSpeedLatitudezzz',
+			'type': 'Long'
+		},
+		36864: {
+			'name': 'ExifVersion',
+			'type': 'Undefined'
+		},
+		36867: {
+			'name': 'DateTimeOriginal',
+			'type': 'Ascii'
+		},
+		36868: {
+			'name': 'DateTimeDigitized',
+			'type': 'Ascii'
+		},
+		37121: {
+			'name': 'ComponentsConfiguration',
+			'type': 'Undefined'
+		},
+		37122: {
+			'name': 'CompressedBitsPerPixel',
+			'type': 'Rational'
+		},
+		37377: {
+			'name': 'ShutterSpeedValue',
+			'type': 'SRational'
+		},
+		37378: {
+			'name': 'ApertureValue',
+			'type': 'Rational'
+		},
+		37379: {
+			'name': 'BrightnessValue',
+			'type': 'SRational'
+		},
+		37380: {
+			'name': 'ExposureBiasValue',
+			'type': 'SRational'
+		},
+		37381: {
+			'name': 'MaxApertureValue',
+			'type': 'Rational'
+		},
+		37382: {
+			'name': 'SubjectDistance',
+			'type': 'Rational'
+		},
+		37383: {
+			'name': 'MeteringMode',
+			'type': 'Short'
+		},
+		37384: {
+			'name': 'LightSource',
+			'type': 'Short'
+		},
+		37385: {
+			'name': 'Flash',
+			'type': 'Short'
+		},
+		37386: {
+			'name': 'FocalLength',
+			'type': 'Rational'
+		},
+		37396: {
+			'name': 'SubjectArea',
+			'type': 'Short'
+		},
+		37500: {
+			'name': 'MakerNote',
+			'type': 'Undefined'
+		},
+		37510: {
+			'name': 'UserComment',
+			'type': 'Ascii'
+		},
+		37520: {
+			'name': 'SubSecTime',
+			'type': 'Ascii'
+		},
+		37521: {
+			'name': 'SubSecTimeOriginal',
+			'type': 'Ascii'
+		},
+		37522: {
+			'name': 'SubSecTimeDigitized',
+			'type': 'Ascii'
+		},
+		40960: {
+			'name': 'FlashpixVersion',
+			'type': 'Undefined'
+		},
+		40961: {
+			'name': 'ColorSpace',
+			'type': 'Short'
+		},
+		40962: {
+			'name': 'PixelXDimension',
+			'type': 'Long'
+		},
+		40963: {
+			'name': 'PixelYDimension',
+			'type': 'Long'
+		},
+		40964: {
+			'name': 'RelatedSoundFile',
+			'type': 'Ascii'
+		},
+		40965: {
+			'name': 'InteroperabilityTag',
+			'type': 'Long'
+		},
+		41483: {
+			'name': 'FlashEnergy',
+			'type': 'Rational'
+		},
+		41484: {
+			'name': 'SpatialFrequencyResponse',
+			'type': 'Undefined'
+		},
+		41486: {
+			'name': 'FocalPlaneXResolution',
+			'type': 'Rational'
+		},
+		41487: {
+			'name': 'FocalPlaneYResolution',
+			'type': 'Rational'
+		},
+		41488: {
+			'name': 'FocalPlaneResolutionUnit',
+			'type': 'Short'
+		},
+		41492: {
+			'name': 'SubjectLocation',
+			'type': 'Short'
+		},
+		41493: {
+			'name': 'ExposureIndex',
+			'type': 'Rational'
+		},
+		41495: {
+			'name': 'SensingMethod',
+			'type': 'Short'
+		},
+		41728: {
+			'name': 'FileSource',
+			'type': 'Undefined'
+		},
+		41729: {
+			'name': 'SceneType',
+			'type': 'Undefined'
+		},
+		41730: {
+			'name': 'CFAPattern',
+			'type': 'Undefined'
+		},
+		41985: {
+			'name': 'CustomRendered',
+			'type': 'Short'
+		},
+		41986: {
+			'name': 'ExposureMode',
+			'type': 'Short'
+		},
+		41987: {
+			'name': 'WhiteBalance',
+			'type': 'Short'
+		},
+		41988: {
+			'name': 'DigitalZoomRatio',
+			'type': 'Rational'
+		},
+		41989: {
+			'name': 'FocalLengthIn35mmFilm',
+			'type': 'Short'
+		},
+		41990: {
+			'name': 'SceneCaptureType',
+			'type': 'Short'
+		},
+		41991: {
+			'name': 'GainControl',
+			'type': 'Short'
+		},
+		41992: {
+			'name': 'Contrast',
+			'type': 'Short'
+		},
+		41993: {
+			'name': 'Saturation',
+			'type': 'Short'
+		},
+		41994: {
+			'name': 'Sharpness',
+			'type': 'Short'
+		},
+		41995: {
+			'name': 'DeviceSettingDescription',
+			'type': 'Undefined'
+		},
+		41996: {
+			'name': 'SubjectDistanceRange',
+			'type': 'Short'
+		},
+		42016: {
+			'name': 'ImageUniqueID',
+			'type': 'Ascii'
+		},
+		42032: {
+			'name': 'CameraOwnerName',
+			'type': 'Ascii'
+		},
+		42033: {
+			'name': 'BodySerialNumber',
+			'type': 'Ascii'
+		},
+		42034: {
+			'name': 'LensSpecification',
+			'type': 'Rational'
+		},
+		42035: {
+			'name': 'LensMake',
+			'type': 'Ascii'
+		},
+		42036: {
+			'name': 'LensModel',
+			'type': 'Ascii'
+		},
+		42037: {
+			'name': 'LensSerialNumber',
+			'type': 'Ascii'
+		},
+		42240: {
+			'name': 'Gamma',
+			'type': 'Rational'
+		}
+	},
+	'GPS': {
+		0: {
+			'name': 'GPSVersionID',
+			'type': 'Byte'
+		},
+		1: {
+			'name': 'GPSLatitudeRef',
+			'type': 'Ascii'
+		},
+		2: {
+			'name': 'GPSLatitude',
+			'type': 'Rational'
+		},
+		3: {
+			'name': 'GPSLongitudeRef',
+			'type': 'Ascii'
+		},
+		4: {
+			'name': 'GPSLongitude',
+			'type': 'Rational'
+		},
+		5: {
+			'name': 'GPSAltitudeRef',
+			'type': 'Byte'
+		},
+		6: {
+			'name': 'GPSAltitude',
+			'type': 'Rational'
+		},
+		7: {
+			'name': 'GPSTimeStamp',
+			'type': 'Rational'
+		},
+		8: {
+			'name': 'GPSSatellites',
+			'type': 'Ascii'
+		},
+		9: {
+			'name': 'GPSStatus',
+			'type': 'Ascii'
+		},
+		10: {
+			'name': 'GPSMeasureMode',
+			'type': 'Ascii'
+		},
+		11: {
+			'name': 'GPSDOP',
+			'type': 'Rational'
+		},
+		12: {
+			'name': 'GPSSpeedRef',
+			'type': 'Ascii'
+		},
+		13: {
+			'name': 'GPSSpeed',
+			'type': 'Rational'
+		},
+		14: {
+			'name': 'GPSTrackRef',
+			'type': 'Ascii'
+		},
+		15: {
+			'name': 'GPSTrack',
+			'type': 'Rational'
+		},
+		16: {
+			'name': 'GPSImgDirectionRef',
+			'type': 'Ascii'
+		},
+		17: {
+			'name': 'GPSImgDirection',
+			'type': 'Rational'
+		},
+		18: {
+			'name': 'GPSMapDatum',
+			'type': 'Ascii'
+		},
+		19: {
+			'name': 'GPSDestLatitudeRef',
+			'type': 'Ascii'
+		},
+		20: {
+			'name': 'GPSDestLatitude',
+			'type': 'Rational'
+		},
+		21: {
+			'name': 'GPSDestLongitudeRef',
+			'type': 'Ascii'
+		},
+		22: {
+			'name': 'GPSDestLongitude',
+			'type': 'Rational'
+		},
+		23: {
+			'name': 'GPSDestBearingRef',
+			'type': 'Ascii'
+		},
+		24: {
+			'name': 'GPSDestBearing',
+			'type': 'Rational'
+		},
+		25: {
+			'name': 'GPSDestDistanceRef',
+			'type': 'Ascii'
+		},
+		26: {
+			'name': 'GPSDestDistance',
+			'type': 'Rational'
+		},
+		27: {
+			'name': 'GPSProcessingMethod',
+			'type': 'Undefined'
+		},
+		28: {
+			'name': 'GPSAreaInformation',
+			'type': 'Undefined'
+		},
+		29: {
+			'name': 'GPSDateStamp',
+			'type': 'Ascii'
+		},
+		30: {
+			'name': 'GPSDifferential',
+			'type': 'Short'
+		},
+		31: {
+			'name': 'GPSHPositioningError',
+			'type': 'Rational'
+		}
+	},
+	'Interop': {
+		1: {
+			'name': 'InteroperabilityIndex',
+			'type': 'Ascii'
+		}
+	},
+};
+const TYPES = {
+	"Byte": 1,
+	"Ascii": 2,
+	"Short": 3,
+	"Long": 4,
+	"Rational": 5,
+	"Undefined": 7,
+	"SLong": 9,
+	"SRational": 10
+};
+TAGS["0th"] = TAGS["Image"];
+TAGS["1st"] = TAGS["Image"];
+class Piexif {
+	version = "1.0.4"
+	TAGS = TAGS
+	ImageIFD = {
+		ProcessingSoftware: 11,
+		NewSubfileType: 254,
+		SubfileType: 255,
+		ImageWidth: 256,
+		ImageLength: 257,
+		BitsPerSample: 258,
+		Compression: 259,
+		PhotometricInterpretation: 262,
+		Threshholding: 263,
+		CellWidth: 264,
+		CellLength: 265,
+		FillOrder: 266,
+		DocumentName: 269,
+		ImageDescription: 270,
+		Make: 271,
+		Model: 272,
+		StripOffsets: 273,
+		Orientation: 274,
+		SamplesPerPixel: 277,
+		RowsPerStrip: 278,
+		StripByteCounts: 279,
+		XResolution: 282,
+		YResolution: 283,
+		PlanarConfiguration: 284,
+		GrayResponseUnit: 290,
+		GrayResponseCurve: 291,
+		T4Options: 292,
+		T6Options: 293,
+		ResolutionUnit: 296,
+		TransferFunction: 301,
+		Software: 305,
+		DateTime: 306,
+		Artist: 315,
+		HostComputer: 316,
+		Predictor: 317,
+		WhitePoint: 318,
+		PrimaryChromaticities: 319,
+		ColorMap: 320,
+		HalftoneHints: 321,
+		TileWidth: 322,
+		TileLength: 323,
+		TileOffsets: 324,
+		TileByteCounts: 325,
+		SubIFDs: 330,
+		InkSet: 332,
+		InkNames: 333,
+		NumberOfInks: 334,
+		DotRange: 336,
+		TargetPrinter: 337,
+		ExtraSamples: 338,
+		SampleFormat: 339,
+		SMinSampleValue: 340,
+		SMaxSampleValue: 341,
+		TransferRange: 342,
+		ClipPath: 343,
+		XClipPathUnits: 344,
+		YClipPathUnits: 345,
+		Indexed: 346,
+		JPEGTables: 347,
+		OPIProxy: 351,
+		JPEGProc: 512,
+		JPEGInterchangeFormat: 513,
+		JPEGInterchangeFormatLength: 514,
+		JPEGRestartInterval: 515,
+		JPEGLosslessPredictors: 517,
+		JPEGPointTransforms: 518,
+		JPEGQTables: 519,
+		JPEGDCTables: 520,
+		JPEGACTables: 521,
+		YCbCrCoefficients: 529,
+		YCbCrSubSampling: 530,
+		YCbCrPositioning: 531,
+		ReferenceBlackWhite: 532,
+		XMLPacket: 700,
+		Rating: 18246,
+		RatingPercent: 18249,
+		ImageID: 32781,
+		CFARepeatPatternDim: 33421,
+		CFAPattern: 33422,
+		BatteryLevel: 33423,
+		Copyright: 33432,
+		ExposureTime: 33434,
+		ImageResources: 34377,
+		ExifTag: 34665,
+		InterColorProfile: 34675,
+		GPSTag: 34853,
+		Interlace: 34857,
+		TimeZoneOffset: 34858,
+		SelfTimerMode: 34859,
+		FlashEnergy: 37387,
+		SpatialFrequencyResponse: 37388,
+		Noise: 37389,
+		FocalPlaneXResolution: 37390,
+		FocalPlaneYResolution: 37391,
+		FocalPlaneResolutionUnit: 37392,
+		ImageNumber: 37393,
+		SecurityClassification: 37394,
+		ImageHistory: 37395,
+		ExposureIndex: 37397,
+		TIFFEPStandardID: 37398,
+		SensingMethod: 37399,
+		XPTitle: 40091,
+		XPComment: 40092,
+		XPAuthor: 40093,
+		XPKeywords: 40094,
+		XPSubject: 40095,
+		PrintImageMatching: 50341,
+		DNGVersion: 50706,
+		DNGBackwardVersion: 50707,
+		UniqueCameraModel: 50708,
+		LocalizedCameraModel: 50709,
+		CFAPlaneColor: 50710,
+		CFALayout: 50711,
+		LinearizationTable: 50712,
+		BlackLevelRepeatDim: 50713,
+		BlackLevel: 50714,
+		BlackLevelDeltaH: 50715,
+		BlackLevelDeltaV: 50716,
+		WhiteLevel: 50717,
+		DefaultScale: 50718,
+		DefaultCropOrigin: 50719,
+		DefaultCropSize: 50720,
+		ColorMatrix1: 50721,
+		ColorMatrix2: 50722,
+		CameraCalibration1: 50723,
+		CameraCalibration2: 50724,
+		ReductionMatrix1: 50725,
+		ReductionMatrix2: 50726,
+		AnalogBalance: 50727,
+		AsShotNeutral: 50728,
+		AsShotWhiteXY: 50729,
+		BaselineExposure: 50730,
+		BaselineNoise: 50731,
+		BaselineSharpness: 50732,
+		BayerGreenSplit: 50733,
+		LinearResponseLimit: 50734,
+		CameraSerialNumber: 50735,
+		LensInfo: 50736,
+		ChromaBlurRadius: 50737,
+		AntiAliasStrength: 50738,
+		ShadowScale: 50739,
+		DNGPrivateData: 50740,
+		MakerNoteSafety: 50741,
+		CalibrationIlluminant1: 50778,
+		CalibrationIlluminant2: 50779,
+		BestQualityScale: 50780,
+		RawDataUniqueID: 50781,
+		OriginalRawFileName: 50827,
+		OriginalRawFileData: 50828,
+		ActiveArea: 50829,
+		MaskedAreas: 50830,
+		AsShotICCProfile: 50831,
+		AsShotPreProfileMatrix: 50832,
+		CurrentICCProfile: 50833,
+		CurrentPreProfileMatrix: 50834,
+		ColorimetricReference: 50879,
+		CameraCalibrationSignature: 50931,
+		ProfileCalibrationSignature: 50932,
+		AsShotProfileName: 50934,
+		NoiseReductionApplied: 50935,
+		ProfileName: 50936,
+		ProfileHueSatMapDims: 50937,
+		ProfileHueSatMapData1: 50938,
+		ProfileHueSatMapData2: 50939,
+		ProfileToneCurve: 50940,
+		ProfileEmbedPolicy: 50941,
+		ProfileCopyright: 50942,
+		ForwardMatrix1: 50964,
+		ForwardMatrix2: 50965,
+		PreviewApplicationName: 50966,
+		PreviewApplicationVersion: 50967,
+		PreviewSettingsName: 50968,
+		PreviewSettingsDigest: 50969,
+		PreviewColorSpace: 50970,
+		PreviewDateTime: 50971,
+		RawImageDigest: 50972,
+		OriginalRawFileDigest: 50973,
+		SubTileBlockSize: 50974,
+		RowInterleaveFactor: 50975,
+		ProfileLookTableDims: 50981,
+		ProfileLookTableData: 50982,
+		OpcodeList1: 51008,
+		OpcodeList2: 51009,
+		OpcodeList3: 51022,
+		NoiseProfile: 51041,
+	}
+	ExifIFD = {
+		ExposureTime: 33434,
+		FNumber: 33437,
+		ExposureProgram: 34850,
+		SpectralSensitivity: 34852,
+		ISOSpeedRatings: 34855,
+		OECF: 34856,
+		SensitivityType: 34864,
+		StandardOutputSensitivity: 34865,
+		RecommendedExposureIndex: 34866,
+		ISOSpeed: 34867,
+		ISOSpeedLatitudeyyy: 34868,
+		ISOSpeedLatitudezzz: 34869,
+		ExifVersion: 36864,
+		DateTimeOriginal: 36867,
+		DateTimeDigitized: 36868,
+		ComponentsConfiguration: 37121,
+		CompressedBitsPerPixel: 37122,
+		ShutterSpeedValue: 37377,
+		ApertureValue: 37378,
+		BrightnessValue: 37379,
+		ExposureBiasValue: 37380,
+		MaxApertureValue: 37381,
+		SubjectDistance: 37382,
+		MeteringMode: 37383,
+		LightSource: 37384,
+		Flash: 37385,
+		FocalLength: 37386,
+		SubjectArea: 37396,
+		MakerNote: 37500,
+		UserComment: 37510,
+		SubSecTime: 37520,
+		SubSecTimeOriginal: 37521,
+		SubSecTimeDigitized: 37522,
+		FlashpixVersion: 40960,
+		ColorSpace: 40961,
+		PixelXDimension: 40962,
+		PixelYDimension: 40963,
+		RelatedSoundFile: 40964,
+		InteroperabilityTag: 40965,
+		FlashEnergy: 41483,
+		SpatialFrequencyResponse: 41484,
+		FocalPlaneXResolution: 41486,
+		FocalPlaneYResolution: 41487,
+		FocalPlaneResolutionUnit: 41488,
+		SubjectLocation: 41492,
+		ExposureIndex: 41493,
+		SensingMethod: 41495,
+		FileSource: 41728,
+		SceneType: 41729,
+		CFAPattern: 41730,
+		CustomRendered: 41985,
+		ExposureMode: 41986,
+		WhiteBalance: 41987,
+		DigitalZoomRatio: 41988,
+		FocalLengthIn35mmFilm: 41989,
+		SceneCaptureType: 41990,
+		GainControl: 41991,
+		Contrast: 41992,
+		Saturation: 41993,
+		Sharpness: 41994,
+		DeviceSettingDescription: 41995,
+		SubjectDistanceRange: 41996,
+		ImageUniqueID: 42016,
+		CameraOwnerName: 42032,
+		BodySerialNumber: 42033,
+		LensSpecification: 42034,
+		LensMake: 42035,
+		LensModel: 42036,
+		LensSerialNumber: 42037,
+		Gamma: 42240,
+	}
+	GPSIFD = {
+		GPSVersionID: 0,
+		GPSLatitudeRef: 1,
+		GPSLatitude: 2,
+		GPSLongitudeRef: 3,
+		GPSLongitude: 4,
+		GPSAltitudeRef: 5,
+		GPSAltitude: 6,
+		GPSTimeStamp: 7,
+		GPSSatellites: 8,
+		GPSStatus: 9,
+		GPSMeasureMode: 10,
+		GPSDOP: 11,
+		GPSSpeedRef: 12,
+		GPSSpeed: 13,
+		GPSTrackRef: 14,
+		GPSTrack: 15,
+		GPSImgDirectionRef: 16,
+		GPSImgDirection: 17,
+		GPSMapDatum: 18,
+		GPSDestLatitudeRef: 19,
+		GPSDestLatitude: 20,
+		GPSDestLongitudeRef: 21,
+		GPSDestLongitude: 22,
+		GPSDestBearingRef: 23,
+		GPSDestBearing: 24,
+		GPSDestDistanceRef: 25,
+		GPSDestDistance: 26,
+		GPSProcessingMethod: 27,
+		GPSAreaInformation: 28,
+		GPSDateStamp: 29,
+		GPSDifferential: 30,
+		GPSHPositioningError: 31,
+	}
+	InteropIFD = {
+		InteroperabilityIndex: 1,
+	}
+	GPSHelper = {
+		degToDmsRational(degFloat : number) {
+			const degAbs = Math.abs(degFloat);
+			const minFloat = degAbs % 1 * 60;
+			const secFloat = minFloat % 1 * 60;
+			const deg = Math.floor(degAbs);
+			const min = Math.floor(minFloat);
+			const sec = Math.round(secFloat * 100);
+
+			return [
+				[deg, 1],
+				[min, 1],
+				[sec, 100]
+			];
+		},
+		dmsRationalToDeg(dmsArray : number[][], ref : string) {
+			const sign = (ref === 'S' || ref === 'W') ? -1.0 : 1.0;
+			const deg = dmsArray[0][0] / dmsArray[0][1] +
+				dmsArray[1][0] / dmsArray[1][1] / 60.0 +
+				dmsArray[2][0] / dmsArray[2][1] / 3600.0;
+
+			return deg * sign;
+		},
+	}
+	remove(jpeg) {
+		let b64 = false;
+		if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg
+			.slice(0, 22) == "data:image/jpg;base64,") {
+			jpeg = atob(jpeg.split(",")[1]);
+			b64 = true;
+		} else {
+			throw new Error("Given data is not jpeg.");
+		}
+
+		const segments = splitIntoSegments(jpeg);
+		const newSegments = segments.filter(function (seg) {
+			return !(seg.slice(0, 2) == "\xff\xe1" &&
+				seg.slice(4, 10) == "Exif\x00\x00");
+		});
+
+		let new_data = newSegments.join("");
+		if (b64) {
+			new_data = "data:image/jpeg;base64," + btoa(new_data);
+		}
+
+		return new_data;
+	}
+	insert(exif, jpeg) {
+		let b64 = false;
+		if (exif.slice(0, 6) != "\x45\x78\x69\x66\x00\x00") {
+			throw new Error("Given data is not exif.");
+		}
+		if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg
+			.slice(0, 22) == "data:image/jpg;base64,") {
+			jpeg = atob(jpeg.split(",")[1]);
+			b64 = true;
+		} else {
+			throw new Error("Given data is not jpeg.");
+		}
+
+		const exifStr = "\xff\xe1" + pack(">H", [exif.length + 2]) + exif;
+		const segments = splitIntoSegments(jpeg);
+		let new_data = mergeSegments(segments, exifStr);
+		if (b64) {
+			new_data = "data:image/jpeg;base64," + btoa(new_data);
+		}
+
+		return new_data;
+	}
+	load(data) {
+		let input_data;
+		if (isString(data)) {
+			if (data.slice(0, 2) == "\xff\xd8") {
+				input_data = data;
+			} else if (data.slice(0, 23) == "data:image/jpeg;base64," || data.slice(0, 22) == "data:image/jpg;base64,") {
+				input_data = atob(data.split(",")[1]);
+			} else if (data.slice(0, 4) == "Exif") {
+				input_data = data.slice(6);
+			} else {
+				throw new Error("'load' gots invalid file data.");
+			}
+		} else {
+			throw new Error("'load' gots invalid type argument.");
+		}
+
+		let exifDict = {};
+		let exif_dict = {
+			"0th": {},
+			"Exif": {},
+			"GPS": {},
+			"Interop": {},
+			"1st": {},
+			"thumbnail": null
+		};
+		const exifReader = new ExifReader(input_data);
+		if (exifReader.tiftag === null) {
+			return exif_dict;
+		}
+
+		if (exifReader.tiftag.slice(0, 2) == "\x49\x49") {
+			exifReader.endian_mark = "<";
+		} else {
+			exifReader.endian_mark = ">";
+		}
+
+		let pointer = unpack(exifReader.endian_mark + "L",
+			exifReader.tiftag.slice(4, 8))[0];
+		exif_dict["0th"] = exifReader.get_ifd(pointer, "0th");
+
+		const first_ifd_pointer = exif_dict["0th"]["first_ifd_pointer"];
+		delete exif_dict["0th"]["first_ifd_pointer"];
+
+		if (34665 in exif_dict["0th"]) {
+			pointer = exif_dict["0th"][34665];
+			exif_dict["Exif"] = exifReader.get_ifd(pointer, "Exif");
+		}
+		if (34853 in exif_dict["0th"]) {
+			pointer = exif_dict["0th"][34853];
+			exif_dict["GPS"] = exifReader.get_ifd(pointer, "GPS");
+		}
+		if (40965 in exif_dict["Exif"]) {
+			pointer = exif_dict["Exif"][40965];
+			exif_dict["Interop"] = exifReader.get_ifd(pointer, "Interop");
+		}
+		if (first_ifd_pointer != "\x00\x00\x00\x00") {
+			pointer = unpack(exifReader.endian_mark + "L",
+				first_ifd_pointer)[0];
+			exif_dict["1st"] = exifReader.get_ifd(pointer, "1st");
+			if ((513 in exif_dict["1st"]) && (514 in exif_dict["1st"])) {
+				var end = exif_dict["1st"][513] + exif_dict["1st"][514];
+				var thumb = exifReader.tiftag.slice(exif_dict["1st"][513], end);
+				exif_dict["thumbnail"] = thumb;
+			}
+		}
+
+		return exif_dict;
+	}
+	dump(exif_dict_original) {
+		const TIFF_HEADER_LENGTH = 8;
+
+		const exif_dict : any = cloneDeep(exif_dict_original);
+		const header = "Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08";
+		let exif_is = 0//false;
+		let gps_is = 0 //false;
+		let interop_is = 0//false;
+		let first_is = false;
+
+		let zeroth_ifd,
+			exif_ifd,
+			interop_ifd,
+			gps_ifd,
+			first_ifd;
+
+		if ("0th" in exif_dict) {
+			zeroth_ifd = exif_dict["0th"];
+		} else {
+			zeroth_ifd = {};
+		}
+
+		if ((("Exif" in exif_dict) && (Object.keys(exif_dict["Exif"]).length)) ||
+			(("Interop" in exif_dict) && (Object.keys(exif_dict["Interop"]).length))) {
+			zeroth_ifd[34665] = 1;
+			exif_is = 1//true;
+			exif_ifd = exif_dict["Exif"];
+			if (("Interop" in exif_dict) && Object.keys(exif_dict["Interop"]).length) {
+				exif_ifd[40965] = 1;
+				interop_is = 1//true;
+				interop_ifd = exif_dict["Interop"];
+			} else if (Object.keys(exif_ifd).indexOf(this.ExifIFD.InteroperabilityTag.toString()) > -1) {
+				delete exif_ifd[40965];
+			}
+		} else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.ExifTag.toString()) > -1) {
+			delete zeroth_ifd[34665];
+		}
+
+		if (("GPS" in exif_dict) && (Object.keys(exif_dict["GPS"]).length)) {
+			zeroth_ifd[this.ImageIFD.GPSTag] = 1;
+			gps_is = 1 //true;
+			gps_ifd = exif_dict["GPS"];
+		} else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.GPSTag.toString()) > -1) {
+			delete zeroth_ifd[this.ImageIFD.GPSTag];
+		}
+
+		if (("1st" in exif_dict) &&
+			("thumbnail" in exif_dict) &&
+			(exif_dict["thumbnail"] != null)) {
+			first_is = true;
+			exif_dict["1st"][513] = 1;
+			exif_dict["1st"][514] = 1;
+			first_ifd = exif_dict["1st"];
+		}
+
+		const zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0);
+		const zeroth_length = (zeroth_set[0].length + exif_is * 12 + gps_is * 12 + 4 +
+			zeroth_set[1].length);
+
+		let exif_set,
+			exif_bytes = "",
+			exif_length = 0,
+			gps_set,
+			gps_bytes = "",
+			gps_length = 0,
+			interop_set,
+			interop_bytes = "",
+			interop_length = 0,
+			first_set,
+			first_bytes = "",
+			thumbnail;
+		if (exif_is) {
+			exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length);
+			exif_length = exif_set[0].length + interop_is * 12 + exif_set[1].length;
+		}
+		if (gps_is) {
+			gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length);
+			gps_bytes = gps_set.join("");
+			gps_length = gps_bytes.length;
+		}
+		if (interop_is) {
+			const offset = zeroth_length + exif_length + gps_length;
+			interop_set = _dict_to_bytes(interop_ifd, "Interop", offset);
+			interop_bytes = interop_set.join("");
+			interop_length = interop_bytes.length;
+		}
+		if (first_is) {
+			const offset = zeroth_length + exif_length + gps_length + interop_length;
+			first_set = _dict_to_bytes(first_ifd, "1st", offset);
+			thumbnail = _get_thumbnail(exif_dict["thumbnail"]);
+			if (thumbnail.length > 64000) {
+				throw new Error("Given thumbnail is too large. max 64kB");
+			}
+		}
+
+		let exif_pointer = "",
+			gps_pointer = "",
+			interop_pointer = "",
+			first_ifd_pointer = "\x00\x00\x00\x00";
+		if (exif_is) {
+			const pointer_value = TIFF_HEADER_LENGTH + zeroth_length;
+			const pointer_str = pack(">L", [pointer_value]);
+			const key = 34665;
+			const key_str = pack(">H", [key]);
+			const type_str = pack(">H", [TYPES["Long"]]);
+			const length_str = pack(">L", [1]);
+			exif_pointer = key_str + type_str + length_str + pointer_str;
+		}
+		if (gps_is) {
+			const pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length;
+			const pointer_str = pack(">L", [pointer_value]);
+			const key = 34853;
+			const key_str = pack(">H", [key]);
+			const type_str = pack(">H", [TYPES["Long"]]);
+			const length_str = pack(">L", [1]);
+			gps_pointer = key_str + type_str + length_str + pointer_str;
+		}
+		if (interop_is) {
+			const pointer_value = (TIFF_HEADER_LENGTH +
+				zeroth_length + exif_length + gps_length);
+			const pointer_str = pack(">L", [pointer_value]);
+			const key = 40965;
+			const key_str = pack(">H", [key]);
+			const type_str = pack(">H", [TYPES["Long"]]);
+			const length_str = pack(">L", [1]);
+			interop_pointer = key_str + type_str + length_str + pointer_str;
+		}
+		if (first_is) {
+			const pointer_value = (TIFF_HEADER_LENGTH + zeroth_length +
+				exif_length + gps_length + interop_length);
+			first_ifd_pointer = pack(">L", [pointer_value]);
+			const thumbnail_pointer = (pointer_value + first_set[0].length + 24 +
+				4 + first_set[1].length);
+			const thumbnail_p_bytes = ("\x02\x01\x00\x04\x00\x00\x00\x01" +
+				pack(">L", [thumbnail_pointer]));
+			const thumbnail_length_bytes = ("\x02\x02\x00\x04\x00\x00\x00\x01" +
+				pack(">L", [thumbnail.length]));
+			first_bytes = (first_set[0] + thumbnail_p_bytes +
+				thumbnail_length_bytes + "\x00\x00\x00\x00" +
+				first_set[1] + thumbnail);
+		}
+
+		const zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer +
+			first_ifd_pointer + zeroth_set[1]);
+		if (exif_is) {
+			exif_bytes = exif_set[0] + interop_pointer + exif_set[1];
+		}
+
+		return (header + zeroth_bytes + exif_bytes + gps_bytes +
+			interop_bytes + first_bytes);
+	}
+}
+
+
+
+function _get_thumbnail(jpeg) {
+	let segments = splitIntoSegments(jpeg);
+	while (("\xff\xe0" <= segments[1].slice(0, 2)) && (segments[1].slice(0, 2) <= "\xff\xef")) {
+		segments = [segments[0]].concat(segments.slice(2));
+	}
+	return segments.join("");
+}
+function _pack_byte(array) {
+	return pack(">" + nStr("B", array.length), array);
+}
+
+function _pack_short(array) {
+	return pack(">" + nStr("H", array.length), array);
+}
+
+function _pack_long(array) {
+	return pack(">" + nStr("L", array.length), array);
+}
+
+function _value_to_bytes(raw_value, value_type, offset) {
+	let four_bytes_over = "";
+	let value_str = "";
+	let length,
+		new_value,
+		num,
+		den;
+
+	if (value_type == "Byte") {
+		length = raw_value.length;
+		if (length <= 4) {
+			value_str = (_pack_byte(raw_value) +
+				nStr("\x00", 4 - length));
+		} else {
+			value_str = pack(">L", [offset]);
+			four_bytes_over = _pack_byte(raw_value);
+		}
+	} else if (value_type == "Short") {
+		length = raw_value.length;
+		if (length <= 2) {
+			value_str = (_pack_short(raw_value) +
+				nStr("\x00\x00", 2 - length));
+		} else {
+			value_str = pack(">L", [offset]);
+			four_bytes_over = _pack_short(raw_value);
+		}
+	} else if (value_type == "Long") {
+		length = raw_value.length;
+		if (length <= 1) {
+			value_str = _pack_long(raw_value);
+		} else {
+			value_str = pack(">L", [offset]);
+			four_bytes_over = _pack_long(raw_value);
+		}
+	} else if (value_type == "Ascii") {
+		new_value = raw_value + "\x00";
+		length = new_value.length;
+		if (length > 4) {
+			value_str = pack(">L", [offset]);
+			four_bytes_over = new_value;
+		} else {
+			value_str = new_value + nStr("\x00", 4 - length);
+		}
+	} else if (value_type == "Rational") {
+		if (typeof (raw_value[0]) == "number") {
+			length = 1;
+			num = raw_value[0];
+			den = raw_value[1];
+			new_value = pack(">L", [num]) + pack(">L", [den]);
+		} else {
+			length = raw_value.length;
+			new_value = "";
+			for (var n = 0; n < length; n++) {
+				num = raw_value[n][0];
+				den = raw_value[n][1];
+				new_value += (pack(">L", [num]) +
+					pack(">L", [den]));
+			}
+		}
+		value_str = pack(">L", [offset]);
+		four_bytes_over = new_value;
+	} else if (value_type == "SRational") {
+		if (typeof (raw_value[0]) == "number") {
+			length = 1;
+			num = raw_value[0];
+			den = raw_value[1];
+			new_value = pack(">l", [num]) + pack(">l", [den]);
+		} else {
+			length = raw_value.length;
+			new_value = "";
+			for (var n = 0; n < length; n++) {
+				num = raw_value[n][0];
+				den = raw_value[n][1];
+				new_value += (pack(">l", [num]) +
+					pack(">l", [den]));
+			}
+		}
+		value_str = pack(">L", [offset]);
+		four_bytes_over = new_value;
+	} else if (value_type == "Undefined") {
+		length = raw_value.length;
+		if (length > 4) {
+			value_str = pack(">L", [offset]);
+			four_bytes_over = raw_value;
+		} else {
+			value_str = raw_value + nStr("\x00", 4 - length);
+		}
+	}
+
+	var length_str = pack(">L", [length]);
+
+	return [length_str, value_str, four_bytes_over];
+}
+
+function _dict_to_bytes(ifd_dict, ifd, ifd_offset) {
+	const TIFF_HEADER_LENGTH = 8;
+	const tag_count = Object.keys(ifd_dict).length;
+	const entry_header = pack(">H", [tag_count]);
+	let entries_length;
+	if (["0th", "1st"].indexOf(ifd) > -1) {
+		entries_length = 2 + tag_count * 12 + 4;
+	} else {
+		entries_length = 2 + tag_count * 12;
+	}
+	let entries = "";
+	let values = "";
+	let key;
+
+	for (key in ifd_dict) {
+		if (typeof (key) == "string") {
+			key = parseInt(key);
+		}
+		if ((ifd == "0th") && ([34665, 34853].indexOf(key) > -1)) {
+			continue;
+		} else if ((ifd == "Exif") && (key == 40965)) {
+			continue;
+		} else if ((ifd == "1st") && ([513, 514].indexOf(key) > -1)) {
+			continue;
+		}
+
+		var raw_value = ifd_dict[key];
+		var key_str = pack(">H", [key]);
+		var value_type = TAGS[ifd][key]["type"];
+		var type_str = pack(">H", [TYPES[value_type]]);
+
+		if (typeof (raw_value) == "number") {
+			raw_value = [raw_value];
+		}
+		var offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + values.length;
+		var b = _value_to_bytes(raw_value, value_type, offset);
+		var length_str = b[0];
+		var value_str = b[1];
+		var four_bytes_over = b[2];
+
+		entries += key_str + type_str + length_str + value_str;
+		values += four_bytes_over;
+	}
+
+	return [entry_header + entries, values];
+}
+
+
+
+class ExifReader {
+	tiftag = null
+	endian_mark = ''
+	constructor(data) {
+		let segments,
+			app1;
+		if (data.slice(0, 2) == "\xff\xd8") { // JPEG
+			segments = splitIntoSegments(data);
+			app1 = getExifSeg(segments);
+			if (app1) {
+				this.tiftag = app1.slice(10);
+			} 
+		} else if (["\x49\x49", "\x4d\x4d"].indexOf(data.slice(0, 2)) > -1) { // TIFF
+			this.tiftag = data;
+		} else if (data.slice(0, 4) == "Exif") { // Exif
+			this.tiftag = data.slice(6);
+		} else {
+			throw new Error("Given file is neither JPEG nor TIFF.");
+		}
+	}
+	get_ifd(pointer, ifd_name) {
+		let ifd_dict = {};
+		let tag_count = unpack(this.endian_mark + "H",
+			this.tiftag.slice(pointer, pointer + 2))[0];
+		const offset = pointer + 2;
+		let t;
+		if (["0th", "1st"].indexOf(ifd_name) > -1) {
+			t = "Image";
+		} else {
+			t = ifd_name;
+		}
+
+		for (let x = 0; x < tag_count; x++) {
+			pointer = offset + 12 * x;
+			const tag = unpack(this.endian_mark + "H",
+				this.tiftag.slice(pointer, pointer + 2))[0];
+			const value_type = unpack(this.endian_mark + "H",
+				this.tiftag.slice(pointer + 2, pointer + 4))[0];
+			const value_num = unpack(this.endian_mark + "L",
+				this.tiftag.slice(pointer + 4, pointer + 8))[0];
+			const value = this.tiftag.slice(pointer + 8, pointer + 12);
+
+			const v_set = [value_type, value_num, value];
+			if (tag in TAGS[t]) {
+				ifd_dict[tag] = this.convert_value(v_set);
+			}
+		}
+
+		if (ifd_name == "0th") {
+			pointer = offset + 12 * tag_count;
+			ifd_dict["first_ifd_pointer"] = this.tiftag.slice(pointer, pointer + 4);
+		}
+
+		return ifd_dict;
+	}
+
+	convert_value(val) {
+		let data = null;
+		const t = val[0];
+		const length = val[1];
+		const value = val[2];
+		let pointer;
+
+		if (t == 1) { // BYTE
+			if (length > 4) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = unpack(this.endian_mark + nStr("B", length),
+					this.tiftag.slice(pointer, pointer + length));
+			} else {
+				data = unpack(this.endian_mark + nStr("B", length), value.slice(0, length));
+			}
+		} else if (t == 2) { // ASCII
+			if (length > 4) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = this.tiftag.slice(pointer, pointer + length - 1);
+			} else {
+				data = value.slice(0, length - 1);
+			}
+		} else if (t == 3) { // SHORT
+			if (length > 2) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = unpack(this.endian_mark + nStr("H", length),
+					this.tiftag.slice(pointer, pointer + length * 2));
+			} else {
+				data = unpack(this.endian_mark + nStr("H", length),
+					value.slice(0, length * 2));
+			}
+		} else if (t == 4) { // LONG
+			if (length > 1) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = unpack(this.endian_mark + nStr("L", length),
+					this.tiftag.slice(pointer, pointer + length * 4));
+			} else {
+				data = unpack(this.endian_mark + nStr("L", length),
+					value);
+			}
+		} else if (t == 5) { // RATIONAL
+			pointer = unpack(this.endian_mark + "L", value)[0];
+			if (length > 1) {
+				data = [];
+				for (let x = 0; x < length; x++) {
+					data.push([unpack(this.endian_mark + "L",
+						this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0],
+					unpack(this.endian_mark + "L",
+						this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0]
+					]);
+				}
+			} else {
+				data = [unpack(this.endian_mark + "L",
+					this.tiftag.slice(pointer, pointer + 4))[0],
+				unpack(this.endian_mark + "L",
+					this.tiftag.slice(pointer + 4, pointer + 8))[0]
+				];
+			}
+		} else if (t == 7) { // UNDEFINED BYTES
+			if (length > 4) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = this.tiftag.slice(pointer, pointer + length);
+			} else {
+				data = value.slice(0, length);
+			}
+		} else if (t == 9) { // SLONG
+			if (length > 1) {
+				pointer = unpack(this.endian_mark + "L", value)[0];
+				data = unpack(this.endian_mark + nStr("l", length),
+					this.tiftag.slice(pointer, pointer + length * 4));
+			} else {
+				data = unpack(this.endian_mark + nStr("l", length),
+					value);
+			}
+		} else if (t == 10) { // SRATIONAL
+			pointer = unpack(this.endian_mark + "L", value)[0];
+			if (length > 1) {
+				data = [];
+				for (let x = 0; x < length; x++) {
+					data.push([unpack(this.endian_mark + "l",
+						this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0],
+					unpack(this.endian_mark + "l",
+						this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0]
+					]);
+				}
+			} else {
+				data = [unpack(this.endian_mark + "l",
+					this.tiftag.slice(pointer, pointer + 4))[0],
+				unpack(this.endian_mark + "l",
+					this.tiftag.slice(pointer + 4, pointer + 8))[0]
+				];
+			}
+		} else {
+			throw new Error("Exif might be wrong. Got incorrect value " +
+				"type to decode. type:" + t);
+		}
+
+		if ((data instanceof Array) && (data.length == 1)) {
+			return data[0];
+		} else {
+			return data;
+		}
+	}
+
+}
+
+
+function pack(mark, array) {
+	if (!(array instanceof Array)) {
+		throw new Error("'pack' error. Got invalid type argument.");
+	}
+	if ((mark.length - 1) != array.length) {
+		throw new Error("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements.");
+	}
+
+	let littleEndian;
+	if (mark[0] == "<") {
+		littleEndian = true;
+	} else if (mark[0] == ">") {
+		littleEndian = false;
+	} else {
+		throw new Error("");
+	}
+	let packed = "";
+	let p = 1;
+	let val = null;
+	let c = null;
+	let valStr = null;
+
+	while (c = mark[p]) {
+		if (c.toLowerCase() == "b") {
+			val = array[p - 1];
+			if ((c == "b") && (val < 0)) {
+				val += 0x100;
+			}
+			if ((val > 0xff) || (val < 0)) {
+				throw new Error("'pack' error.");
+			} else {
+				valStr = String.fromCharCode(val);
+			}
+		} else if (c == "H") {
+			val = array[p - 1];
+			if ((val > 0xffff) || (val < 0)) {
+				throw new Error("'pack' error.");
+			} else {
+				valStr = String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) +
+					String.fromCharCode(val % 0x100);
+				if (littleEndian) {
+					valStr = valStr.split("").reverse().join("");
+				}
+			}
+		} else if (c.toLowerCase() == "l") {
+			val = array[p - 1];
+			if ((c == "l") && (val < 0)) {
+				val += 0x100000000;
+			}
+			if ((val > 0xffffffff) || (val < 0)) {
+				throw new Error("'pack' error.");
+			} else {
+				valStr = String.fromCharCode(Math.floor(val / 0x1000000)) +
+					String.fromCharCode(Math.floor((val % 0x1000000) / 0x10000)) +
+					String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) +
+					String.fromCharCode(val % 0x100);
+				if (littleEndian) {
+					valStr = valStr.split("").reverse().join("");
+				}
+			}
+		} else {
+			throw new Error("'pack' error.");
+		}
+
+		packed += valStr;
+		p += 1;
+	}
+
+	return packed;
+}
+
+
+function unpack(mark, str) {
+	if (typeof (str) != "string") {
+		throw new Error("'unpack' error. Got invalid type argument.");
+	}
+	let l = 0;
+	for (let markPointer = 1; markPointer < mark.length; markPointer++) {
+		if (mark[markPointer].toLowerCase() == "b") {
+			l += 1;
+		} else if (mark[markPointer].toLowerCase() == "h") {
+			l += 2;
+		} else if (mark[markPointer].toLowerCase() == "l") {
+			l += 4;
+		} else {
+			throw new Error("'unpack' error. Got invalid mark.");
+		}
+	}
+
+	if (l != str.length) {
+		throw new Error("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length);
+	}
+
+	let littleEndian;
+	if (mark[0] == "<") {
+		littleEndian = true;
+	} else if (mark[0] == ">") {
+		littleEndian = false;
+	} else {
+		throw new Error("'unpack' error.");
+	}
+	let unpacked = [];
+	let strPointer = 0;
+	let p = 1;
+	let val = null;
+	let c = null;
+	let length = null;
+	let sliced = "";
+
+	while (c = mark[p]) {
+		if (c.toLowerCase() == "b") {
+			length = 1;
+			sliced = str.slice(strPointer, strPointer + length);
+			val = sliced.charCodeAt(0);
+			if ((c == "b") && (val >= 0x80)) {
+				val -= 0x100;
+			}
+		} else if (c == "H") {
+			length = 2;
+			sliced = str.slice(strPointer, strPointer + length);
+			if (littleEndian) {
+				sliced = sliced.split("").reverse().join("");
+			}
+			val = sliced.charCodeAt(0) * 0x100 +
+				sliced.charCodeAt(1);
+		} else if (c.toLowerCase() == "l") {
+			length = 4;
+			sliced = str.slice(strPointer, strPointer + length);
+			if (littleEndian) {
+				sliced = sliced.split("").reverse().join("");
+			}
+			val = sliced.charCodeAt(0) * 0x1000000 +
+				sliced.charCodeAt(1) * 0x10000 +
+				sliced.charCodeAt(2) * 0x100 +
+				sliced.charCodeAt(3);
+			if ((c == "l") && (val >= 0x80000000)) {
+				val -= 0x100000000;
+			}
+		} else {
+			throw new Error("'unpack' error. " + c);
+		}
+
+		unpacked.push(val);
+		strPointer += length;
+		p += 1;
+	}
+
+	return unpacked;
+}
+
+
+function nStr(ch, num) {
+	let str = "";
+	for (let i = 0; i < num; i++) {
+		str += ch;
+	}
+	return str;
+}
+
+
+function splitIntoSegments(data) {
+	if (data.slice(0, 2) != "\xff\xd8") {
+		throw new Error("Given data isn't JPEG.");
+	}
+
+	let head = 2;
+	const segments = ["\xff\xd8"];
+	while (true) {
+		if (data.slice(head, head + 2) == "\xff\xda") {
+			segments.push(data.slice(head));
+			break;
+		} else {
+			var length = unpack(">H", data.slice(head + 2, head + 4))[0];
+			var endPoint = head + length + 2;
+			segments.push(data.slice(head, endPoint));
+			head = endPoint;
+		}
+
+		if (head >= data.length) {
+			throw new Error("Wrong JPEG data.");
+		}
+	}
+	return segments;
+}
+
+
+function getExifSeg(segments) {
+	let seg;
+	for (let i = 0; i < segments.length; i++) {
+		seg = segments[i];
+		if (seg.slice(0, 2) == "\xff\xe1" &&
+			seg.slice(4, 10) == "Exif\x00\x00") {
+			return seg;
+		}
+	}
+	return null;
+}
+
+
+function mergeSegments(segments, exif) {
+	let hasExifSegment = false;
+	let additionalAPP1ExifSegments = [];
+
+	segments.forEach(function (segment, i) {
+		// Replace first occurence of APP1:Exif segment
+		if (segment.slice(0, 2) == "\xff\xe1" &&
+			segment.slice(4, 10) == "Exif\x00\x00"
+		) {
+			if (!hasExifSegment) {
+				segments[i] = exif;
+				hasExifSegment = true;
+			} else {
+				additionalAPP1ExifSegments.unshift(i);
+			}
+		}
+	});
+
+	// Remove additional occurences of APP1:Exif segment
+	additionalAPP1ExifSegments.forEach(function (segmentIndex) {
+		segments.splice(segmentIndex, 1);
+	});
+
+	if (!hasExifSegment && exif) {
+		segments = [segments[0], exif].concat(segments.slice(1));
+	}
+
+	return segments.join("");
+}
+
+

+ 27 - 0
uni_modules/lime-shared/platform/index.ts

xqd
@@ -0,0 +1,27 @@
+// @ts-nocheck
+declare var tt: Uni
+declare var swan: Uni
+declare var my: Uni
+declare var dd: Uni
+declare var ks: Uni
+declare var jd: Uni
+declare var qa: Uni
+declare var qq: Uni
+declare var qh: Uni
+declare var qq: Uni
+
+export function platform(): Uni | WechatMiniprogram.Wx {
+	const UNDEFINED = 'undefined'
+	if(typeof wx !== UNDEFINED) return wx // 微信
+	if(typeof tt !== UNDEFINED) return tt // 字节 飞书
+	if(typeof swan !== UNDEFINED) return swan // 百度
+	if(typeof my !== UNDEFINED) return my // 支付宝
+	if(typeof dd !== UNDEFINED) return dd // 钉钉
+	if(typeof ks !== UNDEFINED) return ks // 快手
+	if(typeof jd !== UNDEFINED) return jd // 京东
+	if(typeof qa !== UNDEFINED) return qa // 快应用
+	if(typeof qq !== UNDEFINED) return qq // qq
+	if(typeof qh !== UNDEFINED) return qh // 360
+	if(typeof uni !== UNDEFINED) return uni
+	return null
+}

+ 23 - 0
uni_modules/lime-shared/raf/index.ts

xqd
@@ -0,0 +1,23 @@
+// @ts-nocheck
+import {isBrowser} from '../isBrowser'
+
+// Keep forward compatible
+// should be removed in next major version
+export const supportsPassive = true;
+
+export function raf(fn: FrameRequestCallback): number | NodeJS.Timeout {
+  return isBrowser ? requestAnimationFrame(fn) : setTimeout(fn, 1000 / 30)
+}
+
+export function cancelRaf(id: number) {
+  if (isBrowser) {
+    cancelAnimationFrame(id);
+  } else {
+	clearTimeout(id);  
+  }
+}
+
+// double raf for animation
+export function doubleRaf(fn: FrameRequestCallback): void {
+  raf(() => raf(fn));
+}

+ 14 - 0
uni_modules/lime-shared/random/index.ts

xqd
@@ -0,0 +1,14 @@
+// @ts-nocheck
+/**
+ * 返回区域内随机数
+ * @param min 最小值
+ * @param max 最大值
+ * @param fixed 保留几位小数,默认0
+ * @returns
+ */
+export function random(min: number, max: number, fixed: number = 0) {
+  min = +min || 0;
+  max = +max || 0;
+  const num = Math.random() * (max - min) + min
+  return fixed == 0 ? Math.round(num) : Number(num.toFixed(fixed))
+}

+ 21 - 0
uni_modules/lime-shared/range/index.ts

xqd
@@ -0,0 +1,21 @@
+/**
+ * 生成指定范围的数组 
+ *
+ * @private
+ * @param {number} start 开始的范围.
+ * @param {number} end 结束的范围.
+ * @param {number} step 范围的增量 或者 减量.
+ * @param {boolean} [fromRight] 反向.
+ * @returns {Array} Returns 范围数组.
+ */
+export function range(start: number, end: number, step: number = 1, fromRight: boolean = false): number[] {
+  let index = -1
+  let length = Math.max(Math.ceil((end - start) / (step || 1)), 0)
+  const result = new Array(length)
+
+  while (length--) {
+    result[fromRight ? length : ++index] = start
+    start += step
+  }
+  return result
+}

+ 191 - 0
uni_modules/lime-shared/readme.md

xqd
@@ -0,0 +1,191 @@
+# lime-shared 工具库
+- 本人插件的几个公共函数
+
+### 引入
+```js
+// 按需引入
+// 这种只会引入相关的方法
+import {getRect} from '@/uni_modules/lime-shared/getRect'
+
+// 全量引入
+// 这种引入方式,会全量打包
+import {getRect} from '@/uni_modules/lime-shared'
+```
+
+
+#### getRect
+- 返回节点尺寸信息
+
+```js
+// 组件内需要传入上下文
+// 如果是nvue 则需要在节点上加与id或class同名的ref
+getRect('#id',{context: this}).then(res => {})
+```
+
+#### addUnit 
+- 将未带单位的数值添加px,如果有单位则返回原值
+
+```js
+addUnit(10)
+// 10px
+```
+
+#### unitConvert
+- 将带有rpx|px的字符转成number,若本身是number则直接返回
+
+```js
+unitConvert('10rpx') 
+// 5 设备不同 返回的值也不同
+unitConvert('10px') 
+// 10
+unitConvert(10) 
+// 10
+```
+
+#### canIUseCanvas2d
+- 环境是否支持使用 canvas 2d
+
+```js
+canIUseCanvas2d()
+// 若支持返回 true 否则 false
+```
+
+
+#### getCurrentPage
+- 获取当前页
+
+```js
+const page = getCurrentPage()
+```
+
+
+#### base64ToPath
+- 把base64的图片转成临时路径
+
+```js
+base64ToPath(`xxxxx`).then(res => {})
+```
+
+#### pathToBase64
+- 把图片的临时路径转成base64
+
+```js
+pathToBase64(`xxxxx/xxx.png`).then(res => {})
+```
+
+#### sleep
+- 睡眠,让 async 内部程序等待一定时间后再执行
+
+```js
+async next () => {
+	await sleep(300)
+	console.log('limeui');
+}
+```
+
+#### isBase64
+- 判断字符串是否为base64
+
+```js
+isBase64('xxxxx')
+```
+
+#### throttle
+- 节流
+
+```js
+throttle((nama) => {console.log(nama)}, 200)('limeui');
+```
+
+#### debounce
+- 防抖
+
+```js
+debounce((nama) => {console.log(nama)}, 200)('limeui');
+```
+
+#### random
+- 返回指定范围的随机数
+
+```js
+random(1, 5);
+```
+
+#### range
+- 生成区间数组
+
+```js
+range(0, 5)
+// [0,1,2,3,4,5]
+```
+
+### clamp
+- 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回
+
+```js
+clamp(0, 10, -1)
+// 0
+clamp(0, 10, 11)
+// 10
+clamp(0, 10, 9)
+// 9
+```
+
+#### fillZero
+- 补零,如果传入的是`个位数`则在前面补0
+
+```js
+fillZero(9);
+// 09
+```
+
+#### exif
+- 获取图片exif
+- 支持临时路径、base64
+
+```js
+uni.chooseImage({
+	count: 1, //最多可以选择的图片张数
+	sizeType: "original",
+	success: (res) => {
+		exif.getData(res.tempFiles[0], function() {
+			let tagj = exif.getTag(this, "GPSLongitude");
+			let	Orientation = exif.getTag(this, 'Orientation');  
+			console.log(tagj, Orientation)
+		})
+	}
+})
+```
+
+#### selectComponent
+- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件)
+- 仅vue3,vue2没有测试过
+
+```js
+// 当前页面
+const page = getCurrentPage()
+selectComponent('.custom', {context: page}).then(res => {
+})
+```
+
+
+### createAnimation
+- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue
+
+```html
+<view ref="ball" :animation="animationData"></view>
+```
+```js
+const ball = ref(null)
+const animation = createAnimation({
+  transformOrigin: "50% 50%",
+  duration: 1000,
+  timingFunction: "ease",
+  delay: 0
+})
+
+animation.scale(2,2).rotate(45).step()
+// nvue 无导出数据,这样写只为了平台一致,
+// nvue 需要把 ref 传入,其它平台不需要
+const animationData = animation.export(ball.value)
+```

+ 139 - 0
uni_modules/lime-shared/selectComponent/index.ts

xqd
@@ -0,0 +1,139 @@
+// @ts-nocheck
+interface SelectOptions {
+	context?: any
+	needAll?: boolean
+	node?: boolean
+}
+// #ifdef MP
+function selectMPComponent(key: string, name: string, context: any, needAll: boolean) {
+	const {proxy, $vm} = context
+	context = $vm || proxy
+	if(key !== 'refs') {
+		const queue = [context]
+		let result = null
+		const selector = (key == 'id' ? '#': '.') + name;
+		while(queue.length > 0) {
+			const child = queue.shift();
+			const flag = child?.selectComponent(selector)
+			if(flag) {
+				if(!needAll) {return result = flag.$vm}
+				return result = child.selectAllComponents(selector).map(item => item.$vm)
+			} else {
+				child.$children && (queue.push(...child.$children));
+			}
+		}
+		return result
+	} else {
+		const {$templateRefs} = context.$
+		const nameMap = {}
+		for (var i = 0; i < $templateRefs.length; i++) {
+			const item = $templateRefs[i]
+			nameMap[item.i] = item.r
+		}
+		let result = []
+		if(context.$children.length) {
+			const queue = [...context.$children]
+			while(queue.length > 0) {
+				const child = queue.shift();
+				if(child.$refs && child.$refs[name]) {
+					result = child.$refs[name]
+				} else if(nameMap[child.id] === name){
+					result.push(child)
+				}  else {
+					child.$children && (queue.push(...child.$children));
+				}
+				if(result.length && !needAll) {
+					return;
+				}
+			}
+		}
+		return needAll ? result : result[0]
+	}
+}
+// #endif
+// #ifdef H5
+function selectH5Component(key: string, name: string, context: any, needAll: boolean) {
+	const {_, component } = context
+	const child = {component: _ || component || context, children: null , subTree: null, props: null}
+	let result = []
+	let queue = [child]
+	while(queue.length > 0 ) {
+		const child = queue.shift()
+		const {component, children , props, subTree} = child
+		if(key === 'refs' && component && (props?.ref == name || component[key][name])) {
+			if(props?.ref == name) {
+				result.push(component.exposed)
+			} else if(component[key][name]) {
+				result.push(component[key][name])
+			}
+		} else if(key !== 'refs' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) {
+			result.push(component.exposed)
+		} else if(children && Array.isArray(children)) {
+			queue.push(...children)
+		} else if(!component && subTree) {
+			queue.push(subTree)
+		} else if(component?.subTree) {
+			queue.push(component.subTree)
+		}
+		if(result.length && !needAll) {
+			break
+		}
+	}
+	return needAll ? result : result[0]
+}
+// #endif
+// #ifdef APP
+function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) {
+	let result = []
+	// const {_, component} = context
+	// const child = {component: _ || component || context, children: null, props: null, subTree: null}
+	const queue = [context]
+	while(queue.length > 0) {
+		const child = queue.shift()
+		const {component, children, props, subTree} = child
+		const isComp = component && props && component.exposed && !node
+		if(props?.[key] === name && node) {
+			result.push(child)
+		} else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) {
+			result.push(component.exposed)
+		} else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) {
+			result.push(component.exposed)
+		}
+		// else if(component && component.subTree && Array.isArray(component.subTree.children)){
+		// 	queue.push(...component.subTree.children)
+		// }
+		else if(subTree) {
+			queue.push(subTree)
+		} else if(component && component.subTree){
+			queue.push(component.subTree)
+		} 
+		 else if(children && Array.isArray(children)) {
+			queue.push(...children)
+		}
+		if(result.length && !needAll) {
+			break;
+		}
+	}
+	return needAll ? result : result[0]
+}
+// #endif
+export function selectComponent(selector: string, options: SelectOptions = {}) {
+	// . class
+	// # id
+	// $ ref
+	const reg = /^(\.|#|\$)([a-zA-Z_0-9\-]+)$/;
+	if(!reg.test(selector)) return null
+	let { context, needAll, node} = options
+	const [,prefix, name] = selector.match(reg)
+	const key = prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref';
+	
+	// #ifdef MP
+	return selectMPComponent(key, name, context, needAll)
+	// #endif
+	// #ifdef H5
+	return selectH5Component(key, name, context, needAll)
+	// #endif
+	// #ifdef APP
+	return selectAPPComponent(key, name, context, needAll, node)
+	// #endif
+}

+ 5 - 0
uni_modules/lime-shared/sleep/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**
+ * @param delay 延时时间
+ */
+export const sleep = (delay: number = 300) => new Promise(resolve => setTimeout(resolve, delay))

+ 19 - 0
uni_modules/lime-shared/throttle/index.ts

xqd
@@ -0,0 +1,19 @@
+// @ts-nocheck
+/**
+ * 节流
+ * @param fn 回调函数
+ * @param delay 延迟时间
+ * @returns
+ */
+export function throttle(fn: (...args: any[]) => void, delay: number) {
+  let flag = true;
+  return (...args: any[]) => {
+    if (flag) {
+      flag = false;
+      fn(...args);
+      setTimeout(() => {
+        flag = true;
+      }, delay);
+    }
+  };
+}

+ 5 - 0
uni_modules/lime-shared/toArray/index.ts

xqd
@@ -0,0 +1,5 @@
+// @ts-nocheck
+/**
+ * 转成数组
+ * */
+export const toArray = <T>(item: T | T[]): T[] => Array.isArray(item) ? item : [item];

+ 6 - 0
uni_modules/lime-shared/toNumber/index.ts

xqd
@@ -0,0 +1,6 @@
+// @ts-nocheck
+/** 将数字字符转成数字类型 */
+export function toNumber(val: string): number | string {
+  const n = parseFloat(val)
+  return isNaN(n) ? val : n
+}

+ 37 - 0
uni_modules/lime-shared/unitConvert/index.ts

xqd
@@ -0,0 +1,37 @@
+// @ts-nocheck
+import {isString} from '../isString'
+import {isNumeric} from '../isNumeric'
+// export const unitConvert = (value: number | string): number => {
+//   if (typeof value === 'string') {
+//     if (value.includes('rpx')) {
+//       return (parseInt(value, 10) * (systemInfo?.screenWidth ?? 750)) / 750;
+//     }
+//     return parseInt(value, 10);
+//   }
+//   return value;
+// };
+
+/**
+ * 将 rpx | px 转成 number
+ */
+export function unitConvert(value: string | number) : number{
+	// 如果是字符串数字
+	if (isNumeric(value)) {return Number(value)}
+	// 如果有单位
+	if (isString(value)) {
+		const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
+		const results = reg.exec(value);
+		if (!value || !results) {
+			return 0;
+		}
+		const unit = results[3];
+		value = parseFloat(value);
+		if (unit === 'rpx') {
+			return uni.upx2px(value);
+		} 
+		if (unit === 'px') {
+			return value * 1;
+		} 
+	}
+	return 0
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است