1
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
## 1.0.6(2025-08-17)
|
||||
* 兼容原生鸿蒙
|
||||
## 1.0.3(2025-04-13)
|
||||
* 安卓端修改了布局结构现会覆盖官方的dialog-page了并且会自动隐藏状态栏,并可以覆盖底部安全区域
|
||||
## 1.0.2(2025-02-23)
|
||||
* 安卓添加了显示背景遮罩动画,及关闭遮罩动画
|
||||
* ios添加了显示背景遮罩动画,及关闭遮罩动画
|
||||
* 上述添加后,不管是显示和关闭都更加的自然
|
||||
## 1.0.1(2024-09-06)
|
||||
* 更新支持Uniapp
|
||||
## 1.0.0(2024-08-06)
|
||||
安卓,ios,web可用。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"id": "x-loading-s",
|
||||
"displayName": "全屏加载,可覆盖系统界面任意位置调用",
|
||||
"version": "1.0.6",
|
||||
"description": "平替官方uni.showLoading",
|
||||
"keywords": [
|
||||
"uts,tmui,uappx,原生插件"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.8",
|
||||
"uni-app": "",
|
||||
"uni-app-x": "^4.75"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "uts",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "1.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "√"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "-",
|
||||
"vue3": "-"
|
||||
},
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"vue": "-",
|
||||
"nvue": "-",
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-",
|
||||
"alipay": "-",
|
||||
"toutiao": "-",
|
||||
"baidu": "-",
|
||||
"kuaishou": "-",
|
||||
"jd": "-",
|
||||
"harmony": "-",
|
||||
"qq": "-",
|
||||
"lark": "-"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"android": {
|
||||
"extVersion": "",
|
||||
"minVersion": "21"
|
||||
},
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
# x-loading-s
|
||||
|
||||
### 特性说明
|
||||
|
||||
主要是用来代替uni.showLoading的,相比优化有:
|
||||
|
||||
1. 高自动适应,因此可以添加超长的title文字。
|
||||
2. 图标可以任意更新,大小,颜色等
|
||||
3. 可以添加title标题,允许改变字号,颜色
|
||||
4. 支持渐隐动画效果,包括遮罩层渐入和内容缩放
|
||||
5. 遮罩层会阻止点击穿透,保护底层内容
|
||||
|
||||
### 兼容性
|
||||
|
||||
| Harmony | IOS | Android | WEB | 小程序 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
|
||||
### 开发文档
|
||||
|
||||
**浏览器端需要你把目录中的```remixicon.ttf```复制到static目录下**
|
||||
|
||||
调用:
|
||||
```ts
|
||||
import { showLoading, hideXloading, XLOADINGS_TYPE } from "@/uni_modules/x-loading-s"
|
||||
// 显示,字段参数全是可选的
|
||||
showLoading({} as XLOADINGS_TYPE)
|
||||
// 或者,null使用默认值
|
||||
showLoading(null)
|
||||
// 隐藏
|
||||
hideXloading()
|
||||
|
||||
```
|
||||
|
||||
参数配置:
|
||||
|
||||
下面的图标iconCode,同组件库x-icon是相同的字体图标,请打开图标网站:[查看](https://remixicon.com/)
|
||||
找到直接想要的图标后 复制 unicode ,不是名称。比如:我们只要取ED29
|
||||
下面的参数图标iconCode就是ED29 **iconCode:'ED29'**
|
||||
|
||||
**XLOADINGS_TYPE**
|
||||
```ts
|
||||
// 所有参数都是可选的
|
||||
export type XLOADINGS_TYPE = {
|
||||
// 图标颜色,默认:#2979ff
|
||||
iconColor?:string,
|
||||
// 内容背景色,默认:#fff
|
||||
contentBgColor?:string,
|
||||
// 遮罩层背景色,默认:rgba(0,0,0,0.3)
|
||||
maskBgColor?:string,
|
||||
// 图标大小,默认:42
|
||||
iconSize?:number,
|
||||
// 图标编码,默认:ED29
|
||||
iconCode?:string,
|
||||
// 提示文字
|
||||
title?:string,
|
||||
// 文字大小,默认:28
|
||||
titleSize?:number,
|
||||
// 文字颜色,默认:#333
|
||||
titleColor?:string,
|
||||
// 约定的宽,最小高值100,宽100,默认:220
|
||||
size?:number,
|
||||
}
|
||||
```
|
||||
|
||||
### 显示和隐藏时机
|
||||
|
||||
- 组件会在页面ready后显示
|
||||
- 页面卸载时会自动隐藏
|
||||
- 可以通过hideXloading()手动隐藏
|
||||
|
||||
### 动画效果
|
||||
|
||||
- 显示时有300ms的渐入动画
|
||||
- 遮罩层透明度从0到1渐变
|
||||
- 内容区域有缩放和透明度渐变效果
|
||||
- 加载图标有持续旋转动画
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
import LinearLayout from 'android.widget.LinearLayout';
|
||||
import Context from 'android.content.Context'
|
||||
import RelativeLayout from 'android.widget.RelativeLayout';
|
||||
import ViewGroup from 'android.view.ViewGroup';
|
||||
import View from 'android.view.View';
|
||||
import { XLOADINGS_TYPE, XLOADINGS_TYPE_PRIVATE } from "../interface.uts"
|
||||
import Color from 'android.graphics.Color';
|
||||
import Gravity from 'android.view.Gravity';
|
||||
import GradientDrawable from 'android.graphics.drawable.GradientDrawable';
|
||||
import TextView from 'android.widget.TextView';
|
||||
import Typeface from 'android.graphics.Typeface';
|
||||
import TextUtils from 'android.text.TextUtils';
|
||||
import Uri from 'android.net.Uri';
|
||||
import ObjectAnimator from 'android.animation.ObjectAnimator';
|
||||
import ValueAnimator from 'android.animation.ValueAnimator';
|
||||
import TimeInterpolator from 'android.animation.TimeInterpolator';
|
||||
import MotionEvent from 'android.view.MotionEvent';
|
||||
import { hexToRgb } from "../libs/color.uts"
|
||||
import { defaultConfig, configCover } from "../libs/config.uts"
|
||||
import ViewPropertyAnimator from 'android.view.ViewPropertyAnimator';
|
||||
import AccelerateDecelerateInterpolator from 'android.view.animation.AccelerateDecelerateInterpolator';
|
||||
// #ifdef UNI-APP-X
|
||||
import getCurrentPages from 'io.dcloud.uniapp.framework.getCurrentPages';
|
||||
import onReady from 'io.dcloud.uniapp.framework.onReady';
|
||||
import onUnload from 'io.dcloud.uniapp.framework.onUnload';
|
||||
import onShow from 'io.dcloud.uniapp.framework.onShow';
|
||||
// #endif
|
||||
|
||||
import AttributeSet from 'android.util.AttributeSet';
|
||||
import R from 'android.R';
|
||||
import Bundle from "android.os.Bundle";
|
||||
import Dialog from "android.app.Dialog";
|
||||
import WindowManager from "android.view.WindowManager";
|
||||
import Window from "android.view.Window";
|
||||
import ColorDrawable from "android.graphics.drawable.ColorDrawable";
|
||||
let rotationAnimator:ObjectAnimator|null = null;
|
||||
let maskDomId = 5894
|
||||
function px2dp(n : number) : number {
|
||||
|
||||
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
|
||||
|
||||
return mets.density * n
|
||||
}
|
||||
function setSpin(textView : TextView) {
|
||||
try {
|
||||
textView.clearAnimation();
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
}
|
||||
let rotationStart = 0
|
||||
let rotation = 360
|
||||
let rotationSFD = 0.5
|
||||
if(rotationAnimator!=null){
|
||||
rotationAnimator!.cancel()
|
||||
}
|
||||
rotationAnimator = ObjectAnimator.ofFloat(textView, "rotation", rotationStart.toFloat(), rotation.toFloat());
|
||||
if(rotationAnimator==null) return;
|
||||
rotationAnimator!.setDuration(1000);
|
||||
class LinearInterpolator implements TimeInterpolator {
|
||||
override getInterpolation(input : Float) : Float {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
rotationAnimator!.setInterpolator(new LinearInterpolator());
|
||||
rotationAnimator!.setRepeatCount(ValueAnimator.INFINITE);
|
||||
rotationAnimator!.start();
|
||||
}
|
||||
let dialogModal : FullScreenDialogFragment | null = null;
|
||||
class FullScreenDialogFragment extends Dialog {
|
||||
private mContext : Context;
|
||||
private opts : XLOADINGS_TYPE|null = null;
|
||||
private maskeDom:RelativeLayout|null = null;
|
||||
constructor(context : Context, optsOwer : XLOADINGS_TYPE|null) {
|
||||
super(context,R.style.Theme_Translucent_NoTitleBar_Fullscreen);
|
||||
this.mContext = context;
|
||||
this.opts = optsOwer
|
||||
}
|
||||
override onCreate(savedInstanceState ?: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
// 禁止按键返回及侧滑返回.
|
||||
this.setCancelable(false)
|
||||
this.maskeDom = _createToastView(this.getContext(),this.window!.decorView! as ViewGroup,this.opts)
|
||||
// 设置窗口属性
|
||||
let window = this.getWindow();
|
||||
if (window != null) {
|
||||
// 首先设置全屏标志
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// 设置布局参数为全屏
|
||||
window.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT
|
||||
);
|
||||
|
||||
// 设置背景透明
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
window.setGravity(Gravity.CENTER);
|
||||
|
||||
// 完整的沉浸式设置
|
||||
let decorView = window.getDecorView();
|
||||
let option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
decorView.setSystemUiVisibility(option);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
window.setStatusBarColor(Color.TRANSPARENT)
|
||||
window.setNavigationBarColor(Color.TRANSPARENT)
|
||||
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL)
|
||||
}
|
||||
|
||||
}
|
||||
override dismiss(){
|
||||
super.dismiss()
|
||||
this.maskeDom = null;
|
||||
dialogModal = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function hideXloading() {
|
||||
UTSAndroid.getDispatcher("main").async(function (_) {
|
||||
dialogModal?.dismiss()
|
||||
dialogModal = null;
|
||||
rotationAnimator?.cancel()
|
||||
rotationAnimator = null;
|
||||
})
|
||||
}
|
||||
|
||||
function _createToastView(context : Context, decorView : ViewGroup, opts : XLOADINGS_TYPE|null):RelativeLayout{
|
||||
|
||||
let realopts = opts == null ? ({} as XLOADINGS_TYPE) : (opts!)
|
||||
let config = configCover(realopts)
|
||||
let rmbgColor = hexToRgb(config.maskBgColor);
|
||||
let textColor = hexToRgb(config.iconColor);
|
||||
let wrapColor = hexToRgb(config.contentBgColor);
|
||||
let titleColorRgb = hexToRgb(config.titleColor);
|
||||
|
||||
let mbgColor = rmbgColor == null ? Color.parseColor(config.maskBgColor) : Color.argb((rmbgColor!.a * 255).toInt(), rmbgColor!.r.toInt(), rmbgColor!.g.toInt(), rmbgColor!.b.toInt())
|
||||
let fontColor = textColor == null ? Color.parseColor(config.iconColor) : Color.argb((textColor!.a * 255).toInt(), textColor!.r.toInt(), textColor!.g.toInt(), textColor!.b.toInt())
|
||||
let contentColor = rmbgColor == null ? Color.parseColor(config.contentBgColor) : Color.argb((wrapColor!.a * 255).toInt(), wrapColor!.r.toInt(), wrapColor!.g.toInt(), wrapColor!.b.toInt())
|
||||
let titleColor = titleColorRgb == null ? Color.parseColor(config.titleColor) : Color.argb((titleColorRgb!.a * 255).toInt(), titleColorRgb!.r.toInt(), titleColorRgb!.g.toInt(), titleColorRgb!.b.toInt())
|
||||
|
||||
let maskerDom = new RelativeLayout(context)
|
||||
|
||||
class MaskerDomClickListsner extends View.OnTouchListener {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
override onTouch(view : View, event : MotionEvent) : Boolean {
|
||||
// 阻止事件向下传递。
|
||||
return true;
|
||||
}
|
||||
}
|
||||
maskerDom.setAlpha((0).toFloat())
|
||||
maskerDom.setOnTouchListener(new MaskerDomClickListsner())
|
||||
|
||||
let maskerDomLayrPrams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
maskerDom.setLayoutParams(maskerDomLayrPrams)
|
||||
maskerDom.setBackgroundColor(mbgColor)
|
||||
|
||||
|
||||
maskerDom.setGravity(Gravity.CENTER)
|
||||
maskerDom.setId((maskDomId).toInt())
|
||||
let contentWrapDiv = new LinearLayout(context);
|
||||
// 设置圆角背景
|
||||
let bg = new GradientDrawable()
|
||||
bg.setColor(contentColor)
|
||||
bg.setCornerRadius((px2dp(16)).toFloat())
|
||||
contentWrapDiv.setBackground(bg)
|
||||
// contentWrapDiv.setBackgroundColor(Color.parseColor(config.contentBgColor))
|
||||
let wpx = px2dp(config.size).toInt();
|
||||
let contentLays = new LinearLayout.LayoutParams(wpx,ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
contentWrapDiv.setLayoutParams(contentLays)
|
||||
contentWrapDiv.setMinimumHeight(wpx)
|
||||
let wpadingx = px2dp(16).toInt();
|
||||
contentWrapDiv.setPadding(wpadingx, wpadingx, wpadingx, wpadingx)
|
||||
contentWrapDiv.setGravity(Gravity.CENTER)
|
||||
contentWrapDiv.setOrientation(LinearLayout.VERTICAL)
|
||||
|
||||
// 创建图标。
|
||||
let iconDiv = new TextView(context)
|
||||
// iconDiv.setLineHeight(1)
|
||||
iconDiv.setTextColor(fontColor)
|
||||
let assetManager = context!.getAssets();
|
||||
let typeface = Typeface.createFromAsset(assetManager, "remixicon.ttf")
|
||||
iconDiv.setTypeface(typeface)
|
||||
iconDiv.setGravity(Gravity.CENTER)
|
||||
iconDiv.setTextSize(config.iconSize.toFloat())
|
||||
if (!TextUtils.isEmpty(config.iconCode)) {
|
||||
let codePoint = Integer.parseInt(config.iconCode, 16);
|
||||
let charArray = Character.toChars(codePoint);
|
||||
let text = new String(charArray);
|
||||
iconDiv.setText(text);
|
||||
}
|
||||
setSpin(iconDiv)
|
||||
|
||||
|
||||
|
||||
|
||||
contentWrapDiv.addView(iconDiv)
|
||||
|
||||
if (config.title != "") {
|
||||
// 添加一个title
|
||||
let titleDiv = new TextView(context)
|
||||
titleDiv.setText(config.title)
|
||||
titleDiv.setTextSize((config.titleSize).toFloat())
|
||||
titleDiv.setTextColor(titleColor)
|
||||
titleDiv.setGravity(Gravity.CENTER)
|
||||
contentWrapDiv.addView(titleDiv)
|
||||
}
|
||||
|
||||
maskerDom.addView(contentWrapDiv)
|
||||
|
||||
decorView.addView(maskerDom!)
|
||||
|
||||
try {
|
||||
let s0 = (0).toFloat()
|
||||
let s1 = (1).toFloat()
|
||||
contentWrapDiv.setAlpha(s0)
|
||||
contentWrapDiv.setScaleX(s0)
|
||||
contentWrapDiv.setScaleY(s0)
|
||||
|
||||
// 遮罩渐变
|
||||
const maskAni = maskerDom.animate() as ViewPropertyAnimator;
|
||||
maskAni.alpha(s1)
|
||||
.setDuration(300)
|
||||
.setInterpolator(AccelerateDecelerateInterpolator())
|
||||
.start()
|
||||
|
||||
let ani = contentWrapDiv.animate() as ViewPropertyAnimator;
|
||||
ani.alpha(s1)
|
||||
.scaleX(s1)
|
||||
.scaleY(s1)
|
||||
.setDuration(300)
|
||||
.setInterpolator(AccelerateDecelerateInterpolator())
|
||||
.start()
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
}
|
||||
|
||||
return maskerDom;
|
||||
}
|
||||
function _showLoading_(opts : XLOADINGS_TYPE | null) {
|
||||
dialogModal = new FullScreenDialogFragment(UTSAndroid.getUniActivity()!,opts)
|
||||
UTSAndroid.getDispatcher("main").async(function (_) {
|
||||
dialogModal?.show()
|
||||
})
|
||||
}
|
||||
|
||||
export function showLoading(opts : XLOADINGS_TYPE | null) {
|
||||
dialogModal?.dismiss()
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 0) {
|
||||
const page = pages[pages.length - 1].vm!
|
||||
const instance = page.$
|
||||
if (page.$isReady) {
|
||||
_showLoading_(opts)
|
||||
} else {
|
||||
hideXloading()
|
||||
}
|
||||
onUnload(() => {
|
||||
hideXloading()
|
||||
}, instance)
|
||||
}else{
|
||||
_showLoading_(opts)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"x_loading_s": "./lib/x_loading_s.har"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { XLOADINGS_TYPE, XLOADINGS_TYPE_PRIVATE } from "../interface.uts"
|
||||
import { defaultConfig, configCover } from "../libs/config.uts"
|
||||
import { hexToRgb } from "../libs/color.uts"
|
||||
import { xshowLoadings,xhidesloadings } from "x_loading_s";
|
||||
|
||||
|
||||
|
||||
export const hideXloading = () => {
|
||||
xhidesloadings(UTSHarmony.getCurrentWindow()!.getUIContext())
|
||||
}
|
||||
export function showLoading(opts : XLOADINGS_TYPE|null) {
|
||||
let config = configCover(opts==null ? ({} as XLOADINGS_TYPE) : (opts!))
|
||||
xshowLoadings(UTSHarmony.getCurrentWindow()!.getUIContext(),config)
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>remixicon.ttf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deploymentTarget": "12"
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import { XLOADINGS_TYPE, XLOADINGS_TYPE_PRIVATE } from "../interface.uts"
|
||||
import { defaultConfig, configCover } from "../libs/config.uts"
|
||||
import { UIView, UIViewController, NSLayoutConstraint, UITextView, UIFont, UILabel, NSTextAlignment, NSLineBreakMode } from 'UIKit';
|
||||
import { CGRect, CGPoint, CGFloat, CGSize, CFDictionary } from 'CoreFoundation';
|
||||
import { CGSizeMake } from 'CoreGraphics';
|
||||
import { Alignment } from 'SwiftUI';
|
||||
import { UInt32, KeyPath } from 'Swift';
|
||||
import { NSString } from 'Foundation';
|
||||
import { CABasicAnimation, CAMediaTimingFunction, CAMediaTimingFunctionName } from 'QuartzCore';
|
||||
|
||||
let maskerDom : null | UIView = null;
|
||||
|
||||
|
||||
export function showLoading(opts : XLOADINGS_TYPE | null) {
|
||||
DispatchQueue.main.async(execute = () : void => {
|
||||
if (maskerDom != null) {
|
||||
maskerDom!.removeFromSuperview()
|
||||
}
|
||||
let parent = UTSiOS.getCurrentViewController() as UIViewController;
|
||||
let parentView = parent.view as UIView
|
||||
|
||||
let realopts = opts == null ? ({} as XLOADINGS_TYPE) : (opts!)
|
||||
let config = configCover(realopts)
|
||||
|
||||
maskerDom = new UIView()
|
||||
|
||||
if (maskerDom == null) return;
|
||||
let cg = parentView.frame as CGRect
|
||||
maskerDom!.frame = cg
|
||||
maskerDom!.backgroundColor = UTSiOS.colorWithString(config.maskBgColor)
|
||||
|
||||
let contetDiv = new UIView();
|
||||
contetDiv.layer.cornerRadius = 16
|
||||
contetDiv.layer.masksToBounds = true;
|
||||
contetDiv.backgroundColor = UTSiOS.colorWithString(config.contentBgColor)
|
||||
|
||||
// contetDiv.frame = new CGRect(x = 0, y = 0, width = 100, height = 100)
|
||||
// contetDiv.center = new CGPoint(x = (cg.size.width) / 2, y = (cg.size.height) / 2)
|
||||
|
||||
let textView = new UILabel()
|
||||
textView.frame = new CGRect(x = 0, y = 0, width = config.iconSize.toInt(), height = config.iconSize.toInt())
|
||||
textView.font = new UIFont(name = "remixicon", size = new CGFloat(config.iconSize))
|
||||
|
||||
let codePoint = new UInt32(config.iconCode, radix = 16)
|
||||
let stricon = new NSString(format = "%C", codePoint!)
|
||||
|
||||
textView.text = new String(stricon);
|
||||
textView.textColor = UTSiOS.colorWithString(config.iconColor)
|
||||
textView.textAlignment = NSTextAlignment.center
|
||||
|
||||
|
||||
|
||||
// 添加动画图标旋转
|
||||
let spinAni = new CABasicAnimation(keyPath = "transform.rotation")
|
||||
spinAni.fromValue = 0
|
||||
spinAni.toValue = 2 * CGFloat.pi
|
||||
spinAni.duration = 1.1
|
||||
spinAni.repeatCount = HUGE
|
||||
textView.layer.add(spinAni, forKey = "rotationAnimation")
|
||||
// 添加内容缩放动画
|
||||
let scaleAni = new CABasicAnimation(keyPath = "transform.scale")
|
||||
scaleAni.fromValue = 0
|
||||
scaleAni.toValue = 1
|
||||
scaleAni.duration = 0.3
|
||||
scaleAni.timingFunction = new CAMediaTimingFunction(name = CAMediaTimingFunctionName.easeInEaseOut)
|
||||
contetDiv.layer.add(scaleAni, forKey = "scaleAnimation")
|
||||
|
||||
let opacityAni = new CABasicAnimation(keyPath = "opacity")
|
||||
opacityAni.fromValue = 0
|
||||
opacityAni.toValue = 1
|
||||
opacityAni.duration = 0.3
|
||||
opacityAni.timingFunction = new CAMediaTimingFunction(name = CAMediaTimingFunctionName.easeInEaseOut)
|
||||
|
||||
maskerDom!.layer.add(opacityAni, forKey = "opacityAnimation")
|
||||
|
||||
|
||||
|
||||
if (config.title != "") {
|
||||
textView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contetDiv.addSubview(textView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
// 顶部约束
|
||||
textView.topAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.topAnchor, constant = 16),
|
||||
// textView.bottomAnchor.constraint(equalTo= contetDiv.safeAreaLayoutGuide.bottomAnchor,constant=-16),
|
||||
// 左侧约束
|
||||
textView.leadingAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.leadingAnchor, constant = 16),
|
||||
// 右侧约束
|
||||
textView.trailingAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.trailingAnchor, constant = -16)
|
||||
])
|
||||
} else {
|
||||
textView.center = new CGPoint(x = 50, y = 50)
|
||||
contetDiv.addSubview(textView)
|
||||
}
|
||||
|
||||
|
||||
// 设置标题
|
||||
if (config.title != "") {
|
||||
let titleView = new UILabel()
|
||||
titleView.text = new String(config.title);
|
||||
titleView.textAlignment = NSTextAlignment.center
|
||||
titleView.lineBreakMode = NSLineBreakMode.byWordWrapping
|
||||
titleView.numberOfLines = 0
|
||||
titleView.font = UIFont.systemFont(ofSize = new CGFloat(config.titleSize))
|
||||
titleView.textColor = UTSiOS.colorWithString(config.titleColor)
|
||||
titleView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contetDiv.addSubview(titleView)
|
||||
NSLayoutConstraint.activate([
|
||||
// 顶部约束
|
||||
titleView.topAnchor.constraint(equalTo = textView.safeAreaLayoutGuide.bottomAnchor, constant = 1),
|
||||
titleView.bottomAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.bottomAnchor, constant = -16),
|
||||
// 左侧约束
|
||||
titleView.leadingAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.leadingAnchor, constant = 16),
|
||||
// 右侧约束
|
||||
titleView.trailingAnchor.constraint(equalTo = contetDiv.safeAreaLayoutGuide.trailingAnchor, constant = -16)
|
||||
])
|
||||
|
||||
}
|
||||
|
||||
contetDiv.translatesAutoresizingMaskIntoConstraints = false
|
||||
maskerDom!.addSubview(contetDiv)
|
||||
NSLayoutConstraint.activate([
|
||||
// 中心约束
|
||||
contetDiv.centerXAnchor.constraint(equalTo = maskerDom!.centerXAnchor),
|
||||
contetDiv.centerYAnchor.constraint(equalTo = maskerDom!.centerYAnchor),
|
||||
// 宽约束100
|
||||
contetDiv.widthAnchor.constraint(equalToConstant = config.size.toDouble()),
|
||||
// 高为至少100
|
||||
contetDiv.heightAnchor.constraint(greaterThanOrEqualToConstant = config.size.toDouble())
|
||||
])
|
||||
|
||||
parentView.addSubview(maskerDom!)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export function hideXloading() {
|
||||
DispatchQueue.main.async(execute = () : void => {
|
||||
if (maskerDom != null) {
|
||||
maskerDom!.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export type XLOADINGS_TYPE = {
|
||||
iconColor?:string,
|
||||
contentBgColor?:string,
|
||||
maskBgColor?:string,
|
||||
iconSize?:number,
|
||||
iconCode?:string,
|
||||
title?:string,
|
||||
titleSize?:number,
|
||||
titleColor?:string,
|
||||
size?:number
|
||||
}
|
||||
export type XLOADINGS_TYPE_PRIVATE = {
|
||||
iconColor:string,
|
||||
contentBgColor:string,
|
||||
maskBgColor:string,
|
||||
iconSize:number,
|
||||
iconCode:string,
|
||||
title:string,
|
||||
titleSize:number,
|
||||
titleColor:string,
|
||||
size:number
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
type RGBA = { r : number, g : number, b : number, a : number }
|
||||
export function hexToRgb(sColors : string) : RGBA | null {
|
||||
if (sColors == "") {
|
||||
return null
|
||||
}
|
||||
let reg = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
|
||||
let sColor : string = sColors.toLowerCase();
|
||||
|
||||
if (sColor != '' && reg.test(sColor)) {
|
||||
if (sColor.length == 4) {
|
||||
let sColorNew = "#";
|
||||
for (let i = 1; i < 4; i += 1) {
|
||||
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
|
||||
}
|
||||
sColor = sColorNew;
|
||||
}
|
||||
|
||||
//处理六位的颜色值
|
||||
let sColorChange : number[] = [];
|
||||
sColorChange.push(parseInt(sColor.substring(1, 3), 16));
|
||||
sColorChange.push(parseInt(sColor.substring(3, 5), 16));
|
||||
sColorChange.push(parseInt(sColor.substring(5, 7), 16));
|
||||
if (sColor.length == 9) {
|
||||
sColorChange.push(parseInt(sColor.substring(7, 9), 16) / 255);
|
||||
}
|
||||
return {
|
||||
r: sColorChange[0],
|
||||
g: sColorChange[1],
|
||||
b: sColorChange[2],
|
||||
a: sColorChange.length == 4 ? sColorChange[3] : 1
|
||||
} as RGBA
|
||||
} else if (/^(rgb|RGB|rgba|RGBA)/.test(sColor)) {
|
||||
let arr : string[] = sColor.replace(/(?:\(|\)|rgba|rgb|RGB|RGBA)*/g, "").split(",")
|
||||
let p : number[] = arr.map((val : string) : number => parseInt(val));
|
||||
|
||||
if (p.length < 3) {
|
||||
return {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
} as RGBA
|
||||
}
|
||||
if (p.length == 3) {
|
||||
p.add(1)
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
r: p[0],
|
||||
g: p[1],
|
||||
b: p[2],
|
||||
a: arr.length==3?p[3]:parseFloat(arr[3])
|
||||
} as RGBA
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { XLOADINGS_TYPE, XLOADINGS_TYPE_PRIVATE } from "../interface.uts"
|
||||
export const defaultConfig = {
|
||||
iconColor:'rgb(5,121,255)',
|
||||
contentBgColor:'rgba(255,255,255,1)',
|
||||
maskBgColor:'rgba(0,0,0,0.6)',
|
||||
iconSize:36,
|
||||
iconCode:'EEC4',
|
||||
title:'',
|
||||
titleSize:12,
|
||||
titleColor:'#a0a1a7',
|
||||
size:100
|
||||
} as XLOADINGS_TYPE_PRIVATE
|
||||
|
||||
export function configCover(opts : XLOADINGS_TYPE) : XLOADINGS_TYPE_PRIVATE {
|
||||
let iconColor = opts.iconColor != null ? (opts.iconColor!) : defaultConfig.iconColor;
|
||||
let contentBgColor = opts.contentBgColor != null ? (opts.contentBgColor!) : defaultConfig.contentBgColor;
|
||||
let maskBgColor = opts.maskBgColor != null ? (opts.maskBgColor!) : defaultConfig.maskBgColor;
|
||||
let iconSize = opts.iconSize != null ? (opts.iconSize!) : defaultConfig.iconSize;
|
||||
let iconCode = opts.iconCode != null ? (opts.iconCode!) : defaultConfig.iconCode;
|
||||
let title = opts.title != null ? (opts.title!) : defaultConfig.title;
|
||||
let titleSize = opts.titleSize != null ? (opts.titleSize!) : defaultConfig.titleSize;
|
||||
let titleColor = opts.titleColor != null ? (opts.titleColor!) : defaultConfig.titleColor;
|
||||
let size = opts.size != null ? (opts.size!) : defaultConfig.size;
|
||||
|
||||
return {
|
||||
iconColor,
|
||||
contentBgColor,
|
||||
maskBgColor,
|
||||
iconSize,
|
||||
iconCode,title,titleSize,titleColor,size
|
||||
} as XLOADINGS_TYPE_PRIVATE
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import {defaultConfig,configCover} from "../libs/config.uts"
|
||||
export {XLOADINGS_TYPE,XLOADINGS_TYPE_PRIVATE} from "../interface.uts"
|
||||
export const showLoading = (opts : XLOADINGS_TYPE|null) => {
|
||||
let realopts = opts==null?({} as XLOADINGS_TYPE):(opts!)
|
||||
let config = configCover(realopts)
|
||||
wx.showLoading({
|
||||
title:opts?.title??"...",
|
||||
mask:true
|
||||
})
|
||||
}
|
||||
export const hideXloading = ()=>{
|
||||
wx.hideLoading()
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
export {XLOADINGS_TYPE,XLOADINGS_TYPE_PRIVATE} from "../interface.uts"
|
||||
import {defaultConfig,configCover} from "../libs/config.uts"
|
||||
import { LoadFontFace } from "./loadFont.uts"
|
||||
let maskerDom : null | HTMLDivElement = null;
|
||||
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
export const showLoading = (opts : XLOADINGS_TYPE|null) => {
|
||||
LoadFontFace()
|
||||
if (maskerDom != null) {
|
||||
maskerDom.remove()
|
||||
}
|
||||
// @ts-ignore
|
||||
let realopts = opts==null?({} as XLOADINGS_TYPE):(opts!)
|
||||
let config = configCover(realopts)
|
||||
|
||||
maskerDom = document.createElement("div")
|
||||
if (maskerDom == null) return;
|
||||
maskerDom.style.setProperty("background-color", config.maskBgColor)
|
||||
maskerDom.style.setProperty("position", "fixed")
|
||||
maskerDom.style.setProperty("left", "0px")
|
||||
maskerDom.style.setProperty("top", "0px")
|
||||
maskerDom.style.setProperty("width", "100%")
|
||||
maskerDom.style.setProperty("height", "100%")
|
||||
maskerDom.style.display = "flex"
|
||||
maskerDom.style.flexDirection = "row"
|
||||
maskerDom.style.justifyContent = "center"
|
||||
maskerDom.style.alignItems = "center"
|
||||
maskerDom.style.zIndex = "10000"
|
||||
|
||||
let contentWrapDiv = document.createElement("div")
|
||||
contentWrapDiv.style.padding = "16px"
|
||||
contentWrapDiv.style.width = config.size+"px"
|
||||
// contentWrapDiv.style.height = "100px"
|
||||
contentWrapDiv.style.minHeight = config.size+"px"
|
||||
contentWrapDiv.style.backgroundColor = config.contentBgColor
|
||||
contentWrapDiv.style.borderRadius = "16px"
|
||||
contentWrapDiv.style.boxSizing = "border-box"
|
||||
contentWrapDiv.style.display = "flex"
|
||||
contentWrapDiv.style.flexDirection = "column"
|
||||
contentWrapDiv.style.justifyContent = "center"
|
||||
contentWrapDiv.style.alignItems = "center"
|
||||
contentWrapDiv.style.transition = 'all 0.3s'
|
||||
contentWrapDiv.style.scale = '0'
|
||||
let textDiv = document.createElement("span")
|
||||
|
||||
textDiv.innerText = String.fromCharCode(parseInt(config.iconCode, 16));
|
||||
textDiv.style.fontFamily = "remixicon"
|
||||
textDiv.style.fontSize = config.iconSize+"px"
|
||||
textDiv.style.color = config.iconColor
|
||||
textDiv.className = 'xShowLoadingAnimation'
|
||||
|
||||
|
||||
var anistr = `
|
||||
.xShowLoadingAnimation{
|
||||
animation-name: xShowLoadingAnimation;
|
||||
animation-duration: 1.2s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@keyframes xShowLoadingAnimation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`
|
||||
var styleDIv = document.getElementById('xUiLoadWeingIcon');
|
||||
if (!styleDIv) {
|
||||
let style = document.createElement("style");
|
||||
style.id = 'xUiLoadWeingIcon'
|
||||
document.head.appendChild(style)
|
||||
styleDIv = style;
|
||||
}
|
||||
let sheet = styleDIv.sheet;
|
||||
if (sheet.cssRules.length == 0) {
|
||||
styleDIv.append(anistr)
|
||||
}
|
||||
|
||||
contentWrapDiv.appendChild(textDiv)
|
||||
|
||||
if(config.title!=''){
|
||||
let titleDiv = document.createElement("span")
|
||||
titleDiv.innerText = config.title;
|
||||
titleDiv.style.fontSize=config.titleSize+"px"
|
||||
titleDiv.style.color= config.titleColor
|
||||
contentWrapDiv.appendChild(titleDiv)
|
||||
titleDiv.style.textAlign = "center"
|
||||
}
|
||||
|
||||
maskerDom!.appendChild(contentWrapDiv)
|
||||
window.document.body.appendChild(maskerDom!)
|
||||
|
||||
try{
|
||||
setTimeout(function() {
|
||||
contentWrapDiv.style.scale = '1'
|
||||
}, 10);
|
||||
}catch(e){
|
||||
//TODO handle the exception
|
||||
}
|
||||
}
|
||||
|
||||
export const hideXloading = ()=>{
|
||||
if(maskerDom!=null){
|
||||
maskerDom.remove()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export function LoadFontFace(){
|
||||
// 定义字体
|
||||
// @ts-ignore
|
||||
const font = new FontFace("remixicon", "url(/static/tmui4xLibs/static/remixicon.ttf)", {
|
||||
style: "italic",
|
||||
weight: "800",
|
||||
stretch: "condensed",
|
||||
});
|
||||
|
||||
// 把字体添加到 document.fonts(FontFaceSet)中
|
||||
document.fonts.add(font);
|
||||
font.load();
|
||||
}
|
||||
Reference in New Issue
Block a user