1
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
type callbackFunType = (x:number)=>number
|
||||
let kSplineTableSize = 11;
|
||||
let kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
|
||||
|
||||
function A(aA1:number, aA2:number):number { return 1.0 - 3.0 * aA2 + 3.0 * aA1 }
|
||||
function B(aA1:number, aA2:number):number { return 3.0 * aA2 - 6.0 * aA1 }
|
||||
function C(aA1:number):number { return 3.0 * aA1 }
|
||||
|
||||
function calcBezier(aT:number, aA1:number, aA2:number):number { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT }
|
||||
function getSlope(aT:number, aA1:number, aA2:number):number { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1) }
|
||||
|
||||
function binarySubdivide(aX:number, aA:number, aB:number, mX1:number, mX2:number):number {
|
||||
let currentX = 0;
|
||||
let currentT = 0;
|
||||
let i = 0;
|
||||
do {
|
||||
currentT = aA + (aB - aA) / 2.0;
|
||||
currentX = calcBezier(currentT, mX1, mX2) - aX;
|
||||
if (currentX > 0.0) { aB = currentT; } else { aA = currentT; }
|
||||
} while (Math.abs(currentX) > 0.0000001 && ++i < 10);
|
||||
return currentT;
|
||||
}
|
||||
|
||||
function newtonRaphsonIterate(aX:number, aGuessT:number, mX1:number, mX2:number):number {
|
||||
let pat = aGuessT;
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
let currentSlope = getSlope(aGuessT, mX1, mX2);
|
||||
if (currentSlope == 0.0) { return aGuessT; }
|
||||
let currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
||||
pat -= currentX / currentSlope;
|
||||
}
|
||||
return pat;
|
||||
}
|
||||
|
||||
function bezier(mX1:number, mY1:number, mX2:number, mY2:number):callbackFunType|null {
|
||||
|
||||
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { return null; }
|
||||
let sampleValues:number[] = [];
|
||||
|
||||
if (mX1 != mY1 || mX2 != mY2) {
|
||||
for (let i = 0; i < kSplineTableSize; ++i) {
|
||||
sampleValues.push(calcBezier(i * kSampleStepSize, mX1, mX2))
|
||||
}
|
||||
}
|
||||
|
||||
function getTForX(aX:number):number {
|
||||
|
||||
let intervalStart = 0;
|
||||
let currentSample = 1;
|
||||
let lastSample = kSplineTableSize - 1;
|
||||
|
||||
for (; currentSample != lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
|
||||
intervalStart += kSampleStepSize;
|
||||
}
|
||||
|
||||
--currentSample;
|
||||
|
||||
let dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
|
||||
let guessForT:number = intervalStart + dist * kSampleStepSize;
|
||||
let initialSlope = getSlope(guessForT, mX1, mX2);
|
||||
|
||||
if (initialSlope >= 0.001) {
|
||||
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
|
||||
} else if (initialSlope == 0.0) {
|
||||
return guessForT;
|
||||
}
|
||||
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
|
||||
}
|
||||
|
||||
return function (x:number):number {
|
||||
if (mX1 == mY1 && mX2 == mY2) { return x; }
|
||||
if (x == 0 || x == 1) { return x; }
|
||||
return calcBezier(getTForX(x), mY1, mY2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default bezier
|
||||
@@ -0,0 +1,20 @@
|
||||
import {xRequestHistoryType,X_REQUEST_PRIVATE_CALL_FUN_TYPE} from "../../interface.uts"
|
||||
|
||||
|
||||
/**
|
||||
* 内部组件私有
|
||||
*/
|
||||
export const xRequestCall = reactive({
|
||||
history:[] as xRequestHistoryType[],
|
||||
authPass:true,
|
||||
hostUrl:"",
|
||||
dev:false,
|
||||
header:null,
|
||||
/** 请求前是否显示loading遮罩 */
|
||||
showLoadToast:true,
|
||||
/** 请求成功后是否提示,否则不提示 */
|
||||
showSuccessToast:true,
|
||||
/** 出错时,是否显示提示,否则不提示 */
|
||||
showErrorToast:true
|
||||
} as X_REQUEST_PRIVATE_CALL_FUN_TYPE)
|
||||
|
||||
@@ -0,0 +1,850 @@
|
||||
/**
|
||||
* xAnimate
|
||||
* @author tmzdy
|
||||
* @copyright tmui4.0附带核心基类
|
||||
* @version 1.0.0
|
||||
* @description 通过nodeElement元素来控制属性动画变得更为简单方便
|
||||
*
|
||||
*/
|
||||
import { XANIMATE_OPIONS } from "../../interface.uts"
|
||||
import { getUid } from "./xCoreUtil.uts";
|
||||
import { hexToRgb,getDefaultColor,rgbToHex } from "./xCoreColorUtil.uts";
|
||||
import bezier from "./bezier.uts"
|
||||
// #ifdef APP-ANDROID
|
||||
import Choreographer from "android.view.Choreographer";
|
||||
import FrameCallback from "android.view.Choreographer.FrameCallback";
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-IOS
|
||||
// import * as UIKit from 'UIKit';
|
||||
// #endif
|
||||
|
||||
type ATTRGERTS = {
|
||||
from : string,
|
||||
to : string,
|
||||
unit : string,
|
||||
now : string,
|
||||
progress : number,
|
||||
name : string
|
||||
}
|
||||
type callbackFunType = (x:number)=>number
|
||||
type easingFunType = {
|
||||
x:number,
|
||||
y:number,
|
||||
m:number,
|
||||
n:number,
|
||||
}
|
||||
|
||||
export class xAnimate {
|
||||
easingList = new Map<string,number[]>([
|
||||
["linear",[0.250, 0.250, 0.750, 0.750]],
|
||||
["ease",[0.250, 0.100, 0.250, 1.000]],
|
||||
["easeIn",[0.420, 0.000, 1.000, 1.000]],
|
||||
["easeOut",[0.000, 0.000, 0.580, 1.000]],
|
||||
["easeInOut",[0.420, 0.000, 0.580, 1.000]],
|
||||
["easeInQuad", [0.550, 0.085, 0.680, 0.530]],
|
||||
["easeOutQuad", [0.250, 0.460, 0.450, 0.940]],
|
||||
["easeInOutQuad", [0.455, 0.030, 0.515, 0.955]],
|
||||
["easeInCubic", [0.550, 0.055, 0.675, 0.190]],
|
||||
["easeOutCubic", [0.215, 0.610, 0.355, 1.000]],
|
||||
["easeInOutCubic", [0.645, 0.045, 0.355, 1.000]],
|
||||
["easeInQuart", [0.895, 0.030, 0.685, 0.220]],
|
||||
["easeOutQuart", [0.165, 0.840, 0.440, 1.000]],
|
||||
["easeInOutQuart", [0.770, 0.000, 0.175, 1.000]],
|
||||
["easeInQuint", [0.755, 0.050, 0.855, 0.060]],
|
||||
["easeOutQuint", [0.230, 1.000, 0.320, 1.000]],
|
||||
["easeInOutQuint", [0.860, 0.000, 0.070, 1.000]],
|
||||
["easeInSine", [0.470, 0.000, 0.745, 0.715]],
|
||||
["easeOutSine", [0.390, 0.575, 0.565, 1.000]],
|
||||
["easeInOutSine", [0.445, 0.050, 0.550, 0.950]],
|
||||
["easeInExpo", [0.950, 0.050, 0.795, 0.035]],
|
||||
["easeOutExpo", [0.190, 1.000, 0.220, 1.000]],
|
||||
["easeInOutExpo", [1.000, 0.000, 0.000, 1.000]],
|
||||
["easeInCirc", [0.600, 0.040, 0.980, 0.335]],
|
||||
["easeOutCirc", [0.075, 0.820, 0.165, 1.000]],
|
||||
["easeInOutBack", [0.680, -0.550, 0.265, 1.550]],
|
||||
]);
|
||||
private tid = null as null | number;
|
||||
element = null as null|UniElement;
|
||||
// 当前播放的动画函数
|
||||
timingFunction = 'linear';
|
||||
//当前设置的播放时间
|
||||
duration = 500;
|
||||
//元素id
|
||||
ele = "";
|
||||
// 当前是否播放中
|
||||
running = false
|
||||
//当前是否暂停中
|
||||
pauseing = false
|
||||
//当前的播放进度
|
||||
progress = 0
|
||||
reverse = false;
|
||||
//设置true时,loop>1播放时会来回播放动画,而不是从起始开始而是
|
||||
// 始-终-始这样的模式播放。
|
||||
tyty = false;
|
||||
_tyty = false;
|
||||
// 循环播放次数-1表示无限循环.
|
||||
loop = 1
|
||||
// 播放计数
|
||||
private _loop = 0
|
||||
|
||||
// 是否停止动画。
|
||||
private _isStopping = true
|
||||
private attrIndex = 0;
|
||||
|
||||
private completeCallBack = () => { }
|
||||
private startCallBack = () => { }
|
||||
private doCallBack = (propress:number) => { }
|
||||
|
||||
private tagetsAttr = [] as ATTRGERTS[]
|
||||
|
||||
private startTime = 0
|
||||
private easing = null as null | callbackFunType;
|
||||
/** 是否让动画顺序挨过按attr添加的属性顺序执行动画,而不是统一一起执行。 */
|
||||
private isDescPlay = false
|
||||
|
||||
private enterCallFun = ()=>{}
|
||||
// #ifdef APP-ANDROID
|
||||
private ChoreographerDemo = null as Choreographer|null
|
||||
private FrameCallbackCallFun = null as FrameCallback|null
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-IOS
|
||||
// private CADisplayLink = null as UIKit.CADisplayLink|null
|
||||
private dhid = 0
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef WEB || APP-IOS
|
||||
private canFrameIds:number|null = null
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 创建xAnimate动画实例
|
||||
* @param ele 元素UniElement
|
||||
* @param options 动画参数 XANIMATE_OPIONS
|
||||
*/
|
||||
constructor(ele:UniElement|null,options : XANIMATE_OPIONS) {
|
||||
|
||||
// this.element = uni.getElementById(options.ele);
|
||||
this.element = ele;
|
||||
|
||||
this.duration = options.duration == null?this.duration:options.duration!
|
||||
this.loop = options.loop == null?this.loop:options.loop!
|
||||
this.tyty = options.tyty == null?this.tyty:options.tyty!
|
||||
this.isDescPlay = options.isDescPlay == null?this.isDescPlay:options.isDescPlay!
|
||||
|
||||
|
||||
let easingName = options.timingFunction == null?'linear':options.timingFunction!
|
||||
let ecall = this.easingList.get(easingName);
|
||||
if(ecall!=null){
|
||||
let ecallps = ecall!
|
||||
this.easing = bezier(ecallps[0],ecallps[1],ecallps[2],ecallps[3]);
|
||||
}
|
||||
if(options.bezier!=null){
|
||||
let ecallps = options.bezier!
|
||||
this.easing = bezier(ecallps[0],ecallps[1],ecallps[2],ecallps[3]);
|
||||
}
|
||||
if(options.complete!=null){
|
||||
this.completeCallBack = options.complete!
|
||||
}
|
||||
if(options.start!=null){
|
||||
this.startCallBack = options.start!
|
||||
}
|
||||
if(options.frame!=null){
|
||||
this.doCallBack = options.frame!
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private getUnit(n ?: string) : string {
|
||||
if (n == null) return 'px';
|
||||
let unit = n.replace(/[\d|\-|\+]/g, '');
|
||||
if(unit=="."){
|
||||
unit = ""
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
/**
|
||||
* 添加自定义动画
|
||||
*/
|
||||
addTimingFunction(name:string,nubs:number[]){
|
||||
this.easingList.set(name,nubs)
|
||||
}
|
||||
/**
|
||||
* 设置动画反转状态
|
||||
*/
|
||||
setAniReverse(n:boolean|null=null){
|
||||
if(n!=null){
|
||||
this.reverse = n!
|
||||
}else{
|
||||
this.reverse = !this.reverse
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置播放动画的次数
|
||||
*/
|
||||
setLoops(n:number|null = null){
|
||||
if(n!=null){
|
||||
this.loop = n!;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置单次动画播放的持续时间
|
||||
*/
|
||||
setDurations(n:number|null = null){
|
||||
if(n!=null){
|
||||
this.duration = n!;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 连续播放的模式切换
|
||||
*/
|
||||
setTytys(n:boolean|null = null){
|
||||
if(n!=null){
|
||||
this.tyty = n!;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 添加动画属性
|
||||
* @param {string} name 属性名,目前支持官方的css属性,相同的name会被覆盖
|
||||
* @param {string} from 起始值,允许数字字符,带单位2px,20%这样的,但必须和to相同单位
|
||||
* @param {string} to 结束值,允许数字字符,带单位2px,20%这样的,但必须和from相同单位
|
||||
* @param {boolean} only 是否让其是唯一性,重复添加相同的name会被最后一次替换。
|
||||
*/
|
||||
attr(name:string,from : string, to : string ,only:boolean = true):xAnimate {
|
||||
let unit = this.isColorStyle(name)?'':this.getUnit(from);
|
||||
let from_n = this.isColorStyle(name)?getDefaultColor(from):parseFloat(from).toString()
|
||||
let to_n = this.isColorStyle(name)?getDefaultColor(to):parseFloat(to).toString()
|
||||
let index = this.tagetsAttr.findIndex((item:ATTRGERTS):boolean => item.name == name);
|
||||
if(!only){
|
||||
index=-1;
|
||||
}
|
||||
if(index==-1){
|
||||
this.tagetsAttr.push({
|
||||
from: from_n.toString(),
|
||||
to: to_n.toString(),
|
||||
unit,
|
||||
progress: 0,
|
||||
now: from_n,
|
||||
name: name
|
||||
} as ATTRGERTS)
|
||||
}else{
|
||||
this.tagetsAttr[index] = {
|
||||
from: from_n.toString(),
|
||||
to: to_n.toString(),
|
||||
unit,
|
||||
progress: 0,
|
||||
now: from_n,
|
||||
name: name
|
||||
} as ATTRGERTS
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
private interpolate(startValue:number, endValue:number, progress:number):number {
|
||||
return startValue + (endValue - startValue) * progress;
|
||||
}
|
||||
private isColorStyle(val:string):boolean{
|
||||
return val.indexOf('background')>-1||val.indexOf('color')>-1;
|
||||
}
|
||||
private _setAttr(name:string,current:number,unit:string,progress:number,item:ATTRGERTS){
|
||||
if(this.element==null) return
|
||||
|
||||
if(name=='scaleX'){
|
||||
this.element!.style!.setProperty("transform",`scaleX(${current})`)
|
||||
}else if(name=='scaleY'){
|
||||
this.element!.style!.setProperty("transform",`scaleY(${current})`)
|
||||
}else if(name=='scale'){
|
||||
this.element!.style!.setProperty("transform",`scale(${current})`)
|
||||
}else if(name=='rotateX'){
|
||||
this.element!.style!.setProperty("transform",`rotateX(${(current).toString()+unit})`)
|
||||
}else if(name=='rotateY'){
|
||||
this.element!.style!.setProperty("transform",`rotateY(${(current).toString()+unit})`)
|
||||
}else if(name=='rotate'){
|
||||
this.element!.style!.setProperty("transform",`rotate(${(current).toString()+unit})`)
|
||||
}else if(name=='translateX'){
|
||||
this.element!.style!.setProperty("transform",`translateX(${(current).toString()+unit})`)
|
||||
}else if(name=='translateY'){
|
||||
this.element!.style!.setProperty("transform",`translateY(${(current).toString()+unit})`)
|
||||
}else if(name=='translate'){
|
||||
this.element!.style!.setProperty("transform",`translate(${(current).toString()+unit},${(current).toString()+unit})`)
|
||||
}else if(this.isColorStyle(name)){
|
||||
let startRgba = hexToRgb(item.from)
|
||||
let dndRgba = hexToRgb(item.to)
|
||||
let r = this.interpolate(startRgba.getNumber('r')!,dndRgba.getNumber('r')!,progress);
|
||||
let g = this.interpolate(startRgba.getNumber('g')!,dndRgba.getNumber('g')!,progress);
|
||||
let b = this.interpolate(startRgba.getNumber('b')!,dndRgba.getNumber('b')!,progress);
|
||||
let a = this.interpolate(startRgba.getNumber('a')!,dndRgba.getNumber('a')!,progress);
|
||||
this.element!.style!
|
||||
.setProperty(name,`rgba(${r.toFixed(0)},${g.toFixed(0)},${b.toFixed(0)},${a.toFixed(1)})`)
|
||||
}else{
|
||||
|
||||
this.element!.style!.setProperty(name,current.toFixed(2)+unit)
|
||||
}
|
||||
|
||||
}
|
||||
private _run_web() {
|
||||
|
||||
// #ifdef H5 || APP-IOS || APP-HARMONY
|
||||
|
||||
let _this = this;
|
||||
_this.startTime = 0
|
||||
|
||||
if(_this.canFrameIds!=null){
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
function run(){
|
||||
if (_this.startTime<=0) {
|
||||
_this.startTime = Date.now(); // 记录动画开始时间
|
||||
}
|
||||
const progress = Math.min((Date.now() - _this.startTime) / _this.duration + _this.progress, 1); // 计算当前进度
|
||||
if(_this.element!=null){
|
||||
if(!_this.isDescPlay){
|
||||
for(let i=0;i<_this.tagetsAttr.length;i++){
|
||||
let item = _this.tagetsAttr[i]
|
||||
item.progress = progress
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(_this.attrIndex<_this.tagetsAttr.length){
|
||||
let item = _this.tagetsAttr[_this.attrIndex]
|
||||
item.progress = progress
|
||||
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(_this.pauseing){
|
||||
if(_this.canFrameIds!=null){
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
_this.running = false
|
||||
console.log(_this.pauseing,"动画暂停")
|
||||
_this.progress = progress
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(_this.running,"动画结束")
|
||||
if(progress>=1||_this._isStopping){
|
||||
|
||||
if(_this.isDescPlay&&_this.attrIndex<_this.tagetsAttr.length){
|
||||
_this.attrIndex +=1;
|
||||
_this.progress = 0
|
||||
_this._run_web();
|
||||
return;
|
||||
}
|
||||
|
||||
if(_this.canFrameIds!=null) {
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
_this.attrIndex = 0;
|
||||
_this.progress = 0
|
||||
if(_this.tyty){
|
||||
_this._tyty = !_this._tyty
|
||||
}
|
||||
if(_this.loop==-1){
|
||||
_this._run_web();
|
||||
return;
|
||||
}else{
|
||||
_this._loop+=1;
|
||||
if(_this._loop<_this.loop){
|
||||
_this._run_web();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_this.running = false
|
||||
_this.completeCallBack()
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress < 1 && _this.running) {
|
||||
_this.doCallBack(progress)
|
||||
_this.canFrameIds = requestAnimationFrame(run)
|
||||
}
|
||||
}
|
||||
_this.startCallBack()
|
||||
|
||||
run()
|
||||
// #endif
|
||||
}
|
||||
|
||||
private _run_weapp() {
|
||||
// #ifdef MP
|
||||
let _this = this;
|
||||
_this.startTime = 0
|
||||
clearTimeout(_this.dhid)
|
||||
function run(){
|
||||
if (_this.startTime<=0) {
|
||||
_this.startTime = Date.now(); // 记录动画开始时间
|
||||
}
|
||||
const progress = Math.min((Date.now() - _this.startTime) / _this.duration + _this.progress, 1); // 计算当前进度
|
||||
if(_this.element!=null){
|
||||
if(!_this.isDescPlay){
|
||||
for(let i=0;i<_this.tagetsAttr.length;i++){
|
||||
let item = _this.tagetsAttr[i]
|
||||
item.progress = progress
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(_this.attrIndex<_this.tagetsAttr.length){
|
||||
let item = _this.tagetsAttr[_this.attrIndex]
|
||||
item.progress = progress
|
||||
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(_this.pauseing){
|
||||
clearTimeout(_this.dhid)
|
||||
_this.running = false
|
||||
console.log(_this.pauseing,"动画暂停")
|
||||
_this.progress = progress
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(_this.running,"动画结束")
|
||||
if(progress>=1||_this._isStopping){
|
||||
|
||||
if(_this.isDescPlay&&_this.attrIndex<_this.tagetsAttr.length){
|
||||
_this.attrIndex +=1;
|
||||
_this.progress = 0
|
||||
_this._run_weapp();
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(_this.dhid)
|
||||
_this.attrIndex = 0;
|
||||
_this.progress = 0
|
||||
if(_this.tyty){
|
||||
_this._tyty = !_this._tyty
|
||||
}
|
||||
if(_this.loop==-1){
|
||||
_this._run_weapp();
|
||||
return;
|
||||
}else{
|
||||
_this._loop+=1;
|
||||
if(_this._loop<_this.loop){
|
||||
_this._run_weapp();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_this.running = false
|
||||
_this.completeCallBack()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (progress < 1 && _this.running) {
|
||||
_this.dhid = setTimeout(()=>{
|
||||
_this.doCallBack(progress)
|
||||
run()
|
||||
}, 6); // 递归调用自身,实现动画效果
|
||||
}
|
||||
|
||||
}
|
||||
_this.startCallBack()
|
||||
run()
|
||||
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
private _run_andriod() {
|
||||
// #ifdef APP-ANDROID
|
||||
let _this = this;
|
||||
_this.startTime = 0
|
||||
let dhid = 0
|
||||
if(this.ChoreographerDemo==null){
|
||||
this.ChoreographerDemo = Choreographer.getInstance();
|
||||
}else{
|
||||
if(this.FrameCallbackCallFun!=null){
|
||||
_this.ChoreographerDemo!.removeFrameCallback(this.FrameCallbackCallFun!)
|
||||
}
|
||||
}
|
||||
class frameCallback extends Choreographer.FrameCallback {
|
||||
override doFrame(frameTimeNanos:Long) {
|
||||
|
||||
if (_this.startTime<=0) {
|
||||
_this.startTime = Date.now(); // 记录动画开始时间
|
||||
}
|
||||
const progress = Math.min((Date.now() - _this.startTime) / _this.duration + _this.progress, 1); // 计算当前进度
|
||||
if(_this.element!=null){
|
||||
|
||||
if(!_this.isDescPlay){
|
||||
for(let i=0;i<_this.tagetsAttr.length;i++){
|
||||
let item = _this.tagetsAttr[i]
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(_this.attrIndex<_this.tagetsAttr.length){
|
||||
let item = _this.tagetsAttr[_this.attrIndex]
|
||||
item.progress = progress
|
||||
|
||||
if(!_this.isColorStyle(item.name)){
|
||||
let fromN = parseFloat(item.from)
|
||||
let toN = parseFloat(item.to)
|
||||
let easeInt = 1
|
||||
if(_this.easing!=null){
|
||||
let eas = _this.easing!
|
||||
easeInt = eas(progress)
|
||||
}
|
||||
|
||||
let current = fromN + (toN - fromN) * (easeInt==1?progress:easeInt); // 根据进度计算当前值
|
||||
if(_this.reverse||_this._tyty ){
|
||||
current = toN + (fromN - toN) * (easeInt==1?progress:easeInt);
|
||||
}
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,current,item.unit,progress,item)
|
||||
}
|
||||
}else{
|
||||
if(_this.element!=null){
|
||||
_this._setAttr(item.name,0,item.unit,progress,item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(progress>=1||_this._isStopping){
|
||||
|
||||
if(_this.isDescPlay&&_this.attrIndex<_this.tagetsAttr.length){
|
||||
_this.attrIndex +=1;
|
||||
_this.progress = 0
|
||||
_this._run_andriod();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_this.progress = 0
|
||||
if(_this.tyty){
|
||||
_this._tyty = !_this._tyty
|
||||
}
|
||||
if(_this.loop==-1){
|
||||
_this._run_andriod();
|
||||
return;
|
||||
}else{
|
||||
_this._loop+=1;
|
||||
if(_this._loop<_this.loop){
|
||||
_this._run_andriod();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_this.running = false
|
||||
_this.completeCallBack()
|
||||
return;
|
||||
}
|
||||
|
||||
if(_this.pauseing){
|
||||
_this.running = false
|
||||
console.log(_this.pauseing,"动画暂停")
|
||||
_this.progress = progress
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (progress < 1 && _this.running) {
|
||||
_this.doCallBack(progress)
|
||||
_this.ChoreographerDemo!.postFrameCallback(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
_this.startCallBack()
|
||||
this.FrameCallbackCallFun = new frameCallback()
|
||||
_this.ChoreographerDemo!.postFrameCallback(this.FrameCallbackCallFun!);
|
||||
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
private __run_web() {
|
||||
// #ifdef H5
|
||||
let _this = this;
|
||||
if(_this.canFrameIds!=null) {
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
function run(){
|
||||
_this.enterCallFun()
|
||||
if(_this._isStopping){
|
||||
_this.running = false
|
||||
|
||||
if(_this.canFrameIds!=null) {
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_this.running) {
|
||||
_this.canFrameIds = requestAnimationFrame(run)
|
||||
}
|
||||
}
|
||||
run()
|
||||
// #endif
|
||||
}
|
||||
|
||||
private __run_andriod() {
|
||||
// #ifdef APP-ANDROID
|
||||
let _this = this;
|
||||
|
||||
if(this.ChoreographerDemo==null){
|
||||
this.ChoreographerDemo = Choreographer.getInstance();
|
||||
}else{
|
||||
if(this.FrameCallbackCallFun!=null){
|
||||
_this.ChoreographerDemo!.removeFrameCallback(this.FrameCallbackCallFun!)
|
||||
}
|
||||
}
|
||||
class frameCallback extends Choreographer.FrameCallback {
|
||||
override doFrame(frameTimeNanos:Long) {
|
||||
_this.enterCallFun()
|
||||
if(_this._isStopping){
|
||||
_this.running = false
|
||||
return;
|
||||
}
|
||||
if (_this.running) {
|
||||
_this.ChoreographerDemo!.postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.FrameCallbackCallFun = new frameCallback()
|
||||
_this.ChoreographerDemo!.postFrameCallback(this.FrameCallbackCallFun!);
|
||||
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
private __run_weapp(){
|
||||
// #ifdef MP
|
||||
let _this = this;
|
||||
clearTimeout(_this.dhid)
|
||||
function run(){
|
||||
_this.enterCallFun()
|
||||
if(_this._isStopping){
|
||||
_this.running = false
|
||||
clearTimeout(_this.dhid)
|
||||
return;
|
||||
}
|
||||
if (_this.running) {
|
||||
_this.dhid = setTimeout(run, 6);
|
||||
}
|
||||
}
|
||||
run()
|
||||
// #endif
|
||||
}
|
||||
/**
|
||||
* 启动动画
|
||||
*/
|
||||
play():xAnimate {
|
||||
if(this.running) return this;
|
||||
this.running = true;
|
||||
// 开始动画
|
||||
this._isStopping = false;
|
||||
this.pauseing = false;
|
||||
this._loop = 0
|
||||
this.attrIndex =0;
|
||||
// #ifdef H5 || APP-IOS || APP-HARMONY
|
||||
this._run_web();
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
this._run_andriod();
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
this._run_weapp();
|
||||
// #endif
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止,会让动画直接结束,把目标值直接前进到结束的地方。
|
||||
*/
|
||||
stop():xAnimate {
|
||||
let _this = this;
|
||||
this._isStopping = true;
|
||||
this.progress = 0
|
||||
this.attrIndex = _this.tagetsAttr.length
|
||||
|
||||
// #ifdef WEB || APP-IOS
|
||||
if(_this.canFrameIds!=null){
|
||||
cancelAnimationFrame(_this.canFrameIds)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
|
||||
if(this.ChoreographerDemo==null){
|
||||
this.ChoreographerDemo = Choreographer.getInstance();
|
||||
}else{
|
||||
if(this.FrameCallbackCallFun!=null){
|
||||
this.ChoreographerDemo!.removeFrameCallback(this.FrameCallbackCallFun!)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
clearTimeout(_this.dhid)
|
||||
// setTimeout(function(){
|
||||
// for(let i=0;i<_this.tagetsAttr.length;i++){
|
||||
// let item = _this.tagetsAttr[i]
|
||||
// item.now = item.to;
|
||||
// if(_this.element!=null){
|
||||
// _this.element!.style!.setProperty(item.name,(_this.reverse?item.from:item.to)+item.unit)
|
||||
// }
|
||||
// }
|
||||
// _this.reverse = false;
|
||||
// },10)
|
||||
// _this.running = false;
|
||||
// #endif
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 暂停,会保留当前的动画效果,不会直接结束。
|
||||
*/
|
||||
pause():xAnimate {
|
||||
this.pauseing = true;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 刷新回调函数
|
||||
*/
|
||||
enterFrame(evt:()=>void){
|
||||
this.stop()
|
||||
this.enterCallFun=evt;
|
||||
this.running = true;
|
||||
this._isStopping = false
|
||||
// #ifdef WEB || APP-IOS
|
||||
this.__run_web()
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
this.__run_andriod();
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
this.__run_weapp();
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,726 @@
|
||||
import { toHex } from "./xCoreUtil.uts";
|
||||
import { xConfig } from "../../config/xConfig.uts";
|
||||
/**
|
||||
* 颜色值列表
|
||||
*/
|
||||
|
||||
export const colors = new Map<string, string>([
|
||||
['primary', '#0088ff'],
|
||||
['success', '#34c759'],
|
||||
['danger', '#ff8d28'],
|
||||
['warn', '#F3BF12'],
|
||||
['error', '#ff383c'],
|
||||
['info', '#f2f2f7'],
|
||||
|
||||
['kleinblue', '#002FA7'], // 克莱因蓝
|
||||
['chinesered', '#FF0000'], // 中国红
|
||||
['internationalorange', '#FF4F00'], // 国际橙
|
||||
['egyptianvlue', '#1034A6'], // 埃及艳蓝
|
||||
['parisviolet', '#6C3082'], // 巴黎紫
|
||||
['moroccanblue', '#1256A7'], // 摩洛哥蓝
|
||||
['brazilgreen', '#009B3A'], // 巴西绿
|
||||
['britishracinggreen', '#004225'], // 英国赛车绿
|
||||
['indianyellow', '#E3A857'], // 印度黄
|
||||
['australiangold', '#FFDF00'], // 澳大利亚金
|
||||
['venetianred', '#C80815'], // 威尼斯红
|
||||
['majorelleblue', '#6050DC'], // 马若雷蓝
|
||||
['tuscanred', '#7C3030'], // 托斯卡红
|
||||
['naplesyellow', '#FADA5E'], // 那不勒斯黄
|
||||
['capumortuum', '#592720'], // 死者之首
|
||||
['mayablue', '#73C2FB'], // 玛雅蓝
|
||||
['persianrose', '#FE28A2'], // 波斯玫瑰
|
||||
['tyrianpurple', '#66023C'], // 泰尔紫
|
||||
['saharasand', '#F1E788'], // 撒哈拉沙
|
||||
['burmeseruby', '#B00A0A'], // 缅甸红宝石
|
||||
|
||||
['transparent', 'rgba(0,0,0,0)'],
|
||||
['aliceblue', '#F0F8FF'],
|
||||
['antiquewhite', '#FAEBD7'],
|
||||
['aqua', '#00FFFF'],
|
||||
['aquamarine', '#7FFFD4'],
|
||||
['azure', '#F0FFFF'],
|
||||
['beige', '#F5F5DC'],
|
||||
['bisque', '#FFE4C4'],
|
||||
['black', '#000000'],
|
||||
['blanchedalmond', '#FFEBCD'],
|
||||
['blue', '#0000FF'],
|
||||
['blueviolet', '#8A2BE2'],
|
||||
['brown', '#A52A2A'],
|
||||
['burlywood', '#DEB887'],
|
||||
['cadetblue', '#5F9EA0'],
|
||||
['chartreuse', '#7FFF00'],
|
||||
['chocolate', '#D2691E'],
|
||||
['coral', '#FF7F50'],
|
||||
['cornflowerblue', '#6495ED'],
|
||||
['cornsilk', '#FFF8DC'],
|
||||
['crimson', '#DC143C'],
|
||||
['cyan', '#00FFFF'],
|
||||
['darkblue', '#00008B'],
|
||||
['darkcyan', '#008B8B'],
|
||||
['darkgoldenrod', '#B8860B'],
|
||||
['darkgray', '#A9A9A9'],
|
||||
['darkgreen', '#006400'],
|
||||
['darkkhaki', '#BDB76B'],
|
||||
['darkmagenta', '#8B008B'],
|
||||
['darkolivegreen', '#556B2F'],
|
||||
['darkorange', '#FF8C00'],
|
||||
['darkorchid', '#9932CC'],
|
||||
['darkred', '#8B0000'],
|
||||
['darksalmon', '#E9967A'],
|
||||
['darkseagreen', '#8FBC8F'],
|
||||
['darkslateblue', '#483D8B'],
|
||||
['darkslategray', '#2F4F4F'],
|
||||
['darkturquoise', '#00CED1'],
|
||||
['darkviolet', '#9400D3'],
|
||||
['deeppink', '#FF1493'],
|
||||
['deepskyblue', '#00BFFF'],
|
||||
['dimgray', '#696969'],
|
||||
['dodgerblue', '#1E90FF'],
|
||||
['firebrick', '#B22222'],
|
||||
['floralwhite', '#FFFAF0'],
|
||||
['forestgreen', '#228B22'],
|
||||
['fuchsia', '#FF00FF'],
|
||||
['gainsboro', '#DCDCDC'],
|
||||
['ghostwhite', '#F8F8FF'],
|
||||
['gold', '#FFD700'],
|
||||
['goldenrod', '#DAA520'],
|
||||
['gray', '#808080'],
|
||||
['green', '#008000'],
|
||||
['greenyellow', '#ADFF2F'],
|
||||
['honeydew', '#F0FFF0'],
|
||||
['hotpink', '#FF69B4'],
|
||||
['indianred', '#CD5C5C'],
|
||||
['indigo', '#4B0082'],
|
||||
['ivory', '#FFFFF0'],
|
||||
['khaki', '#F0E68C'],
|
||||
['lavender', '#E6E6FA'],
|
||||
['lavenderblush', '#FFF0F5'],
|
||||
['lawngreen', '#7CFC00'],
|
||||
['lemonchiffon', '#FFFACD'],
|
||||
['lightblue', '#ADD8E6'],
|
||||
['lightcoral', '#F08080'],
|
||||
['lightcyan', '#E0FFFF'],
|
||||
['lightgoldenrodyellow', '#FAFAD2'],
|
||||
['lightgray', '#D3D3D3'],
|
||||
['lightgreen', '#90EE90'],
|
||||
['lightpink', '#FFB6C1'],
|
||||
['lightsalmon', '#FFA07A'],
|
||||
['lightseagreen', '#20B2AA'],
|
||||
['lightskyblue', '#87CEFA'],
|
||||
['lightslategray', '#778899'],
|
||||
['lightsteelblue', '#B0C4DE'],
|
||||
['lightyellow', '#FFFFE0'],
|
||||
['lime', '#00FF00'],
|
||||
['limegreen', '#32CD32'],
|
||||
['linen', '#FAF0E6'],
|
||||
['magenta', '#FF00FF'],
|
||||
['maroon', '#800000'],
|
||||
['mediumaquamarine', '#66CDAA'],
|
||||
['mediumblue', '#0000CD'],
|
||||
['mediumorchid', '#BA55D3'],
|
||||
['mediumpurple', '#9370DB'],
|
||||
['mediumseagreen', '#3CB371'],
|
||||
['mediumslateblue', '#7B68EE'],
|
||||
['mediumspringgreen', '#00FA9A'],
|
||||
['mediumturquoise', '#48D1CC'],
|
||||
['mediumvioletred', '#C71585'],
|
||||
['midnightblue', '#191970'],
|
||||
['mintcream', '#F5FFFA'],
|
||||
['mistyrose', '#FFE4E1'],
|
||||
['moccasin', '#FFE4B5'],
|
||||
['navajowhite', '#FFDEAD'],
|
||||
['navy', '#000080'],
|
||||
['oldlace', '#FDF5E6'],
|
||||
['olive', '#808000'],
|
||||
['olivedrab', '#6B8E23'],
|
||||
['orange', '#FFA500'],
|
||||
['orangered', '#FF4500'],
|
||||
['orchid', '#DA70D6'],
|
||||
['palegoldenrod', '#EEE8AA'],
|
||||
['palegreen', '#98FB98'],
|
||||
['paleturquoise', '#AFEEEE'],
|
||||
['palevioletred', '#DB7093'],
|
||||
['papayawhip', '#FFEFD5'],
|
||||
['peachpuff', '#FFDAB9'],
|
||||
['peru', '#CD853F'],
|
||||
['pink', '#FFC0CB'],
|
||||
['plum', '#DDA0DD'],
|
||||
['powderblue', '#B0E0E6'],
|
||||
['purple', '#800080'],
|
||||
['rebeccapurple', '#663399'],
|
||||
['red', '#FF0000'],
|
||||
['rosybrown', '#BC8F8F'],
|
||||
['royalblue', '#4169E1'],
|
||||
['saddlebrown', '#8B4513'],
|
||||
['salmon', '#FA8072'],
|
||||
['sandybrown', '#F4A460'],
|
||||
['seagreen', '#2E8B57'],
|
||||
['seashell', '#FFF5EE'],
|
||||
['sienna', '#A0522D'],
|
||||
['silver', '#C0C0C0'],
|
||||
['skyblue', '#87CEEB'],
|
||||
['slateblue', '#6A5ACD'],
|
||||
['slategray', '#708090'],
|
||||
['snow', '#FFFAFA'],
|
||||
['springgreen', '#00FF7F'],
|
||||
['steelblue', '#4682B4'],
|
||||
['tan', '#D2B48C'],
|
||||
['teal', '#008080'],
|
||||
['thistle', '#D8BFD8'],
|
||||
['tomato', '#FF6347'],
|
||||
['turquoise', '#40E0D0'],
|
||||
['violet', '#EE82EE'],
|
||||
['wheat', '#F5DEB3'],
|
||||
['white', '#FFFFFF'],
|
||||
['whitesmoke', '#F5F5F5'],
|
||||
['yellow', '#FFFF00'],
|
||||
['yellowgreen', '#9ACD32']
|
||||
|
||||
])
|
||||
|
||||
export function isValidColor(color : string) : boolean {
|
||||
// return chroma.valid(color);
|
||||
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
|
||||
const rgbRegex = /^rgb\((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\)$/;
|
||||
const rgbaRegex = /^rgba\((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*((1(\.0{1,2})?)|(0(\.\d{1,2})?))\)$/;
|
||||
const hslRegex = /^hsl\((\d{1,3}), (\d{1,3})%, (\d{1,3})%\)$/;
|
||||
const hslaRegex = /^hsla\(\s*((\d{1,2}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((1|0(\.\d{1,2})?|(\.\d{1,2})))\)$/i;
|
||||
|
||||
|
||||
if (color == '') {
|
||||
return false; // 颜色值为空
|
||||
} else if (color === 'inherit' || color === 'transparent') {
|
||||
return false; // 特殊颜色值
|
||||
} else if (color === 'currentColor') {
|
||||
return false; // currentColor 不是有效的颜色值
|
||||
} else if (hexRegex.test(color) || rgbRegex.test(color) || rgbaRegex.test(color) || hslRegex.test(color) || hslaRegex.test(color)) {
|
||||
return true; // 符合颜色值的格式
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 获取默认的颜色值
|
||||
* @param [string] 颜色名称或者16进制颜色值
|
||||
*/
|
||||
export function getDefaultColor(sColor : string) : string {
|
||||
if (sColor == "") return ""
|
||||
let sc = sColor.toLocaleLowerCase().trim().replace(" ", "");
|
||||
|
||||
if (isValidColor(sc)) {
|
||||
// 如果符合所有颜色值,进行转换为16进制。css方便统一解析。
|
||||
return sc;
|
||||
}
|
||||
let colorhtme = xConfig.theme.get(sc)
|
||||
// 检测是否是颜色名称。
|
||||
let sco = colorhtme==null?colors.get(sc):colorhtme;
|
||||
if (typeof sco == 'string') return sco as string;
|
||||
return colors.get("primary")!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 16进制转rgb
|
||||
*/
|
||||
export function hexToRgb(sColors : string) : UTSJSONObject {
|
||||
if (sColors == "") {
|
||||
return { r: 0, g: 0, b: 0, a: 0 }
|
||||
}
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
if (p.length == 3) {
|
||||
arr.push('1')
|
||||
}
|
||||
|
||||
return {
|
||||
r: p[0],
|
||||
g: p[1],
|
||||
b: p[2],
|
||||
a: parseFloat(arr[3])
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* rgb转hsl
|
||||
*/
|
||||
export function rgbToHsl(rgb : UTSJSONObject) : UTSJSONObject {
|
||||
let r = rgb.getNumber("r")
|
||||
r = r as number / 255
|
||||
let g = rgb.getNumber("g")
|
||||
g = g as number / 255
|
||||
let b = rgb.getNumber("b")
|
||||
b = b as number / 255
|
||||
let a = rgb.getNumber("a")
|
||||
|
||||
var max = Math.max(r, g, b);
|
||||
var min = Math.min(r, g, b);
|
||||
let maxmindiff = max - min;
|
||||
let maxmindiffAdd = max + min;
|
||||
// #ifdef APP-ANDROID && uniVersion >=4.31
|
||||
maxmindiff = maxmindiff.toDouble()
|
||||
maxmindiffAdd = maxmindiffAdd.toDouble()
|
||||
// #endif
|
||||
let h = 60 * (4 + (r - g) / maxmindiff);
|
||||
let sdy = 2 - max - min
|
||||
// #ifdef APP-ANDROID && uniVersion >=4.31
|
||||
sdy = sdy.toDouble()
|
||||
// #endif
|
||||
let s = (max - min) / sdy;
|
||||
let l = (max + min) / 2;
|
||||
|
||||
if (max === r) {
|
||||
h = (60 * (g - b)) / maxmindiff;
|
||||
} else if (max === g) {
|
||||
h = 60 * (2 + (b - r) / maxmindiff);
|
||||
}
|
||||
|
||||
if (h < 0) {
|
||||
h += 360;
|
||||
}
|
||||
|
||||
|
||||
if (max === min) {
|
||||
s = 0;
|
||||
} else if (l < 0.5) {
|
||||
s = (max - min) / maxmindiffAdd;
|
||||
}
|
||||
|
||||
return { h: h, s: s * 100, l: l * 100, a: a };
|
||||
}
|
||||
|
||||
/**
|
||||
* hsl转rgb
|
||||
*/
|
||||
export function hslToRgb(hsl : UTSJSONObject) : UTSJSONObject {
|
||||
let h = hsl.getNumber("h")!
|
||||
let s = hsl.getNumber("s")!
|
||||
let l = hsl.getNumber("l")!
|
||||
let a = hsl.getNumber("a")!
|
||||
|
||||
h = h / 360;
|
||||
s = s / 100;
|
||||
l = l / 100;
|
||||
let r = l;
|
||||
let g = l;
|
||||
let b = l;
|
||||
|
||||
function hue2rgb(pxx : number, q : number, txs : number) : number {
|
||||
let t = txs;
|
||||
let p = pxx;
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (s > 0) {
|
||||
let q = l * (1 + s);
|
||||
if (l >= 0.5) {
|
||||
q = l + s - l * s;
|
||||
}
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
|
||||
}
|
||||
|
||||
r = Math.round(r * 255);
|
||||
g = Math.round(g * 255);
|
||||
b = Math.round(b * 255);
|
||||
return { r, g, b, a };
|
||||
}
|
||||
|
||||
/**
|
||||
* rgb转16进制
|
||||
*/
|
||||
export function rgbToHex(rgb : UTSJSONObject) : string {
|
||||
let r = rgb.getNumber("r")!
|
||||
let g = rgb.getNumber("g")!
|
||||
let b = rgb.getNumber("b")!
|
||||
let a = rgb.getNumber("a")!
|
||||
return "#" + toHex(r) + toHex(g) + toHex(b) + toHex(a * 255)
|
||||
}
|
||||
/**
|
||||
* rgb转16进制
|
||||
*/
|
||||
export function rgbToHexNoAlpha(rgb : UTSJSONObject) : string {
|
||||
let r = rgb.getNumber("r")!
|
||||
let g = rgb.getNumber("g")!
|
||||
let b = rgb.getNumber("b")!
|
||||
|
||||
return "#" + toHex(r) + toHex(g) + toHex(b)
|
||||
}
|
||||
export function hslaToCss(hsl : UTSJSONObject) : string {
|
||||
|
||||
let rgb = hslToRgb(hsl)
|
||||
|
||||
return rgbToHex(rgb)
|
||||
}
|
||||
|
||||
export function hslaToRgbCss(hsl : UTSJSONObject) : string {
|
||||
|
||||
let rgb = hslToRgb(hsl)
|
||||
let r = rgb.getNumber("r")!
|
||||
let g = rgb.getNumber("g")!
|
||||
let b = rgb.getNumber("b")!
|
||||
let a = rgb.getNumber("a")!
|
||||
return `rgba(${r},${g},${b},${a})`
|
||||
}
|
||||
|
||||
// 根据 HSLA 颜色计算亮度
|
||||
export function getLuminance(color : UTSJSONObject, type = "rgba") : number {
|
||||
|
||||
|
||||
let colordefault : UTSJSONObject = type == 'rgba' ? color : hslToRgb(color);
|
||||
const r = colordefault.getNumber("r")!
|
||||
const g = colordefault.getNumber("g")!
|
||||
const b = colordefault.getNumber("b")!
|
||||
const rs = r / 255
|
||||
const gs = g / 255
|
||||
const bs = b / 255
|
||||
const rl = Math.pow(rs <= 0.03928 ? rs / 12.92 : ((rs + 0.055) / 1.055), 2.4);
|
||||
const gl = Math.pow(gs <= 0.03928 ? gs / 12.92 : ((gs + 0.055) / 1.055), 2.4);
|
||||
const bl = Math.pow(bs <= 0.03928 ? bs / 12.92 : ((bs + 0.055) / 1.055), 2.4);
|
||||
|
||||
return 0.2126 * rl + 0.7152 * gl + 0.0722 * bl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对颜色加深
|
||||
*/
|
||||
export function colorAddDeepen(sColor : string) : string {
|
||||
|
||||
let rgb = hexToRgb(getDefaultColor(sColor));
|
||||
|
||||
let hsl = rgbToHsl(rgb);
|
||||
|
||||
let l = hsl.getNumber("l")! - 5;
|
||||
l = Math.max(0, Math.min(l, 100))
|
||||
|
||||
return hslaToRgbCss({ h: hsl.getNumber("h"), s: hsl.getNumber("s"), l, a: hsl.getNumber("a")! })
|
||||
}
|
||||
/**
|
||||
* 对颜色提亮
|
||||
*/
|
||||
export function colorAddBrighten(sColor : string) : string {
|
||||
let rgb = hexToRgb(getDefaultColor(sColor));
|
||||
let hsl = rgbToHsl(rgb);
|
||||
let l = hsl.getNumber("l")! + 10;
|
||||
l = Math.max(0, Math.min(l, 100))
|
||||
return rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s: hsl.getNumber("s"), l, a: hsl.getNumber("a")! }))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取浅色系主题,根据颜色值计算。返回正常值和hover值。
|
||||
*/
|
||||
export function colorGetThint(sColor : string) {
|
||||
let rgb = hexToRgb(getDefaultColor(sColor));
|
||||
let hsl = rgbToHsl(rgb);
|
||||
let s = hsl.getNumber("s")!;
|
||||
// let l = hsl.getNumber("l");
|
||||
if (Math.ceil(s) > 0) {
|
||||
s = 45
|
||||
}
|
||||
return {
|
||||
default: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 93, a: hsl.getNumber("a")! })),
|
||||
hover: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 85, a: hsl.getNumber("a")! }))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取正常主题,根据颜色值计算。返回正常值和hover值。
|
||||
*/
|
||||
export function colorGetHover(sColor : string) {
|
||||
|
||||
return {
|
||||
default: sColor,
|
||||
hover: colorAddDeepen(sColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理相主题色。
|
||||
* @param color 需要处理的颜色值
|
||||
* @param hoverColor 对应按下去的颜色默认与color相等。
|
||||
* @returns UTSJSONObject
|
||||
*/
|
||||
export function getDefaultColorObj(color : string, hoverColor : string) : UTSJSONObject {
|
||||
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
|
||||
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
|
||||
let shadow = hslaToRgbCss({ h: hsla.getNumber("h")!, s: (hsla.getNumber("s")!) == 0 ? 0 : 60, l: 40, a: 0.4 });
|
||||
let shadowHOver = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: (hoverHsla.getNumber("s")!) == 0 ? 0 : 60, l: 40, a: 0.4 });
|
||||
if ((hsla.getNumber("h")!) == 0 && (hsla.getNumber("s")!) == 0) {
|
||||
shadow = "transparent"
|
||||
}
|
||||
if ((hoverHsla.getNumber("h")!) == 0 && (hoverHsla.getNumber("s")!) == 0) {
|
||||
shadowHOver = "transparent"
|
||||
}
|
||||
let lightnum = getLuminance(hoverHsla, 'hsla');
|
||||
let h = hsla.getNumber('h')!;
|
||||
let maxLightNum = 0.55
|
||||
if(h>=31&&h<=90){
|
||||
maxLightNum = 0.60
|
||||
}
|
||||
let o = {
|
||||
default: {
|
||||
background: hslaToRgbCss(hsla),
|
||||
borderColor: hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: Math.min(Math.max(0, hsla.getNumber("l")! - 3), hsla.getNumber("l")!) }),
|
||||
fontColor: lightnum < maxLightNum && hsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
|
||||
shadow: shadow
|
||||
},
|
||||
active: {
|
||||
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.min(Math.max(0, hoverHsla.getNumber("l")! - 5), hoverHsla.getNumber("l")!) }),
|
||||
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.max(Math.max(0, hoverHsla.getNumber("l")! - 10), hoverHsla.getNumber("l")!) }),
|
||||
fontColor: lightnum < maxLightNum && hoverHsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
|
||||
shadow: shadowHOver
|
||||
}
|
||||
} as UTSJSONObject
|
||||
|
||||
return o;
|
||||
}
|
||||
const hslColorMap = new Map<string,number[]>([
|
||||
['红色', [331, 360]], // 0-30度
|
||||
['红色', [0, 30]], // 0-30度
|
||||
['橙色', [31, 60]], // 30-60度
|
||||
['黄色', [61, 90]], // 60-90度
|
||||
['绿色', [91, 150]], // 90-150度
|
||||
['青色', [151, 210]], // 150-210度
|
||||
['蓝色', [211, 270]], // 210-270度
|
||||
['紫色', [271, 330]] // 270-330度
|
||||
]);
|
||||
|
||||
|
||||
/**
|
||||
* 处理相主题色。
|
||||
* @param color 需要处理的颜色值
|
||||
* @param hoverColor 对应按下去的颜色默认与color相等。
|
||||
* @returns UTSJSONObject
|
||||
*/
|
||||
export function getTextColorObj(color : string, hoverColor : string, isCoverDark ?: boolean) : UTSJSONObject {
|
||||
let dark = isCoverDark == null ? false : isCoverDark
|
||||
// #ifdef WEB
|
||||
dark = isCoverDark == undefined ? false : dark
|
||||
// #endif
|
||||
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
|
||||
let hsla2 : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
|
||||
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
|
||||
// let fontcolor = getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l:30 })
|
||||
// let bordercolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?21:92 })
|
||||
let bgcolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark == true ? 20 : 95 })
|
||||
|
||||
if (dark) {
|
||||
let p = hsla2;
|
||||
// p.set('l',90)
|
||||
// fontcolor = hslaToRgbCss(p)
|
||||
p.set('l', 20)
|
||||
p.set('s', (p.getNumber('s')) != 0 ? 0 : 20)
|
||||
bgcolor = hslaToRgbCss(p)
|
||||
// p.set('l',22)
|
||||
// bordercolor = hslaToRgbCss(p)
|
||||
}
|
||||
let lightnum = getLuminance(hoverHsla, 'hsla');
|
||||
let h = hsla.getNumber('h')!;
|
||||
let maxLightNum = 0.3
|
||||
// if(h>=31&&h<=90){
|
||||
// maxLightNum = 1
|
||||
// }
|
||||
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
|
||||
let o = {
|
||||
default: {
|
||||
background: "transparent",
|
||||
borderColor: "transparent",
|
||||
fontColor: lightnum < maxLightNum && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
|
||||
shadow: "transparent",
|
||||
},
|
||||
active: {
|
||||
background: bgcolor,
|
||||
borderColor: "transparent",
|
||||
fontColor: lightnum < maxLightNum && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
|
||||
shadow: "transparent",
|
||||
}
|
||||
} as UTSJSONObject
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理相主题色。
|
||||
* @param color 需要处理的颜色值
|
||||
* @param hoverColor 对应按下去的颜色默认与color相等。
|
||||
* @returns UTSJSONObject
|
||||
*/
|
||||
export function getThinColorObj(color : string, hoverColor : string, isCoverDark ?: boolean) : UTSJSONObject {
|
||||
let dark = isCoverDark == null ? false : isCoverDark
|
||||
// #ifdef WEB
|
||||
dark = isCoverDark == undefined ? false : dark
|
||||
// #endif
|
||||
|
||||
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
|
||||
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
|
||||
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
|
||||
let fontcolor = getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 })
|
||||
let bordercolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark == true ? 21 : 92 })
|
||||
let bgcolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark == true ? 20 : 95 })
|
||||
let lightnum = getLuminance(hoverHsla, 'hsla');
|
||||
if (dark) {
|
||||
let p = hsla;
|
||||
p.set('l', 98)
|
||||
fontcolor = hslaToRgbCss(p)
|
||||
p.set('l', 20)
|
||||
p.set('s', (p.getNumber('s')) != 0 ? 5 : 20)
|
||||
bgcolor = hslaToRgbCss(p)
|
||||
p.set('l', 22)
|
||||
bordercolor = hslaToRgbCss(p)
|
||||
}
|
||||
let o = {
|
||||
default: {
|
||||
background: bgcolor,
|
||||
borderColor: bordercolor,
|
||||
fontColor: fontcolor,
|
||||
shadow: "transparent",
|
||||
},
|
||||
active: {
|
||||
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: dark ? 10 : 50, a: hoverHsla.getNumber("a")!, l: dark == true ? 15 : 88 }),
|
||||
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: 0, l: dark == true ? 18 : 80 }),
|
||||
fontColor: lightnum < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 64 }),
|
||||
shadow: "transparent",
|
||||
}
|
||||
} as UTSJSONObject
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理相主题色。
|
||||
* @param color 需要处理的颜色值
|
||||
* @param hoverColor 对应按下去的颜色默认与color相等。
|
||||
* @returns UTSJSONObject
|
||||
*/
|
||||
export function getOutlineColorObj(color : string, hoverColor : string, isCoverDark ?: boolean) : UTSJSONObject {
|
||||
let dark = isCoverDark == null ? false : isCoverDark
|
||||
// #ifdef WEB
|
||||
dark = isCoverDark == undefined ? false : dark
|
||||
// #endif
|
||||
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
|
||||
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
|
||||
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
|
||||
let lightnum = getLuminance(hoverHsla, 'hsla');
|
||||
let h = hsla.getNumber('h')!;
|
||||
let borderLight = 70;
|
||||
// let maxLightNum = 0.55
|
||||
if(dark){
|
||||
borderLight = 50
|
||||
}
|
||||
let o = {
|
||||
default: {
|
||||
background: 'transparent',
|
||||
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: 1, l: borderLight+5 }),
|
||||
fontColor: lightnum < 0.6 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
|
||||
shadow: "transparent",
|
||||
},
|
||||
|
||||
active: {
|
||||
background: 'transparent',
|
||||
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: 1, l: borderLight }),
|
||||
fontColor: lightnum < 0.6 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
|
||||
shadow: "transparent",
|
||||
}
|
||||
} as UTSJSONObject
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置颜色的亮度值。29
|
||||
* isCoverDark:是根据亮度值,是否要设置n的值,如果n超过了
|
||||
* 指定的30就不操作。
|
||||
*/
|
||||
export function setTextColorLightByDark(color : string) : string {
|
||||
let realColor = getDefaultColor(color);
|
||||
let hsla = rgbToHsl(hexToRgb(realColor))
|
||||
let l = hsla.getNumber('l')!
|
||||
let s = hsla.getNumber('s')!
|
||||
if (l < 30) {
|
||||
if (s > 0) {
|
||||
hsla.set('l', 50)
|
||||
} else {
|
||||
hsla.set('l', 100)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return hslaToRgbCss(hsla)
|
||||
}
|
||||
/**
|
||||
* 变动的颜色是否需要变深。如果不需要返回原始颜色
|
||||
* 如果果需要自动加深。
|
||||
*/
|
||||
export function setBgColorLightByDark(color : string) : string {
|
||||
let realColor = getDefaultColor(color);
|
||||
let hsla = rgbToHsl(hexToRgb(realColor))
|
||||
let l = hsla.getNumber('l')!
|
||||
let s = hsla.getNumber('s')!
|
||||
if (s == 0 && l > 50) {
|
||||
hsla.set('l', 100 - l)
|
||||
}
|
||||
if (s > 0) {
|
||||
if (l > 50) {
|
||||
hsla.set('l', 100 - l)
|
||||
}
|
||||
}
|
||||
return hslaToRgbCss(hsla)
|
||||
}
|
||||
|
||||
export function isBlackAndWhite(color : string) : boolean {
|
||||
let realColor = getDefaultColor(color);
|
||||
let hsla = rgbToHsl(hexToRgb(realColor))
|
||||
let s = hsla.getNumber('s')!
|
||||
return s == 0
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
import { getDefaultColor } from "./xCoreColorUtil.uts";
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
/**
|
||||
* 数字转16进制
|
||||
*/
|
||||
function toHex(numbers : number) : string {
|
||||
// let i = 0 as number
|
||||
// if (numbers === i) {
|
||||
// return '00';
|
||||
// }
|
||||
// let hex = '';
|
||||
// const hexChars = '0123456789abcdef';
|
||||
// while (numbers > 0) {
|
||||
// const remainder = numbers % 16;
|
||||
// hex = hexChars[remainder] + hex;
|
||||
// numbers = Math.floor(numbers / 16);
|
||||
// }
|
||||
let n = numbers.toString(16) as string;
|
||||
if(n.length==1){
|
||||
n = '0'+n
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机一个uid
|
||||
* @param rdix 随机因子
|
||||
* @param length 取的长度
|
||||
* @param isAddStr 是否限制随机结果中的长度,不允许输出长度
|
||||
* @returns String
|
||||
*/
|
||||
function getUid(rdix = 1, length = 12) : string {
|
||||
let ix = "";
|
||||
// #ifndef APP
|
||||
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length);
|
||||
// #endif
|
||||
|
||||
// #ifdef APP
|
||||
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length as Int);
|
||||
// #endif
|
||||
return ix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 给定一个值,来填充边距所需要的数组值
|
||||
* @param val any
|
||||
* @returns [左,上,右,下]
|
||||
*/
|
||||
function toFillMarginAr(val : number[]) : number[] {
|
||||
|
||||
let ar : number[] = [];
|
||||
|
||||
if (val.length == 1) {
|
||||
let firstEl = val[0];
|
||||
ar = [firstEl, firstEl, firstEl, firstEl]
|
||||
|
||||
} else if (val.length == 2) {
|
||||
ar = [val[0], val[1], val[0], val[1]]
|
||||
|
||||
} else if (val.length == 3) {
|
||||
ar = [val[0], val[1], val[2], 0]
|
||||
}
|
||||
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
|
||||
function rpx2px(n : number, _w = 750) : number {
|
||||
let r = n
|
||||
// #ifdef APP
|
||||
r = uni.rpx2px(n);
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
|
||||
function getLayoutRatio() : number {
|
||||
const devicePixelRatio = window.devicePixelRatio || 1;
|
||||
const screenWidth = window.innerWidth
|
||||
|
||||
if (screenWidth <= 950) {
|
||||
// 小屏幕设备
|
||||
return screenWidth / 750;
|
||||
} else {
|
||||
return 0.5
|
||||
}
|
||||
}
|
||||
|
||||
if (uni?.rpx2px) {
|
||||
r = uni.rpx2px(n)
|
||||
} else {
|
||||
r = getLayoutRatio() * n
|
||||
}
|
||||
// #endif
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
function px2dp(n : number) : number {
|
||||
let w = n;
|
||||
// #ifdef APP
|
||||
|
||||
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
|
||||
// 屏幕逻辑像素的宽度
|
||||
let width = mets.widthPixels;
|
||||
// 屏幕宽度
|
||||
let screenWidth = uni.getWindowInfo().screenWidth
|
||||
w = n / (width / screenWidth);
|
||||
// #endif
|
||||
return w
|
||||
}
|
||||
|
||||
function checkIsCssUnit(str : string|number, unit : string) : string {
|
||||
if(typeof str != 'string'){
|
||||
return (str as number).toString() + unit;
|
||||
}
|
||||
let s = str as string;
|
||||
if (s.indexOf("px") > -1 || s.indexOf("%") > -1 || s.indexOf("auto") > -1 || s.indexOf("vw") > -1 || s.indexOf("vh") > -1) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return s + (unit==''?'px':unit)
|
||||
}
|
||||
|
||||
// function checkIsCssUnit(str : string|number, unit : string) : string {
|
||||
// let screenWidth = uni.getWindowInfo().windowWidth;
|
||||
// let base = 0;
|
||||
// let baseUnit = unit == ''?xConfig.unit:unit;
|
||||
// if(typeof str != 'string'){
|
||||
// base = str as number;
|
||||
// }else if(typeof str == 'string'){
|
||||
// let s = str as string;
|
||||
// if (
|
||||
// s.indexOf("px") > -1 ||
|
||||
// s.indexOf("rpx") > -1 ||
|
||||
// s.indexOf("%") > -1 ||
|
||||
// s.indexOf("auto") > -1 ||
|
||||
// s.indexOf("vw") > -1 ||
|
||||
// s.indexOf("rem") > -1 ||
|
||||
// s.indexOf("em") > -1 ||
|
||||
// s.indexOf("in") > -1 ||
|
||||
// s.indexOf("vh") > -1)
|
||||
// {
|
||||
// return s;
|
||||
// }
|
||||
// base = parseFloat(s);
|
||||
// base = isNaN(base)?0:base
|
||||
// }
|
||||
|
||||
// if(baseUnit=='rpx'){
|
||||
// let baseDesize = Math.max(xConfig.designSize,375)
|
||||
// let baseMaxWidth = Math.max(xConfig.maximumCalculatedSize,375)
|
||||
// let origSize = 375;
|
||||
// let ratio = 1 - baseDesize / origSize;
|
||||
// let maxRatio = screenWidth / baseMaxWidth;
|
||||
// let baseSize = base - (ratio*base)
|
||||
// // 说明超过了设定缩放的最大屏幕尺寸。
|
||||
// if(maxRatio>=1){
|
||||
// base = baseSize;
|
||||
// }else{
|
||||
// const calcRatio = Math.max(screenWidth/baseDesize,1)
|
||||
// base = calcRatio*baseSize ;
|
||||
// }
|
||||
// let baseunitReal = 'px'
|
||||
// // #ifdef H5
|
||||
// if(base<12){
|
||||
// base = base/16
|
||||
// baseunitReal = "rem"
|
||||
// }
|
||||
// // #endif
|
||||
// return base + baseunitReal
|
||||
// }
|
||||
|
||||
// return base + baseUnit
|
||||
// }
|
||||
|
||||
function fillArrayCssValue(val : Array<string>) : string[] {
|
||||
let ar : string[] = val.map((el : string) : string => {
|
||||
return checkIsCssUnit(el, xConfig.unit)
|
||||
})
|
||||
if (ar.length == 0) return [];
|
||||
if (ar.length == 1) return [ar[0], ar[0], ar[0], ar[0]]
|
||||
if (ar.length == 2) {
|
||||
return [ar[1], ar[0], ar[1], ar[0]]
|
||||
}
|
||||
if (ar.length == 3) return [ar[1], ar[2], '0px', ar[0]]
|
||||
return [ar[1], ar[2], ar[3], ar[0]];
|
||||
}
|
||||
function fillArrayCssValueByround(val : Array<string>) : string[] {
|
||||
let ar : string[] = val.map((el : string) : string => {
|
||||
return checkIsCssUnit(el, xConfig.unit)
|
||||
})
|
||||
if (ar.length == 0) return [];
|
||||
if (ar.length == 1) return [ar[0], ar[0], ar[0], ar[0]]
|
||||
if (ar.length == 2) {
|
||||
return [ar[1], ar[0], ar[1], ar[0]]
|
||||
}
|
||||
if (ar.length == 3) return [ar[0], ar[1], ar[0], '0px']
|
||||
return [ar[0], ar[1], ar[2], ar[3]];
|
||||
}
|
||||
function fillArrayCssValueBycolor(val : Array<string>) : string[] {
|
||||
let ar : string[] = val.map((el : string) : string => {
|
||||
return getDefaultColor(el)
|
||||
})
|
||||
if (ar.length == 0) return [];
|
||||
if (ar.length == 1) return [ar[0], ar[0], ar[0], ar[0]]
|
||||
if (ar.length == 2) {
|
||||
return [ar[1], ar[0], ar[1], ar[0]]
|
||||
}
|
||||
if (ar.length == 3) return [ar[1], ar[2], '0px', ar[0]]
|
||||
return [ar[1], ar[2], ar[3], ar[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数组进行分组。按数量分组
|
||||
*/
|
||||
function splitArray<T>(target : Array<T>, value : number) : Array<Array<T>> {
|
||||
var result = [] as Array<Array<T>>;
|
||||
for (var i = 0; i < target.length; i += value) {
|
||||
let ml = target.slice(i, i + value);
|
||||
result.push(ml);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 对数组进行分组。按指定组数量
|
||||
*/
|
||||
function splitArrayByGroup<T>(target : Array<T>, group : number) : Array<Array<T>> {
|
||||
var groupSize = Math.ceil(target.length / group);
|
||||
var result = [] as Array<Array<T>>;
|
||||
for (var i = 0; i < target.length; i += groupSize) {
|
||||
result.push(target.slice(i, i + groupSize));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符的css单位。
|
||||
*/
|
||||
function getUnit(n ?: string) : string {
|
||||
if (n == null || n == '') return xConfig.unit;
|
||||
let unit = n.replace(/[\d|\-|\+|\.]/g, '');
|
||||
if (unit == "") {
|
||||
unit = xConfig.unit;
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前页面是否刷新。
|
||||
*/
|
||||
function setPagePullRefresh(enbledpull : boolean) {
|
||||
// #ifdef APP
|
||||
let pages = getCurrentPages()
|
||||
let page = pages[pages.length - 1]
|
||||
// let pageJson = page.$getPageStyle()
|
||||
// pageJson.set("enablePullDownRefresh",enbledpull)
|
||||
page.$setPageStyle({ "enablePullDownRefresh": enbledpull } as UTSJSONObject)
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的下拉状态。
|
||||
*/
|
||||
function getPagePullRefresh() : boolean {
|
||||
// #ifdef APP
|
||||
let pages = getCurrentPages()
|
||||
let page = pages[pages.length - 1]
|
||||
let pageJson = page.$getPageStyle()
|
||||
|
||||
let enb = pageJson.get("enablePullDownRefresh") as boolean|null;
|
||||
if(enb==null) return false;
|
||||
return enb as boolean;
|
||||
// #endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖
|
||||
*/
|
||||
function debounce(
|
||||
func : (args : any) => void,
|
||||
wait : number,
|
||||
immediate ?: boolean
|
||||
) : (args : any) => number | null {
|
||||
let timeout : number | null = null;
|
||||
let callNow = immediate == null ? true : (immediate! as boolean);
|
||||
|
||||
// 返回的函数是实际被调用的防抖函数
|
||||
return (args : any) : number | null => {
|
||||
if (timeout != null && callNow == true) {
|
||||
return timeout;
|
||||
}
|
||||
if (timeout != null) {
|
||||
clearTimeout(timeout! as number);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
// 决定是否立即执行函数
|
||||
if (callNow == true) {
|
||||
|
||||
func(args)
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
}, wait);
|
||||
return timeout;
|
||||
}
|
||||
// 否则,设置定时器以稍后执行函数
|
||||
timeout = setTimeout(() => {
|
||||
func(args)
|
||||
|
||||
}, wait);
|
||||
return timeout
|
||||
}
|
||||
}
|
||||
export {
|
||||
toHex,
|
||||
toFillMarginAr,
|
||||
getUid,
|
||||
checkIsCssUnit,
|
||||
px2dp,
|
||||
rpx2px,
|
||||
splitArray,
|
||||
splitArrayByGroup,
|
||||
fillArrayCssValue,
|
||||
fillArrayCssValueByround,
|
||||
fillArrayCssValueBycolor,
|
||||
getUnit,
|
||||
setPagePullRefresh,
|
||||
getPagePullRefresh,
|
||||
debounce
|
||||
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
# xDate
|
||||
|
||||
### 开发文档
|
||||
[TMUI4.0文档](https://xui.tmui.design/)
|
||||
[TMUI4.0组件库](https://ext.dcloud.net.cn/plugin?id=16369)
|
||||
|
||||
日期处理库
|
||||
|
||||
### 说明
|
||||
这是tmui4.0|XUI的日期处理库,提供了丰富的日期操作功能。简单好上手,单库只有14kb,能满足大部分日期处理需求。库采用类Jq式的链式调用,使用灵活方便。
|
||||
|
||||
### 兼容性
|
||||
|
||||
| IOS | Android | WEB | 小程序 |
|
||||
| --- | --- | --- | --- |
|
||||
| 支持 | 支持 | 支持 | 支持 |
|
||||
|
||||
### 导入使用
|
||||
在页面中直接使用快捷导入即可使用,它是class类,需要new
|
||||
```ts
|
||||
import {xDate, createDate} from "@/uni_modules/tmx-ui/index.uts"
|
||||
// 打印当前
|
||||
console.log(new xDate().date)
|
||||
// 辅助函数用来创建系统的Date对象时间
|
||||
console.log(createDate('2023-3-1'))
|
||||
```
|
||||
|
||||
### 创建实例
|
||||
支持string, number, Date, null四种类型
|
||||
```ts
|
||||
import {xDate, createDate} from "@/uni_modules/tmx-ui/index.uts"
|
||||
// 为空,默认是现在
|
||||
const now = new xDate()
|
||||
// 传递字符串的毫秒数
|
||||
const date1 = new xDate('15888888')
|
||||
// 传递字符串的时间
|
||||
const date2 = new xDate('2023-2-3')
|
||||
const date3 = new xDate('2023/2/3')
|
||||
// 传递Date对象
|
||||
const date4 = new xDate(new Date())
|
||||
```
|
||||
### 实例方法
|
||||
| 名称 | 参数 | 介绍 |
|
||||
| --- | --- | --- |
|
||||
| format | s: string \| null = null | 格式化日期,默认格式为YYYY/MM/DD hh:mm:ss |
|
||||
| getYear | 无 | 获取当前年份 |
|
||||
| getMonth | 无 | 获取当前月份(0-11,0代表1月) |
|
||||
| getMonthName | useShort: boolean = false | 获取当前月份的名称(支持国际化) |
|
||||
| getDate | 无 | 获取当前日(1-31) |
|
||||
| getHours | 无 | 获取当前小时(0-23) |
|
||||
| getMinutes | 无 | 获取当前分钟(0-59) |
|
||||
| getSeconds | 无 | 获取当前秒(0-59) |
|
||||
| getDateOf | d: xDateTypeTime = 'd' | 获取指定类型的数据,d可为'y'年,'m'月,'d'日,'h'时,'M'分,'s'秒,'ms'毫秒,'w'周 |
|
||||
| setDateOf | n: number, d: xDateTypeTime = 'd' | 设定日期,支持链式调用 |
|
||||
| getTime | d: xDateTypeTime | 获取时间戳,可指定单位 |
|
||||
| getClone | 无 | 获取当前时间的副本 |
|
||||
| getWeek | 无 | 获取本年的第几周 |
|
||||
| getDateStartOf | d: 'm' \| 'w' \| 'y' = 'm' | 获取时间的第一天(月/周/年) |
|
||||
| getDateEndOf | d: 'm' \| 'w' \| 'y' = 'm' | 获取时间的最后一天(月/周/年) |
|
||||
| getWeekDay | 无 | 获取星期几(0-6,0表示星期天) |
|
||||
| getWeekDayCn | model: string[] \| null = null, useShort: boolean = true | 获取星期名称(支持国际化) |
|
||||
| getMonthCountDay | 无 | 获取当前月份的最大天数 |
|
||||
| getDateInfo | str: string \| null = null | 获取日期详细信息 |
|
||||
| getDaysOf | d: 'm' \| 'w' = 'm' | 获取日期数组(月/周) |
|
||||
| add | count: number, d: xDateTypeTime = 'd' | 日期加法操作,支持链式调用 |
|
||||
| subtraction | count: number, d: xDateTypeTime = 'd' | 日期减法操作,支持链式调用 |
|
||||
| isBetween | start: xDate, end: xDate, type: xDateTypeTime = 'ms', d: '()' \| '[]' \| '(]' \| '[)' = '[]' | 判断日期是否在区间内 |
|
||||
| isBetweenOf | targetDate: xDate, d: '>' \| '>=' \| '<' \| '<=' \| '=' = '>', type: xDateTypeTime = 'ms' | 与目标日期比较 |
|
||||
| fromBetweenLongTime | target: any, model: Map<number, string> \| null, format: string \| null | 计算与目标时间过了多久(支持国际化) |
|
||||
| diffTime | target: any, type: xDateTypeTime = 's' | 计算与目标时间相差多少个单位 |
|
||||
| getQuarter | type: string = '' | 获取季度信息 |
|
||||
| setDateLocale | locale: xDateLanguage | 设置xDate的语言 |
|
||||
| getDateLocale | 无 | 获取当前xDate使用的语言 |
|
||||
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
// 日期时间单位类型
|
||||
export type xDateTypeTime = 'y' | 'm' | 'd' | 'h' | 'M' | 's' | 'ms' | 'w'
|
||||
|
||||
// 季度信息类型
|
||||
export type xDateTypeQuarter = {
|
||||
quarter : number,
|
||||
start : string,
|
||||
end : string
|
||||
}
|
||||
|
||||
// 日期格式类型
|
||||
export type DateFormat = 'RFC2822' | 'ISO8601' | 'CUSTOM';
|
||||
|
||||
// 日期信息类型
|
||||
export type xDateDayInfoType = {
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
hours: number,
|
||||
minutes: number,
|
||||
seconds: number,
|
||||
week: number,
|
||||
weeks: number,
|
||||
weekCn: string,
|
||||
date: string
|
||||
}
|
||||
|
||||
// 支持的语言类型
|
||||
export type xDateLanguage = 'zh-Hans' | 'en' | 'ja' | 'ko' | 'zh-Hant' | 'fr' | 'ru';
|
||||
|
||||
// 国际化相对时间配置类型
|
||||
export type xDateI18nTypeRelativeTime = {
|
||||
future: string,
|
||||
past: string,
|
||||
s: string,
|
||||
m: string,
|
||||
mm: string,
|
||||
h: string,
|
||||
hh: string,
|
||||
d: string,
|
||||
dd: string,
|
||||
M: string,
|
||||
MM: string,
|
||||
y: string,
|
||||
yy: string
|
||||
}
|
||||
|
||||
// 国际化配置类型
|
||||
export type xDateI18nType = {
|
||||
weekdays: string[],
|
||||
weekdaysShort: string[],
|
||||
months: string[],
|
||||
monthsShort: string[],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean) => string,
|
||||
relativeTime: xDateI18nTypeRelativeTime
|
||||
}
|
||||
```
|
||||
|
||||
### 国际化相关API
|
||||
|
||||
```ts
|
||||
// 设置xDate的语言
|
||||
setDateLocale(locale: xDateLanguage): void
|
||||
|
||||
// 获取当前xDate使用的语言
|
||||
getDateLocale(): xDateLanguage
|
||||
|
||||
// 获取当前月份的名称
|
||||
getMonthName(useShort: boolean = false): string
|
||||
|
||||
// 获取星期名称(支持国际化)
|
||||
getWeekDayCn(model: string[] | null = null, useShort: boolean = true): string
|
||||
```
|
||||
```
|
||||
|
||||
### API说明
|
||||
|
||||
#### 辅助函数
|
||||
|
||||
##### createDate
|
||||
```ts
|
||||
function createDate(dateStrs: string): Date
|
||||
```
|
||||
- 描述:用来解析非标准时间以及各种奇怪的时间格式
|
||||
- 参数:
|
||||
- dateStrs: 日期字符串,支持多种格式如YYYY、YYYY-MM、YYYY-MM-DD等
|
||||
- 返回值:Date对象
|
||||
|
||||
##### dateCovertXdate
|
||||
```ts
|
||||
function dateCovertXdate(date: Date): xDate
|
||||
```
|
||||
- 描述:将一个日期转换为xDate对象
|
||||
- 参数:
|
||||
- date: Date对象
|
||||
- 返回值:xDate对象
|
||||
|
||||
#### xDate类方法
|
||||
|
||||
##### 构造函数
|
||||
```ts
|
||||
constructor(dateStr: string | number | Date | null = null)
|
||||
```
|
||||
- 描述:创建xDate实例
|
||||
- 参数:
|
||||
- dateStr: 可以是字符串、数字、Date对象或null(默认为当前时间)
|
||||
|
||||
##### detectDateFormat
|
||||
```ts
|
||||
detectDateFormat(dateStr: string): DateFormat
|
||||
```
|
||||
- 描述:检测字符串的日期格式类型
|
||||
- 参数:
|
||||
- dateStr: 日期字符串
|
||||
- 返回值:日期格式类型('RFC2822'、'ISO8601'或'CUSTOM')
|
||||
|
||||
##### format
|
||||
```ts
|
||||
format(s: string | null = null): string
|
||||
```
|
||||
- 描述:格式化日期
|
||||
- 参数:
|
||||
- s: 模板,比如YYYY/MM/DD hh:mm:ss,默认为YYYY/MM/DD hh:mm:ss
|
||||
- 返回值:格式化后的日期字符串
|
||||
|
||||
##### getYear
|
||||
```ts
|
||||
getYear(): number
|
||||
```
|
||||
- 描述:获取当前年
|
||||
- 返回值:根据当地时间,返回一个对应于给定日期的年份数字
|
||||
|
||||
##### getMonth
|
||||
```ts
|
||||
getMonth(): number
|
||||
```
|
||||
- 描述:获取当前月(0-11,0代表1月)
|
||||
- 返回值:一个0到11的整数值
|
||||
|
||||
##### getMonthName
|
||||
```ts
|
||||
getMonthName(useShort: boolean = false): string
|
||||
```
|
||||
- 描述:获取当前月份的名称(支持国际化)
|
||||
- 参数:
|
||||
- useShort: 是否使用短名称,默认为false
|
||||
- 返回值:返回当前语言环境下的月份名称
|
||||
|
||||
##### getDate
|
||||
```ts
|
||||
getDate(): number
|
||||
```
|
||||
- 描述:获取当前天
|
||||
- 返回值:返回一个1到31的整数值
|
||||
|
||||
##### getHours
|
||||
```ts
|
||||
getHours(): number
|
||||
```
|
||||
- 描述:获取当前小时
|
||||
- 返回值:返回一个0到23之间的整数值
|
||||
|
||||
##### getMinutes
|
||||
```ts
|
||||
getMinutes(): number
|
||||
```
|
||||
- 描述:获取当前分钟
|
||||
- 返回值:返回一个0到59的整数值
|
||||
|
||||
##### getSeconds
|
||||
```ts
|
||||
getSeconds(): number
|
||||
```
|
||||
- 描述:获取当前秒
|
||||
- 返回值:返回一个0到59的整数值
|
||||
|
||||
##### getDateOf
|
||||
```ts
|
||||
getDateOf(d: xDateTypeTime = 'd'): number
|
||||
```
|
||||
- 描述:获取指定类型的数据
|
||||
- 参数:
|
||||
- d: 'y'年 'm'月 'd'日 'h'时 'M'分 's'秒 'ms'毫秒 'w'周
|
||||
- 返回值:指定类型的数值
|
||||
|
||||
##### setDateOf
|
||||
```ts
|
||||
setDateOf(n: number, d: xDateTypeTime = 'd'): xDate
|
||||
```
|
||||
- 描述:设定日期
|
||||
- 参数:
|
||||
- n: 设定的数据
|
||||
- d: 比如d=y,那么就是设置本日期的年份数据
|
||||
- 返回值:xDate对象实例(支持链式调用)
|
||||
|
||||
##### getTime
|
||||
```ts
|
||||
getTime(d: xDateTypeTime): number
|
||||
```
|
||||
- 描述:获取时间戳
|
||||
- 参数:
|
||||
- d: 'y'年 'm'月 'd'日 'h'时 'M'分 's'秒 'ms'毫秒
|
||||
- 返回值:返回从UTC时间1970年1月1日午夜开始以毫秒为单位存储的时间值
|
||||
- 注意:如果不提供d就默认返回毫秒,比如设置d=m那么后面的h,M,s,ms会被设置为0
|
||||
|
||||
##### getClone
|
||||
```ts
|
||||
getClone(): xDate
|
||||
```
|
||||
- 描述:当前时间的副本
|
||||
- 返回值:新的xDate对象
|
||||
|
||||
##### getWeek
|
||||
```ts
|
||||
getWeek(): number
|
||||
```
|
||||
- 描述:本年的第几周
|
||||
- 返回值:周次
|
||||
|
||||
##### getDateStartOf
|
||||
```ts
|
||||
getDateStartOf(d: 'm' | 'w' | 'y' = 'm'): xDate
|
||||
```
|
||||
- 描述:取时间的第一天
|
||||
- 参数:
|
||||
- d: 'm'表示取本月的第一天,'w'取本周的第一天,'y'表示本年的第一天
|
||||
- 返回值:返回xDate对象
|
||||
|
||||
##### getDateEndOf
|
||||
```ts
|
||||
getDateEndOf(d: 'm' | 'w' | 'y' = 'm'): xDate
|
||||
```
|
||||
- 描述:取时间的最后一天
|
||||
- 参数:
|
||||
- d: 'm'表示取本月的最后一天,'w'取本周的最后一天,'y'表示本年的最后一天
|
||||
- 返回值:返回xDate对象
|
||||
|
||||
##### getWeekDay
|
||||
```ts
|
||||
getWeekDay(): number
|
||||
```
|
||||
- 描述:根据本地时间,返回一个具体日期中一周的第几天,0表示星期天
|
||||
- 返回值:根据本地时间,返回一个0到6之间的整数值
|
||||
|
||||
##### getWeekDayCn
|
||||
```ts
|
||||
getWeekDayCn(model: string[] | null = null, useShort: boolean = true): string
|
||||
```
|
||||
- 描述:获取星期名称(支持国际化)
|
||||
- 参数:
|
||||
- model: 具有星期的中文模板,请按照顺序放置如["周日","周一","周二","周三","周四","周五","周六"]
|
||||
- useShort: 是否使用短名称,默认为true
|
||||
- 返回值:返回当前语言环境下的星期名称
|
||||
|
||||
##### getMonthCountDay
|
||||
```ts
|
||||
getMonthCountDay(): number
|
||||
```
|
||||
- 描述:返回当前日期本月的最大天数
|
||||
- 返回值:当前月份的最大天数
|
||||
|
||||
##### getDateInfo
|
||||
```ts
|
||||
getDateInfo(str: string | null = null): xDateDayInfoType
|
||||
```
|
||||
- 描述:返回当日的信息
|
||||
- 参数:
|
||||
- str: 日期字符串,为null时使用当前xDate实例
|
||||
- 返回值:返回一个包含年月日周次,星期,农历等的对象
|
||||
|
||||
##### getDaysOf
|
||||
```ts
|
||||
getDaysOf(d: 'm' | 'w' = 'm'): xDateDayInfoType[]
|
||||
```
|
||||
- 描述:返回日期数组
|
||||
- 参数:
|
||||
- d: 'm'表示返回本月的日期数据,'w'表示返回本周的日期数据
|
||||
- 返回值:返回一个日期数组
|
||||
|
||||
##### add
|
||||
```ts
|
||||
add(count: number, d: xDateTypeTime = 'd'): xDate
|
||||
```
|
||||
- 描述:为当前日期进行加操作
|
||||
- 参数:
|
||||
- count: 要增加的数量
|
||||
- d: 'y'年 'm'月 'd'日 'h'时 'M'分 's'秒 'w'周
|
||||
- 返回值:xDate对象(支持链式调用)
|
||||
|
||||
##### subtraction
|
||||
```ts
|
||||
subtraction(count: number, d: xDateTypeTime = 'd'): xDate
|
||||
```
|
||||
- 描述:为当前日期进行减操作
|
||||
- 参数:
|
||||
- count: 要减少的数量
|
||||
- d: 'y'年 'm'月 'd'日 'h'时 'M'分 's'秒 'w'周
|
||||
- 返回值:xDate对象(支持链式调用)
|
||||
|
||||
##### isBetween
|
||||
```ts
|
||||
isBetween(start: xDate, end: xDate, type: xDateTypeTime = 'ms', d: '()' | '[]' | '(]' | '[)' = '[]'): boolean
|
||||
```
|
||||
- 描述:日期是否在一个区间内
|
||||
- 参数:
|
||||
- start: 开始日期
|
||||
- end: 结束日期
|
||||
- type: 要比较的单位,默认为ms
|
||||
- d: 区间类型,'()'不含起始'[]'包含起始'(]'不包含开始但包含结束'[)'包含开始但不包含结束
|
||||
- 返回值:是否在区间内
|
||||
|
||||
##### isBetweenOf
|
||||
```ts
|
||||
isBetweenOf(targetDate: xDate, d: '>' | '>=' | '<' | '<=' | '=' = '>', type: xDateTypeTime = 'ms'): boolean
|
||||
```
|
||||
- 描述:与目标日期比较
|
||||
- 参数:
|
||||
- targetDate: 要比较的日期
|
||||
- d: 比较类型,'>'大于目标日期,'>='大于等于目标'<'小于目标,'<='小于等于目标,'='全等
|
||||
- type: 要比较的单位
|
||||
- 返回值:比较结果
|
||||
|
||||
##### fromBetweenLongTime
|
||||
```ts
|
||||
fromBetweenLongTime(target: any, model: Map<number, string> | null, format: string | null): string
|
||||
```
|
||||
- 描述:与目标时间过了多久(支持国际化)
|
||||
- 参数:
|
||||
- target: 要对比的相对时间,如果填写null就表示当前时间以来的的多久
|
||||
- model: 如果为null或者为空的map采用默认值,map格式key表示以秒为单位的时间,value为对应的文本
|
||||
- format: 超过最大值最使用格式化日期,如果为null默认为YYYY-MM-DD
|
||||
- 返回值:返回对应的时间文本
|
||||
|
||||
##### diffTime
|
||||
```ts
|
||||
diffTime(target: any, type: xDateTypeTime = 's'): number
|
||||
```
|
||||
- 描述:与目标时间相差多少个单位
|
||||
- 参数:
|
||||
- target: 目标时间
|
||||
- type: 相差的单位,默认:s秒
|
||||
- 返回值:返回以xDateTypeTime为单位相关的时间数
|
||||
|
||||
##### getQuarter
|
||||
```ts
|
||||
getQuarter(type: string = ''): xDateTypeQuarter[]
|
||||
```
|
||||
- 描述:获取季度
|
||||
- 参数:
|
||||
- type: 'y'表示获取本年的4个季度,空值表示获取当前时间所在的季度
|
||||
- 返回值:返回一个季度数组{quarter:number,start:string,end:string}
|
||||
|
||||
##### setDateLocale
|
||||
```ts
|
||||
setDateLocale(locale: xDateLanguage): void
|
||||
```
|
||||
- 描述:设置xDate的语言
|
||||
- 参数:
|
||||
- locale: 语言代码,支持'zh-Hans'(中文简体)、'en'(英文)、'ja'(日文)、'ko'(韩文)、'zh-Hant'(繁体中文)、'fr'(法语)、'ru'(俄语)
|
||||
- 返回值:无
|
||||
|
||||
##### getDateLocale
|
||||
```ts
|
||||
getDateLocale(): xDateLanguage
|
||||
```
|
||||
- 描述:获取当前xDate使用的语言
|
||||
- 返回值:当前语言代码
|
||||
|
||||
### 使用示例
|
||||
|
||||
#### 基本使用
|
||||
```ts
|
||||
import {xDate, createDate} from "@/uni_modules/tmx-ui/index.uts"
|
||||
|
||||
// 创建日期实例
|
||||
const date = new xDate('2023-05-15')
|
||||
|
||||
// 格式化日期
|
||||
console.log(date.format('YYYY年MM月DD日')) // 输出: 2023年05月15日
|
||||
|
||||
// 获取年月日
|
||||
console.log(date.getYear()) // 输出: 2023
|
||||
console.log(date.getMonth()) // 输出: 4 (5月)
|
||||
console.log(date.getDate()) // 输出: 15
|
||||
|
||||
// 链式调用
|
||||
const nextWeek = date.getClone().add(1, 'w')
|
||||
console.log(nextWeek.format()) // 输出下周同一天的日期
|
||||
|
||||
// 获取月份天数
|
||||
console.log(date.getMonthCountDay()) // 输出: 31 (5月有31天)
|
||||
|
||||
// 获取日期信息
|
||||
const info = date.getDateInfo()
|
||||
console.log(info.weekCn) // 输出: 周一 (假设5月15日是周一)
|
||||
|
||||
// 日期比较
|
||||
const date1 = new xDate('2023-01-01')
|
||||
const date2 = new xDate('2023-12-31')
|
||||
console.log(date.isBetween(date1, date2)) // 输出: true
|
||||
```
|
||||
|
||||
#### 高级用法
|
||||
```ts
|
||||
import {xDate} from "@/uni_modules/tmx-ui/index.uts"
|
||||
|
||||
// 获取本月所有日期
|
||||
const currentDate = new xDate()
|
||||
const daysInMonth = currentDate.getDaysOf('m')
|
||||
console.log(`本月共有 ${daysInMonth.length} 天`)
|
||||
|
||||
// 获取本周日期
|
||||
const daysInWeek = currentDate.getDaysOf('w')
|
||||
console.log(`本周的第一天是 ${daysInWeek[0].date}`)
|
||||
|
||||
// 获取季度信息
|
||||
const quarters = currentDate.getQuarter('y')
|
||||
console.log(`今年第一季度: ${quarters[0].start} 至 ${quarters[0].end}`)
|
||||
|
||||
// 计算两个日期之间的差距
|
||||
const birthday = new xDate('1990-01-01')
|
||||
const daysDiff = currentDate.diffTime(birthday, 'd')
|
||||
console.log(`距离生日已经过去了 ${daysDiff} 天`)
|
||||
|
||||
// 友好的时间显示
|
||||
const pastTime = new xDate().subtraction(3, 'h')
|
||||
const timeText = currentDate.fromBetweenLongTime(pastTime, null, null)
|
||||
console.log(timeText) // 输出: 3小时前
|
||||
```
|
||||
|
||||
#### 国际化使用示例
|
||||
```ts
|
||||
import {xDate} from "@/uni_modules/tmx-ui/index.uts"
|
||||
|
||||
// 创建日期实例
|
||||
const date = new xDate('2023-05-15')
|
||||
|
||||
// 默认使用中文简体
|
||||
console.log(date.getWeekDayCn()) // 输出: 周一 (假设5月15日是周一)
|
||||
console.log(date.getMonthName()) // 输出: 五月
|
||||
|
||||
// 切换到英文
|
||||
date.setDateLocale('en')
|
||||
console.log(date.getWeekDayCn()) // 输出: Mon
|
||||
console.log(date.getMonthName()) // 输出: May
|
||||
console.log(date.getMonthName(false)) // 输出: May (完整名称)
|
||||
|
||||
// 切换到日文
|
||||
date.setDateLocale('ja')
|
||||
console.log(date.getWeekDayCn()) // 输出: 月
|
||||
console.log(date.getMonthName()) // 输出: 5月
|
||||
|
||||
// 相对时间显示也会根据语言环境变化
|
||||
const currentDate = new xDate()
|
||||
const pastTime = new xDate().subtraction(3, 'h')
|
||||
|
||||
// 中文环境
|
||||
currentDate.setDateLocale('zh-Hans')
|
||||
console.log(currentDate.fromBetweenLongTime(pastTime, null, null)) // 输出: 3小时前
|
||||
|
||||
// 英文环境
|
||||
currentDate.setDateLocale('en')
|
||||
console.log(currentDate.fromBetweenLongTime(pastTime, null, null)) // 输出: 3 hours ago
|
||||
|
||||
// 获取当前语言设置
|
||||
console.log(currentDate.getDateLocale()) // 输出: en
|
||||
```
|
||||
@@ -0,0 +1,873 @@
|
||||
import { xDateDayInfoType } from "../../interface.uts"
|
||||
import { getCurrentLocale, getLocaleData, setLocale, xDateLanguage } from "./xDateI18n.uts"
|
||||
export type xDateTypeTime = 'y' | 'm' | 'd' | 'h' | 'M' | 's' | 'ms' | 'w'
|
||||
export type xDateTypeQuarter = {
|
||||
quarter : number,
|
||||
start : string,
|
||||
end : string
|
||||
}
|
||||
export type DateFormat = 'RFC2822' | 'ISO8601' | 'CUSTOM';
|
||||
|
||||
/**
|
||||
* 用来解析非标准时间以及各种奇怪的时间格式。
|
||||
*/
|
||||
export function createDate(dateStrs : string) : Date {
|
||||
const dateStr = dateStrs.replace(/\//g, '-')
|
||||
const result = new Date();
|
||||
|
||||
// YYYY,YYYY-MM,YYYY-MM-DD,YYYY-MM-DD HH,YYYY-MM-DD HH:mm,YYYY-MM-DD HH:mm:ss
|
||||
|
||||
let regxyy = /^(\d{4})$/
|
||||
let regxyymm = /^(\d{4})[-/](\d{1,2})$/
|
||||
let regxyymmdd = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})$/
|
||||
let regxyymmddhh = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2}) (\d{1,2})$/
|
||||
let regxyymmddhhmm = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2}) (\d{1,2}):(\d{1,2})$/
|
||||
let regxyymmddhhmmss = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/
|
||||
|
||||
let year = result.getFullYear();
|
||||
let month = result.getMonth() - 1
|
||||
let day = result.getDate()
|
||||
let hour = result.getHours()
|
||||
let minute = result.getMinutes()
|
||||
let second = result.getSeconds()
|
||||
|
||||
result.setSeconds(59);
|
||||
result.setMinutes(59);
|
||||
result.setHours(59);
|
||||
result.setDate(1);
|
||||
if (regxyymmddhhmmss.test(dateStr)) {
|
||||
const match = dateStr.match(regxyymmddhhmmss)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
month = (parseInt(match[2] as string) - 1);
|
||||
day = (parseInt(match[3] as string));
|
||||
hour = (parseInt(match[4] as string));
|
||||
minute = (parseInt(match[5] as string));
|
||||
second = (parseInt(match[6] as string));
|
||||
} else if (regxyymmddhhmm.test(dateStr)) {
|
||||
const match = dateStr.match(regxyymmddhhmm)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
month = (parseInt(match[2] as string) - 1);
|
||||
day = (parseInt(match[3] as string));
|
||||
hour = (parseInt(match[4] as string));
|
||||
minute = (parseInt(match[5] as string));
|
||||
|
||||
} else if (regxyymmddhh.test(dateStr)) {
|
||||
const match = dateStr.match(regxyymmddhh)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
month = (parseInt(match[2] as string) - 1);
|
||||
day = (parseInt(match[3] as string));
|
||||
hour = (parseInt(match[4] as string));
|
||||
} else if (regxyymmdd.test(dateStr)) {
|
||||
const match = dateStr.match(regxyymmdd)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
month = (parseInt(match[2] as string) - 1);
|
||||
day = (parseInt(match[3] as string));
|
||||
|
||||
} else if (regxyymm.test(dateStr)) {
|
||||
const match = dateStr.match(regxyymm)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
month = (parseInt(match[2] as string) - 1);
|
||||
} else if (regxyy.test(dateStr)) {
|
||||
const match = dateStr.match(regxyy)!;
|
||||
year = (parseInt(match[1] as string));
|
||||
}
|
||||
|
||||
result.setSeconds(second);
|
||||
result.setMinutes(minute);
|
||||
result.setHours(hour);
|
||||
result.setFullYear(year);
|
||||
result.setMonth(month);
|
||||
result.setDate(day);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期库 xDate
|
||||
*/
|
||||
export class xDate {
|
||||
date : Date;
|
||||
/**
|
||||
* dateStr,可能是数字,字符串,Date对象
|
||||
*/
|
||||
constructor(dateStr : string | number | Date | null = null) {
|
||||
this.date = this.checkDate(dateStr)
|
||||
}
|
||||
private checkDate(dateStr : string | number | Date | null = null) : Date {
|
||||
let tempDate = new Date();
|
||||
if (dateStr == null) {
|
||||
return tempDate;
|
||||
}
|
||||
if (typeof dateStr == 'number') {
|
||||
return new Date(dateStr! as number);
|
||||
} else if (typeof dateStr == 'string') {
|
||||
if (dateStr == '') {
|
||||
return tempDate;
|
||||
}
|
||||
const dateformatStr = this.detectDateFormat(dateStr!)
|
||||
|
||||
if (dateformatStr == 'CUSTOM') {
|
||||
let str = dateStr! as string;
|
||||
str = str.replace(/-/g, '/')
|
||||
let isNumberStr = /^\d+$/.test(str)
|
||||
if (!isNumberStr) {
|
||||
return createDate(dateStr!)
|
||||
} else {
|
||||
return new Date(parseInt(str!));
|
||||
}
|
||||
|
||||
} else {
|
||||
return new Date(dateStr! as string);
|
||||
}
|
||||
|
||||
} else if (dateStr instanceof Date) {
|
||||
return dateStr! as Date;
|
||||
}
|
||||
|
||||
return tempDate;
|
||||
}
|
||||
/**
|
||||
* 检测字符串的日期格式类型
|
||||
* @param dateStr - 日期字符串
|
||||
* @returns 日期格式类型
|
||||
*/
|
||||
detectDateFormat(dateStr : string) : DateFormat {
|
||||
// RFC2822 格式检测
|
||||
if (/^(?:\w{3},\s)?(?:\d{1,2}\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}(?::\d{2})?(?:\sGMT)?)|(?:\w{3}\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{1,2}\s\d{2}:\d{2}(?::\d{2})?\s\d{4})/.test(dateStr)) {
|
||||
return 'RFC2822';
|
||||
}
|
||||
|
||||
// ISO8601 格式检测
|
||||
if (/^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/.test(dateStr)) {
|
||||
return 'ISO8601';
|
||||
}
|
||||
|
||||
return 'CUSTOM';
|
||||
}
|
||||
/**
|
||||
* 给数字添加前缀0
|
||||
*/
|
||||
translateFullDate(d : number, w : number) : string {
|
||||
let dstr = d.toString();
|
||||
if (dstr.length < w) {
|
||||
let dar : number[] = new Array(w - dstr.length).fill(0);
|
||||
dstr = dar.join('') + dstr
|
||||
}
|
||||
return dstr;
|
||||
}
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param {string} s 模板,比如YYYY/MM/DD hh:mm:ss
|
||||
* @returns {string} 格式化后的日期字符串
|
||||
*/
|
||||
format(s : string | null = null) : string {
|
||||
let str = "YYYY/MM/DD hh:mm:ss"
|
||||
if (s != null) {
|
||||
str = s;
|
||||
}
|
||||
str = str.replace(/YYYY/g, this.translateFullDate(this.date.getFullYear(), 2))
|
||||
str = str.replace(/MM/g, this.translateFullDate(this.date.getMonth() + 1, 2))
|
||||
str = str.replace(/DD/g, this.translateFullDate(this.date.getDate(), 2))
|
||||
str = str.replace(/hh/g, this.translateFullDate(this.date.getHours(), 2))
|
||||
str = str.replace(/mm/g, this.translateFullDate(this.date.getMinutes(), 2))
|
||||
str = str.replace(/ss/g, this.translateFullDate(this.date.getSeconds(), 2))
|
||||
return str;
|
||||
}
|
||||
/**
|
||||
* 获取当前年
|
||||
* @return — 根据当地时间,返回一个对应于给定日期的年份数字。
|
||||
*/
|
||||
getYear() : number {
|
||||
return this.date.getFullYear()
|
||||
}
|
||||
/**
|
||||
* 获取当前月
|
||||
* @description 是从0开始,1-11月
|
||||
* @return — 一个 0 到 11 的整数值:0 代表一月份,1 代表二月份,2 代表三月份,依次类推
|
||||
*/
|
||||
getMonth() : number {
|
||||
return this.date.getMonth()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前月份的名称
|
||||
* @param {boolean} useShort - 是否使用短名称
|
||||
* @return — 返回当前语言环境下的月份名称
|
||||
*/
|
||||
getMonthName(useShort : boolean = false) : string {
|
||||
const monthIndex = this.date.getMonth();
|
||||
return useShort ?
|
||||
getLocaleData().monthsShort[monthIndex] :
|
||||
getLocaleData().months[monthIndex];
|
||||
}
|
||||
/**
|
||||
* 获取当前天
|
||||
* @return — 返回一个 1 到 31 的整数值。
|
||||
*/
|
||||
getDate() : number {
|
||||
return this.date.getDate()
|
||||
}
|
||||
/**
|
||||
* 获取当前小时
|
||||
* @return — 返回一个 0 到 23 之间的整数值。
|
||||
*/
|
||||
getHours() : number {
|
||||
return this.date.getHours()
|
||||
}
|
||||
/**
|
||||
* 获取当前分钟
|
||||
* @return — 返回一个 0 到 59 的整数值。
|
||||
*/
|
||||
getMinutes() : number {
|
||||
return this.date.getMinutes()
|
||||
}
|
||||
/**
|
||||
* 获取当前秒
|
||||
* @return — 返回一个 0 到 59 的整数值。
|
||||
*/
|
||||
getSeconds() : number {
|
||||
return this.date.getSeconds()
|
||||
}
|
||||
/**
|
||||
* 获取指定是数据
|
||||
* @param {xDateTypeTime} d - 'y' 年 'm' 月 'd' 日 'h' 时 'M' 分 's' 秒 'ms' 毫秒
|
||||
* @returns - 指定类型的数值
|
||||
*/
|
||||
getDateOf(d : xDateTypeTime = 'd') : number {
|
||||
if (d == 'y') {
|
||||
return this.date.getFullYear()
|
||||
}
|
||||
else if (d == 'm') {
|
||||
return this.date.getMonth()
|
||||
}
|
||||
else if (d == 'd') {
|
||||
return this.date.getDate()
|
||||
}
|
||||
else if (d == 'h') {
|
||||
return this.date.getHours()
|
||||
}
|
||||
else if (d == 'M') {
|
||||
return this.date.getMinutes()
|
||||
}
|
||||
else if (d == 's') {
|
||||
return this.date.getSeconds()
|
||||
}
|
||||
return this.date.getMilliseconds()
|
||||
}
|
||||
/**
|
||||
* 设定日期
|
||||
* @param {number} n - 设定的数据,
|
||||
* @param {string} d - 比如d=y,那么就是设置本日期的年份数据,'y' 年 'm' 月 'd' 日 'h' 时 'M' 分 's' 秒 'ms' 毫秒
|
||||
* @returns - xDate对象实例
|
||||
*/
|
||||
setDateOf(n : number, d : xDateTypeTime = 'd') : xDate {
|
||||
if (d == 'y') {
|
||||
this.date.setFullYear(n)
|
||||
}
|
||||
else if (d == 'm') {
|
||||
this.date.setMonth(n)
|
||||
}
|
||||
else if (d == 'd') {
|
||||
this.date.setDate(n)
|
||||
}
|
||||
else if (d == 'h') {
|
||||
this.date.setHours(n)
|
||||
}
|
||||
else if (d == 'M') {
|
||||
this.date.setMinutes(n)
|
||||
}
|
||||
else if (d == 's') {
|
||||
this.date.setSeconds(n)
|
||||
}
|
||||
else if (d == 'ms') {
|
||||
this.date.setMilliseconds(n)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} - d:'y' 年 'm' 月 'd' 日 'h' 时 'M' 分 's' 秒 'ms' 毫秒
|
||||
* @returns - 返回从UTC时间1970年1月1日午夜开始以毫秒为单位存储的时间值。
|
||||
* @description 注意,如果不提供d就默认返回毫秒,比如设置d=m那么后面的h,M,s,ms会被设置为0
|
||||
*/
|
||||
getTime(d : xDateTypeTime) : number {
|
||||
let date = new Date(this.date.getTime())
|
||||
|
||||
if (d == 'y') {
|
||||
date.setMonth(0)
|
||||
date.setDate(1)
|
||||
date.setHours(0)
|
||||
date.setMinutes(0)
|
||||
date.setSeconds(0)
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
else if (d == 'm') {
|
||||
date.setDate(1)
|
||||
date.setHours(0)
|
||||
date.setMinutes(0)
|
||||
date.setSeconds(0)
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
else if (d == 'd') {
|
||||
date.setHours(0)
|
||||
date.setMinutes(0)
|
||||
date.setSeconds(0)
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
else if (d == 'h') {
|
||||
date.setMinutes(0)
|
||||
date.setSeconds(0)
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
else if (d == 'M') {
|
||||
date.setSeconds(0)
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
else if (d == 's') {
|
||||
date.setMilliseconds(0)
|
||||
return date.getTime()
|
||||
}
|
||||
return date.getTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间的副本
|
||||
*/
|
||||
getClone() : xDate {
|
||||
|
||||
return new xDate(this.format());
|
||||
}
|
||||
/**
|
||||
* 本年的第几周
|
||||
* @return 周次
|
||||
*/
|
||||
getWeek() : number {
|
||||
let target = new Date(this.format())
|
||||
target.setDate(target.getDate() - (target.getDay() == 0 ? 7 : target.getDay()));
|
||||
let firstDayOfYear = new Date(target.getFullYear(), 0, 1);
|
||||
firstDayOfYear.setDate(firstDayOfYear.getDate() - (firstDayOfYear.getDay() == 0 ? 7 : firstDayOfYear.getDay()));
|
||||
return Math.ceil((((target.getTime() - firstDayOfYear.getTime()) / 86400000) + 1) / 7);
|
||||
}
|
||||
/**
|
||||
* 取时间的第一天
|
||||
* @param {m,w,y} - m表示取本月的第一天,w取本周的第一天,y表示本年的第一天
|
||||
* @returns {xDate} - 返回xDate对象
|
||||
*/
|
||||
getDateStartOf(d : 'm' | 'w' | 'y' = 'm') : xDate {
|
||||
// 获取当前日期对象
|
||||
let now = new Date(this.format())
|
||||
if (d == 'w') {
|
||||
// 将日期设置为所在周的周一(遵循ISO 8601标准,周一开始于周一)
|
||||
let dayOfWeek = now.getDay();
|
||||
if (dayOfWeek === 0) { // 如果是星期日,则向前推一天至上周的周一
|
||||
now.setDate(now.getDate() - 7);
|
||||
}
|
||||
now.setDate(now.getDate() - dayOfWeek + 1);
|
||||
}
|
||||
if (d == 'm' || d == 'y') {
|
||||
now.setDate(1);
|
||||
}
|
||||
if (d == 'y') {
|
||||
now.setMonth(0);
|
||||
}
|
||||
now.setHours(0);
|
||||
now.setMinutes(0);
|
||||
now.setSeconds(0);
|
||||
return dateCovertXdate(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取时间的最后一天
|
||||
* @param {string} - m,w,y m表示取本月的最后一天,w取本周的最后一天,y表示本年的最后一天
|
||||
* @returns {xDate} - 返回xDate对象
|
||||
*/
|
||||
getDateEndOf(d : 'm' | 'w' | 'y' = 'm') : xDate {
|
||||
// 获取当前日期对象
|
||||
let now = new Date(this.format())
|
||||
if (d == 'w') {
|
||||
// 将日期设置为所在周的周一(遵循ISO 8601标准,周一开始于周一)
|
||||
let dayOfWeek = now.getDay();
|
||||
if (dayOfWeek === 0) { // 如果是星期日,则向前推一天至上周的周一
|
||||
return dateCovertXdate(now);
|
||||
}
|
||||
now.setDate(now.getDate() + (7 - dayOfWeek));
|
||||
}
|
||||
if (d == 'm') {
|
||||
now.setDate(this.getMonthCountDay());
|
||||
}
|
||||
if (d == 'y') {
|
||||
// 先设置月份为12月,再设置为该月的最后一天
|
||||
now.setMonth(11);
|
||||
// 设置为12月的最后一天
|
||||
now.setDate(31);
|
||||
}
|
||||
now.setHours(23);
|
||||
now.setMinutes(59);
|
||||
now.setSeconds(59);
|
||||
return dateCovertXdate(now);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据本地时间,返回一个具体日期中一周的第几天,0 表示星期天。
|
||||
* @return — 根据本地时间,返回一个 0 到 6 之间的整数值,代表星期几:0 代表星期日,1 代表星期一,2 代表星期二,依次类推。
|
||||
*/
|
||||
getWeekDay() : number {
|
||||
return this.date.getDay()
|
||||
}
|
||||
/**
|
||||
* 根据本地时间,返回一个具体日期中一周的第几天,0 表示星期天。
|
||||
* @param {string[]} model 具有星期的中文模板,请按照顺序放置如["周日","周一","周二","周三","周四","周五","周六"]
|
||||
* @param {boolean} useShort 是否使用短名称
|
||||
* @return — 返回一个当前语言环境下的星期名称
|
||||
*/
|
||||
getWeekDayCn(model : string[] | null = null, useShort : boolean = true) : string {
|
||||
let ml = useShort ? getLocaleData().weekdaysShort : getLocaleData().weekdays;
|
||||
if (model != null && model?.length == 7) {
|
||||
ml = model!;
|
||||
}
|
||||
return ml[this.getWeekDay()]!
|
||||
}
|
||||
/**
|
||||
* 返回当前日期本月的最大天数
|
||||
* @returns - 当前月份的最大天数
|
||||
*/
|
||||
getMonthCountDay() : number {
|
||||
let nextDate = new Date(this.format())
|
||||
// 先设置为1号,避免后面设置月份时超过月的天数。
|
||||
nextDate.setDate(1)
|
||||
nextDate.setMonth(this.getMonth() + 1)
|
||||
// 设置为上月的最后一天。
|
||||
nextDate.setDate(0)
|
||||
return nextDate.getDate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当日的信息
|
||||
* @returns - 返回一个包含年月日周次,星期,农历等的对象
|
||||
*/
|
||||
getDateInfo(str : string | null = null) : xDateDayInfoType {
|
||||
let date = this as xDate
|
||||
if (str != null) {
|
||||
date = new xDate(str)
|
||||
}
|
||||
let info = {
|
||||
year: date.getYear(),
|
||||
month: date.getMonth(),
|
||||
day: date.getDate(),
|
||||
hours: date.getHours(),
|
||||
minutes: date.getMinutes(),
|
||||
seconds: date.getSeconds(),
|
||||
week: date.getWeekDay(),
|
||||
weeks: date.getWeek(),
|
||||
weekCn: date.getWeekDayCn(),
|
||||
date: ""
|
||||
} as xDateDayInfoType
|
||||
info.date = info.year + '/' + (info.month + 1) + '/' + info.day
|
||||
return info
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回日期数组
|
||||
* @param {string} - m,w,m表示返回本月的日期数据,w表示返回本周的日期数据
|
||||
* @returns - 返回一个日期数组
|
||||
*/
|
||||
getDaysOf(d : 'm' | 'w' = 'm') : xDateDayInfoType[] {
|
||||
let dates = [] as xDateDayInfoType[]
|
||||
if (d == 'w') {
|
||||
let first = this.getDateStartOf('w')
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
let date = first.getClone()
|
||||
date.date.setDate(date.date.getDate() + i)
|
||||
dates.push(date.getDateInfo())
|
||||
}
|
||||
}
|
||||
if (d == 'm') {
|
||||
let first = this.getDateStartOf('m')
|
||||
let maxDay = this.getMonthCountDay()
|
||||
for (let i = 1; i <= maxDay; i++) {
|
||||
let date = first.getClone()
|
||||
date.date.setDate(i)
|
||||
dates.push(date.getDateInfo())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
/**
|
||||
* 按指定天数返回日期数组
|
||||
* @param {string} d - 返回多少天的日期数组
|
||||
* @param {string} type - after表示返回当前日期之后的日期,before表示返回当前日期之前的日期
|
||||
*/
|
||||
getDaysOfNum(d : number = 0, type : 'after' | 'before') : xDateDayInfoType[] {
|
||||
let ar = [] as xDateDayInfoType[];
|
||||
let nowdate = this.getClone()
|
||||
if (type == 'after') {
|
||||
for (let i = 0; i < d; i++) {
|
||||
nowdate.add(1, 'd')
|
||||
ar.push(nowdate.getDateInfo())
|
||||
}
|
||||
} else if (type == 'before') {
|
||||
for (let i = 0; i < d; i++) {
|
||||
nowdate.subtraction(1, 'd')
|
||||
ar.push(nowdate.getDateInfo())
|
||||
}
|
||||
ar.reverse()
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为当前日期进行加操作
|
||||
* @param {string} - y,m,d,h,M,s,w,代表的是年份,月份,日期,小时,分钟,秒数,周
|
||||
*/
|
||||
add(count : number, d : xDateTypeTime = 'd') : xDate {
|
||||
if (d == 'y') {
|
||||
this.date.setFullYear(this.getYear() + count)
|
||||
}
|
||||
else if (d == 'm') {
|
||||
this.date.setMonth(this.getMonth() + count)
|
||||
}
|
||||
else if (d == 'd') {
|
||||
this.date.setDate(this.getDate() + count)
|
||||
}
|
||||
else if (d == 'h') {
|
||||
this.date.setHours(this.getHours() + count)
|
||||
}
|
||||
else if (d == 'M') {
|
||||
this.date.setMinutes(this.getMinutes() + count)
|
||||
}
|
||||
else if (d == 's') {
|
||||
this.date.setSeconds(this.getSeconds() + count)
|
||||
}
|
||||
// 添加一周
|
||||
else if (d == 'w') {
|
||||
this.date.setDate(this.getDate() + count * 7)
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 为当前日期进行减操作
|
||||
* @param {string} - y,m,d,h,M,s,w,代表的是年份,月份,日期,小时,分钟,秒数,周
|
||||
*/
|
||||
subtraction(count : number, d : xDateTypeTime = 'd') : xDate {
|
||||
if (d == 'y') {
|
||||
this.date.setFullYear(this.getYear() - count)
|
||||
}
|
||||
else if (d == 'm') {
|
||||
this.date.setMonth(this.getMonth() - count)
|
||||
}
|
||||
else if (d == 'd') {
|
||||
this.date.setDate(this.getDate() - count)
|
||||
}
|
||||
else if (d == 'h') {
|
||||
this.date.setHours(this.getHours() - count)
|
||||
}
|
||||
else if (d == 'M') {
|
||||
this.date.setMinutes(this.getMinutes() - count)
|
||||
}
|
||||
else if (d == 's') {
|
||||
this.date.setSeconds(this.getSeconds() - count)
|
||||
}
|
||||
// 添加一周
|
||||
else if (d == 'w') {
|
||||
this.date.setDate(this.getDate() - count * 7)
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 日期是否在一个区间内
|
||||
* @param {xDate} start 开始日期
|
||||
* @param {xDate} end 结束日期
|
||||
* @param {string} - type要比较的单位,默认为ms,'y' 年 'm' 月 'd' 日 'h' 时 'M' 分 's' 秒 'ms' 毫秒
|
||||
* @param {string} - d,默认为[] 区间类型,'()'不含起始'[]'包含起始'(]'不包含开始,但包含结束'[)'包含开始,但不包含结束
|
||||
* @returns {boolean} 是否在区间内
|
||||
*/
|
||||
isBetween(start : xDate, end : xDate, type : xDateTypeTime = 'ms', d : '()' | '[]' | '(]' | '[)' = '[]') : boolean {
|
||||
let startTime = start.getTime(type)
|
||||
|
||||
// new Date('1970-1-1 8:0:0').getTime()
|
||||
let endTime = end.getTime(type)
|
||||
let nowTime = this.getTime(type)
|
||||
if (d == '()') {
|
||||
return nowTime > startTime && nowTime < endTime
|
||||
}
|
||||
else if (d == '[]') {
|
||||
return nowTime >= startTime && nowTime <= endTime
|
||||
}
|
||||
else if (d == '(]') {
|
||||
return nowTime > startTime && nowTime <= endTime
|
||||
}
|
||||
else if (d == '[)') {
|
||||
return nowTime >= startTime && nowTime < endTime
|
||||
}
|
||||
return nowTime >= startTime && nowTime <= endTime
|
||||
}
|
||||
|
||||
/**
|
||||
* 与目标日期比较
|
||||
* @param {xDate} targetDate 要比较的日期
|
||||
* @param {string} - d,默认为> 比较类型,'>'大于目标日期,'>='大于等于目标'<'小于目标,'<='小于等于目标,'='全等
|
||||
* @param {string} - type要比较的单位,默认为ms,'y' 年 'm' 月 'd' 日 'h' 时 'M' 分 's' 秒 'ms' 毫秒
|
||||
* @returns {boolean} 比较结果
|
||||
*/
|
||||
isBetweenOf(targetDate : xDate, d : '>' | '>=' | '<' | '<=' | '=' = '>', type : xDateTypeTime = 'ms') : boolean {
|
||||
let startTime = targetDate.getTime(type)
|
||||
let nowTime = this.getTime(type)
|
||||
if (d == '>') {
|
||||
return nowTime > startTime
|
||||
}
|
||||
else if (d == '>=') {
|
||||
return nowTime >= startTime
|
||||
}
|
||||
else if (d == '<') {
|
||||
return nowTime < startTime
|
||||
}
|
||||
else if (d == '<=') {
|
||||
return nowTime <= startTime
|
||||
}
|
||||
else if (d == '=') {
|
||||
return nowTime == startTime
|
||||
}
|
||||
return nowTime > startTime
|
||||
}
|
||||
/**
|
||||
* 与目标时间过了多久
|
||||
* @param {string|xDate|null} target 要对比的相对时间,如果填写null就表示当前时间以来的的多久
|
||||
* @param {Map<number,string>} model 如果为null或者为空的map采用默认值,map格式key表示以秒为单位的时间,value为对应的文本
|
||||
* 比如60,1分钟前
|
||||
* @param {string} format 超过最大值最使用格式化日期,如果为null默认为YYYY-MM-DD
|
||||
* @returns {string} 返回对应的时间文本
|
||||
*/
|
||||
fromBetweenLongTime(target : any, model : Map<number, string> | null, format : string | null) : string {
|
||||
let bijiaodate : xDate = new xDate()
|
||||
if (target instanceof xDate) {
|
||||
bijiaodate = (target as xDate)
|
||||
} else if (typeof target == 'string') {
|
||||
bijiaodate = new xDate(target as string)
|
||||
} else if (target == null) {
|
||||
bijiaodate = new xDate()
|
||||
}
|
||||
|
||||
// 使用国际化相对时间配置
|
||||
let relativeTime = getLocaleData().relativeTime;
|
||||
let mapmodel = new Map<number, string>([
|
||||
[30, relativeTime.s],
|
||||
[60, relativeTime.m],
|
||||
[60 * 5, '5' + relativeTime.mm.replace('%d', '')],
|
||||
[60 * 10, '10' + relativeTime.mm.replace('%d', '')],
|
||||
[60 * 30, '30' + relativeTime.mm.replace('%d', '')],
|
||||
[60 * 60, relativeTime.h],
|
||||
[60 * 60 * 2, '2' + relativeTime.hh.replace('%d', '')],
|
||||
[60 * 60 * 3, '3' + relativeTime.hh.replace('%d', '')],
|
||||
[60 * 60 * 5, '5' + relativeTime.hh.replace('%d', '')],
|
||||
[60 * 60 * 23, '23' + relativeTime.hh.replace('%d', '')],
|
||||
[60 * 60 * 24 * 1, relativeTime.d],
|
||||
[60 * 60 * 24 * 2, '2' + relativeTime.dd.replace('%d', '')],
|
||||
[60 * 60 * 24 * 7, '7' + relativeTime.dd.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30, relativeTime.M],
|
||||
[60 * 60 * 24 * 30 * 2, '2' + relativeTime.MM.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30 * 3, '3' + relativeTime.MM.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30 * 4, '4' + relativeTime.MM.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30 * 5, '5' + relativeTime.MM.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30 * 6, '6' + relativeTime.MM.replace('%d', '')],
|
||||
[60 * 60 * 24 * 30 * 12, relativeTime.y],
|
||||
[60 * 60 * 24 * 30 * 12 + 60 * 60 * 24 * 30, '']
|
||||
])
|
||||
let fmt = format == null ? 'YYYY-MM-DD' : (format!)
|
||||
|
||||
if (model != null) {
|
||||
if (model!.size > 0) {
|
||||
mapmodel = model!
|
||||
}
|
||||
}
|
||||
let str = ""
|
||||
let fanzhumap = [] as number[];
|
||||
mapmodel.forEach((value : string, key : number) => {
|
||||
fanzhumap.push(key)
|
||||
})
|
||||
fanzhumap.reverse()
|
||||
|
||||
try {
|
||||
let startTime = bijiaodate.getTime('s')
|
||||
let nowTime = this.getTime('s')
|
||||
let diff = (nowTime - startTime) / 1000
|
||||
|
||||
|
||||
if (diff > 0) {
|
||||
for (let i = 0; i < fanzhumap.length; i++) {
|
||||
let key = fanzhumap[i];
|
||||
|
||||
if (diff >= key) {
|
||||
console.log(diff, key, 5 * 60 * 60, '---', fanzhumap)
|
||||
str = mapmodel.get(key)!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
str = mapmodel.get(fanzhumap[fanzhumap.length - 1])!
|
||||
}
|
||||
if (str == '') {
|
||||
str = bijiaodate.format(fmt)
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
}
|
||||
|
||||
|
||||
|
||||
return str
|
||||
}
|
||||
/**
|
||||
* 与目标时间相差多少个单位
|
||||
* @param {string|xDate|null} target 目标时间
|
||||
* @param {xDateTypeTime} type 相差的单位,默认:s秒
|
||||
* @returns {string} 反返回以xDateTypeTime为单位相关的时间数
|
||||
*/
|
||||
diffTime(target : any, type : xDateTypeTime = 's') : number {
|
||||
let startTime = 0
|
||||
let typed : xDateTypeTime = (type == 'w' ? 'd' : type)
|
||||
if (target instanceof xDate) {
|
||||
startTime = (target as xDate).getTime(typed)
|
||||
} else if (typeof target == 'string') {
|
||||
startTime = new xDate(target as string).getTime(typed)
|
||||
|
||||
} else if (target == null) {
|
||||
startTime = new xDate().getTime(typed)
|
||||
}
|
||||
|
||||
let nowTime = this.getTime(typed)
|
||||
let diff = Math.abs(nowTime - startTime);
|
||||
let d = 0
|
||||
if (type == 's') {
|
||||
d = diff
|
||||
} else if (type == 'M') {
|
||||
d = diff / 60 / 1000
|
||||
} else if (type == 'h') {
|
||||
d = diff / 60 / 60 / 1000
|
||||
} else if (type == 'd') {
|
||||
d = diff / 60 / 60 / 24 / 1000
|
||||
} else if (type == 'm') {
|
||||
d = diff / 60 / 60 / 24 / 30 / 1000
|
||||
} else if (type == 'y') {
|
||||
d = diff / 60 / 60 / 24 / 30 / 12 / 1000
|
||||
} else if (type == 'w') {
|
||||
let ondate = (1000 * 60 * 60 * 24 * 7);
|
||||
d = diff / ondate
|
||||
}
|
||||
|
||||
return Math.floor(d)
|
||||
}
|
||||
/**
|
||||
* 获取季度
|
||||
* @param {string} type y表示获取本年的4个季度,空值表示获取当前时间所在的季度
|
||||
* @returns {xDateTypeQuarter[]} 返回一个季度数组{quarter:number,start:string,end:string}
|
||||
*/
|
||||
getQuarter(type : string = '') : xDateTypeQuarter[] {
|
||||
let q1 = [1, 2, 3]
|
||||
let q2 = [4, 5, 6]
|
||||
let q3 = [7, 8, 9]
|
||||
let q4 = [10, 11, 12]
|
||||
let nowMonth = this.getMonth() + 1
|
||||
let qall = [q1, q2, q3, q4] as number[][]
|
||||
let qmap = [] as xDateTypeQuarter[]
|
||||
if (type == 'y') {
|
||||
for (let i = 0; i < qall.length; i++) {
|
||||
let item = qall[i]
|
||||
let sdate = new xDate(this.getYear() + '/' + item[0] + '/1')
|
||||
let edate = new xDate(this.getYear() + '/' + item[item.length - 1] + '/1')
|
||||
let start = sdate.format('YYYY/MM/DD')
|
||||
let end = edate.getDateEndOf('m').format('YYYY/MM/DD')
|
||||
qmap.push({
|
||||
quarter: i,
|
||||
start,
|
||||
end
|
||||
} as xDateTypeQuarter)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < qall.length; i++) {
|
||||
let item = qall[i]
|
||||
if (item.includes(nowMonth)) {
|
||||
let sdate = new xDate(this.getYear() + '/' + item[0] + '/1')
|
||||
let edate = new xDate(this.getYear() + '/' + item[item.length - 1] + '/1')
|
||||
let start = sdate.format('YYYY/MM/DD')
|
||||
let end = edate.getDateEndOf('m').format('YYYY/MM/DD')
|
||||
qmap.push({
|
||||
quarter: i,
|
||||
start,
|
||||
end
|
||||
} as xDateTypeQuarter)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return qmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置xDate的语言
|
||||
* @param {xDateLanguage} locale - 语言代码
|
||||
*/
|
||||
setDateLocale(locale : xDateLanguage) : void {
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前xDate使用的语言
|
||||
* @returns {xDateLanguage} - 当前语言代码
|
||||
*/
|
||||
getDateLocale() : xDateLanguage {
|
||||
return getCurrentLocale();
|
||||
}
|
||||
/**
|
||||
* 根据开始和结束时间取之间的时间(含起始)
|
||||
* @param {string|xDate|null} start 目标时间
|
||||
* @param {string|xDate|null} end 目标时间
|
||||
*/
|
||||
getBetweenDate(start : string | number | Date, end : string | number | Date, minx : 'min' | 'max' | 'auto' = 'auto') : Date {
|
||||
let _start = this.checkDate(start);
|
||||
let _end = this.checkDate(end);
|
||||
let startDiff = _start.getTime()
|
||||
let endDiff = _end.getTime()
|
||||
let nowDiff = this.getTime('ms')
|
||||
if(nowDiff>=startDiff&&nowDiff<=endDiff){
|
||||
return this.date;
|
||||
}
|
||||
if (minx == 'min') {
|
||||
return _start;
|
||||
}
|
||||
if (minx == 'max') {
|
||||
return _end;
|
||||
}
|
||||
if (startDiff < endDiff) {
|
||||
return _end;
|
||||
}
|
||||
return _start;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将一个日期转换为xDate对象
|
||||
*/
|
||||
export const dateCovertXdate = function (date : Date) : xDate {
|
||||
return new xDate(date)
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* xDate国际化支持
|
||||
* 支持中文、英文、日文、韩文、繁体中文、法语和俄语
|
||||
*/
|
||||
|
||||
export type xDateLanguage = 'zh-Hans' | 'en' | 'ja' | 'ko' | 'zh-Hant' | 'fr' | 'ru';
|
||||
export type xDateI18nTypeRelativeTime = {
|
||||
future: string,
|
||||
past: string,
|
||||
s: string,
|
||||
m: string,
|
||||
mm: string,
|
||||
h: string,
|
||||
hh: string,
|
||||
d: string,
|
||||
dd: string,
|
||||
M: string,
|
||||
MM: string,
|
||||
y: string,
|
||||
yy: string
|
||||
}
|
||||
export type xDateI18nType = {
|
||||
weekdays: string[],
|
||||
weekdaysShort: string[],
|
||||
months: string[],
|
||||
monthsShort: string[],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean) => string,
|
||||
relativeTime: xDateI18nTypeRelativeTime
|
||||
}
|
||||
|
||||
// 中文简体
|
||||
const zhHans: xDateI18nType = {
|
||||
weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||
weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return hour < 12 ? '上午' : '下午';
|
||||
},
|
||||
relativeTime: {
|
||||
future: '%s内',
|
||||
past: '%s前',
|
||||
s: '几秒',
|
||||
m: '1分钟',
|
||||
mm: '%d分钟',
|
||||
h: '1小时',
|
||||
hh: '%d小时',
|
||||
d: '1天',
|
||||
dd: '%d天',
|
||||
M: '1个月',
|
||||
MM: '%d个月',
|
||||
y: '1年',
|
||||
yy: '%d年'
|
||||
}
|
||||
};
|
||||
|
||||
// 英文
|
||||
const en: xDateI18nType = {
|
||||
weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||
weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return hour < 12 ? (isLowercase ? 'am' : 'AM') : (isLowercase ? 'pm' : 'PM');
|
||||
},
|
||||
relativeTime: {
|
||||
future: 'in %s',
|
||||
past: '%s ago',
|
||||
s: 'a few seconds',
|
||||
m: 'a minute',
|
||||
mm: '%d minutes',
|
||||
h: 'an hour',
|
||||
hh: '%d hours',
|
||||
d: 'a day',
|
||||
dd: '%d days',
|
||||
M: 'a month',
|
||||
MM: '%d months',
|
||||
y: 'a year',
|
||||
yy: '%d years'
|
||||
}
|
||||
};
|
||||
|
||||
// 日文
|
||||
const ja: xDateI18nType = {
|
||||
weekdays: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
|
||||
weekdaysShort: ['日', '月', '火', '水', '木', '金', '土'],
|
||||
months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return hour < 12 ? '午前' : '午後';
|
||||
},
|
||||
relativeTime: {
|
||||
future: '%s後',
|
||||
past: '%s前',
|
||||
s: '数秒',
|
||||
m: '1分',
|
||||
mm: '%d分',
|
||||
h: '1時間',
|
||||
hh: '%d時間',
|
||||
d: '1日',
|
||||
dd: '%d日',
|
||||
M: '1ヶ月',
|
||||
MM: '%dヶ月',
|
||||
y: '1年',
|
||||
yy: '%d年'
|
||||
}
|
||||
};
|
||||
|
||||
// 韩文
|
||||
const ko: xDateI18nType = {
|
||||
weekdays: ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
|
||||
weekdaysShort: ['일', '월', '화', '수', '목', '금', '토'],
|
||||
months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
monthsShort: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return hour < 12 ? '오전' : '오후';
|
||||
},
|
||||
relativeTime: {
|
||||
future: '%s 후',
|
||||
past: '%s 전',
|
||||
s: '몇 초',
|
||||
m: '1분',
|
||||
mm: '%d분',
|
||||
h: '1시간',
|
||||
hh: '%d시간',
|
||||
d: '1일',
|
||||
dd: '%d일',
|
||||
M: '1개월',
|
||||
MM: '%d개월',
|
||||
y: '1년',
|
||||
yy: '%d년'
|
||||
}
|
||||
};
|
||||
|
||||
// 繁体中文
|
||||
const zhHant: xDateI18nType = {
|
||||
weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||
weekdaysShort: ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return hour < 12 ? '上午' : '下午';
|
||||
},
|
||||
relativeTime: {
|
||||
future: '%s內',
|
||||
past: '%s前',
|
||||
s: '幾秒',
|
||||
m: '1分鐘',
|
||||
mm: '%d分鐘',
|
||||
h: '1小時',
|
||||
hh: '%d小時',
|
||||
d: '1天',
|
||||
dd: '%d天',
|
||||
M: '1個月',
|
||||
MM: '%d個月',
|
||||
y: '1年',
|
||||
yy: '%d年'
|
||||
}
|
||||
};
|
||||
|
||||
// 法语
|
||||
const fr: xDateI18nType = {
|
||||
weekdays: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
weekdaysShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
monthsShort: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return '';
|
||||
},
|
||||
relativeTime: {
|
||||
future: 'dans %s',
|
||||
past: 'il y a %s',
|
||||
s: 'quelques secondes',
|
||||
m: 'une minute',
|
||||
mm: '%d minutes',
|
||||
h: 'une heure',
|
||||
hh: '%d heures',
|
||||
d: 'un jour',
|
||||
dd: '%d jours',
|
||||
M: 'un mois',
|
||||
MM: '%d mois',
|
||||
y: 'un an',
|
||||
yy: '%d ans'
|
||||
}
|
||||
};
|
||||
|
||||
// 俄语
|
||||
const ru: xDateI18nType = {
|
||||
weekdays: ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'],
|
||||
weekdaysShort: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
|
||||
months: ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
|
||||
monthsShort: ['янв.', 'февр.', 'март', 'апр.', 'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.', 'нояб.', 'дек.'],
|
||||
meridiem: (hour: number, minute: number, isLowercase: boolean): string => {
|
||||
return '';
|
||||
},
|
||||
relativeTime: {
|
||||
future: 'через %s',
|
||||
past: '%s назад',
|
||||
s: 'несколько секунд',
|
||||
m: 'минута',
|
||||
mm: '%d минут',
|
||||
h: 'час',
|
||||
hh: '%d часов',
|
||||
d: 'день',
|
||||
dd: '%d дней',
|
||||
M: 'месяц',
|
||||
MM: '%d месяцев',
|
||||
y: 'год',
|
||||
yy: '%d лет'
|
||||
}
|
||||
};
|
||||
|
||||
// 语言映射表
|
||||
export const locales: Map<xDateLanguage, xDateI18nType> = new Map([
|
||||
['zh-Hans', zhHans],
|
||||
['en', en],
|
||||
['ja', ja],
|
||||
['ko', ko],
|
||||
['zh-Hant', zhHant],
|
||||
['fr', fr],
|
||||
['ru', ru]
|
||||
]);
|
||||
|
||||
// 默认语言
|
||||
let currentLocale: xDateLanguage = 'zh-Hans';
|
||||
|
||||
/**
|
||||
* 获取当前语言设置
|
||||
*/
|
||||
export function getCurrentLocale(): xDateLanguage {
|
||||
return currentLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前语言
|
||||
* @param locale 语言代码
|
||||
*/
|
||||
export function setLocale(locale: xDateLanguage): void {
|
||||
|
||||
if (locales.has(locale)) {
|
||||
currentLocale = locale;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言的本地化数据
|
||||
*/
|
||||
export function getLocaleData(): xDateI18nType {
|
||||
return locales.get(currentLocale)!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定语言的本地化数据
|
||||
* @param locale 语言代码
|
||||
*/
|
||||
export function getLocaleDataByCode(locale: xDateLanguage): xDateI18nType | null {
|
||||
return locales.has(locale) ? locales.get(locale)! : null;
|
||||
}
|
||||
@@ -0,0 +1,676 @@
|
||||
import { xRequestCall } from "./config.uts"
|
||||
import { xRequestOptions, xRequestOptionsCallBack, xRequestResult, xRequestHistoryType, xRequestMethond } from "../../interface.uts"
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
const i18n = xConfig.i18n;
|
||||
/**
|
||||
* 请求库xRequest
|
||||
* @version 1.0.0
|
||||
* @author tmzdy
|
||||
* @host https://xui.tmui.design
|
||||
* @description 异常简便,没有繁杂的配置选项,只有快速的开发部署。
|
||||
*/
|
||||
/**
|
||||
* auth 表示全局设置了全限auth为false,后面的所有请求都被中止请求,从而触发此事件。
|
||||
* 如果此时返回true,将继续请求。
|
||||
* before 如果返回true,将继续请求。返回false被中断请求。
|
||||
* after 可以返回请求的后台数据格式化并返回到最终的请求结果中。
|
||||
*
|
||||
* auth,before监听函数请务必返回Promise.resolve(boolean),其中函数内得到的arg为xRequestOptionsCallBack类型(请求参数配置数据)
|
||||
* 除了auth,before 函数体内arg 得到的参数全是xRequestResult(请求结果数据), 返回随意,因为内部不采用。
|
||||
* after,需要返回 Promise.resolve(xRequestResult),会覆盖success请求后的数据,相当于你在此hook事件中修改和格式化服务器,并返回到最终结果中。
|
||||
*/
|
||||
type xRequestEventType = "before" | "after" | "abort" | "timeout" | "error" | "auth" | "success" | "complete"
|
||||
type funType = (arg : any) => Promise<any>;
|
||||
type funCall = {
|
||||
fun : funType,
|
||||
type : xRequestEventType
|
||||
}
|
||||
|
||||
function getUid(rdix = 1, length = 12) : string {
|
||||
let ix = "";
|
||||
// #ifndef APP
|
||||
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length);
|
||||
// #endif
|
||||
|
||||
// #ifdef APP
|
||||
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length as Int);
|
||||
// #endif
|
||||
return ix;
|
||||
}
|
||||
|
||||
// 缓存管理器
|
||||
class xCacheManager {
|
||||
private cache : UTSJSONObject = {};
|
||||
|
||||
constructor() {
|
||||
let cs = uni.getStorageSync('xCacheManager');
|
||||
if (cs instanceof UTSJSONObject) {
|
||||
this.cache = cs! as UTSJSONObject
|
||||
}
|
||||
}
|
||||
|
||||
// 生成缓存键
|
||||
generateCacheKey(config : xRequestOptionsCallBack) : string {
|
||||
const url = config.url;
|
||||
const method = config.method;
|
||||
let keyQ = (url?.split('?')?.[0] ?? url) as string;
|
||||
// 简化缓存键生成,避免复杂对象序列化可能导致的不一致
|
||||
let key = `${method}_${keyQ}`;
|
||||
// 对data进行类似处理
|
||||
if (config.data != null) {
|
||||
// 如果是简单类型,直接使用
|
||||
if (!(config.data instanceof UTSJSONObject)) {
|
||||
key += `_d:${config.data}`;
|
||||
} else if (config.data instanceof UTSJSONObject) {
|
||||
let _data = config.data as UTSJSONObject;
|
||||
let _keys = [] as string[]
|
||||
for (const key in _data) {
|
||||
_keys.push(key)
|
||||
}
|
||||
// 对于对象,只使用键名作为缓存键的一部分
|
||||
const dataKeys = _keys.sort()
|
||||
key += `_d:${dataKeys.join(',')}`;
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// 设置缓存
|
||||
set(config : xRequestOptionsCallBack, response : any) : void {
|
||||
const key = this.generateCacheKey(config);
|
||||
this.cache.set(key, {
|
||||
data: response,
|
||||
timestamp: Date.now()
|
||||
} as UTSJSONObject)
|
||||
|
||||
try {
|
||||
uni.setStorageSync('xCacheManager', this.cache);
|
||||
} catch (error) {
|
||||
// 如果存储失败,可能是缓存太大,清理一部分
|
||||
this.pruneCache();
|
||||
try {
|
||||
uni.setStorageSync('xCacheManager', this.cache);
|
||||
} catch (e) {
|
||||
console.error('二次缓存设置失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取缓存
|
||||
get(config : xRequestOptionsCallBack) : any | null {
|
||||
const key = this.generateCacheKey(config);
|
||||
const cacheItem = this.cache.getJSON(key)
|
||||
|
||||
if (cacheItem == null) return null;
|
||||
|
||||
const cacheTime = config.cacheTime == 0 ? 60000 : config.cacheTime
|
||||
const now = Date.now();
|
||||
const timestamp = cacheItem.getNumber("timestamp")!
|
||||
// 检查缓存是否过期
|
||||
if (now - timestamp > cacheTime) {
|
||||
let allkeys = {} as UTSJSONObject;
|
||||
for (const keySelf in this.cache) {
|
||||
if (keySelf != key) {
|
||||
allkeys.set(keySelf, this.cache[keySelf])
|
||||
}
|
||||
}
|
||||
this.cache = allkeys
|
||||
uni.setStorageSync('xCacheManager', allkeys);
|
||||
return null;
|
||||
}
|
||||
|
||||
// console.log('命中缓存:', key);
|
||||
return cacheItem.getAny('data');
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
clear() : void {
|
||||
this.cache = {};
|
||||
uni.removeStorageSync('xCacheManager');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 清理部分缓存
|
||||
private pruneCache() : void {
|
||||
|
||||
const _keys = [] as string[]
|
||||
for (const key in this.cache) {
|
||||
_keys.push(key)
|
||||
}
|
||||
|
||||
if (_keys.length > 20) { // 如果缓存项超过20个
|
||||
// 按时间戳排序,删除最旧的一半
|
||||
const sortedKeys = _keys.sort((a : string, b : string) : number => {
|
||||
let ad = this.cache[a]! as UTSJSONObject
|
||||
let bd = this.cache[b]! as UTSJSONObject
|
||||
return ad.getNumber('timestamp')! - bd.getNumber('timestamp')!
|
||||
});
|
||||
|
||||
const toRemove = sortedKeys.slice(0, Math.floor(_keys.length / 2));
|
||||
let allkeys = {} as UTSJSONObject;
|
||||
for (let i = 0; i < sortedKeys.length; i++) {
|
||||
let key = sortedKeys[i]
|
||||
allkeys.set(key, this.cache[key])
|
||||
}
|
||||
this.cache = allkeys
|
||||
uni.setStorageSync('xCacheManager', allkeys);
|
||||
console.log(`已清理${toRemove.length}个旧缓存项`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class xRequest {
|
||||
private auth = true;
|
||||
private cacheManager : xCacheManager;
|
||||
private selfOpts = {
|
||||
useCache: false,
|
||||
cacheTime: 60 * 1000,
|
||||
hostUrl: xRequestCall.hostUrl,
|
||||
successStatusCode: 200,
|
||||
url: "",
|
||||
data: {} as UTSJSONObject,
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
} as UTSJSONObject,
|
||||
method: "GET",
|
||||
timeout: 6000,
|
||||
firstIpv4: false,
|
||||
/** 请求前是否显示loading遮罩 */
|
||||
showLoadToast: xRequestCall.showLoadToast,
|
||||
/** 请求成功后是否提示,否则不提示 */
|
||||
showSuccessToast: xRequestCall.showSuccessToast,
|
||||
/** 出错时,是否显示提示,否则不提示 */
|
||||
showErrorToast: xRequestCall.showErrorToast,
|
||||
loadToastText: i18n.t("tmui4x.xRequest.loading"),
|
||||
successToastText: i18n.t("tmui4x.xRequest.success"),
|
||||
errorToastText: ""
|
||||
} as xRequestOptionsCallBack
|
||||
|
||||
private _lisentEventList = [] as funCall[]
|
||||
|
||||
private status = null as xRequestEventType | null
|
||||
|
||||
|
||||
private result = {
|
||||
data: null,
|
||||
statusCode: 0,
|
||||
header: {} as any,
|
||||
cookies: [] as string[],
|
||||
} as xRequestResult
|
||||
|
||||
private reqtask = null as null | RequestTask
|
||||
constructor(opts : xRequestOptions | null = null) {
|
||||
this.setOptions(opts)
|
||||
this.cacheManager = new xCacheManager()
|
||||
}
|
||||
/**
|
||||
* 设置当前请求的参数
|
||||
* 只影响本次的实例请求,不影响其它请求。
|
||||
* 但配置中的header,data会与全局的合并。
|
||||
*/
|
||||
setOptions(opts : xRequestOptions | null = null) : xRequestOptionsCallBack {
|
||||
|
||||
if (opts == null) return this.selfOpts;
|
||||
let headerTmep = opts.header
|
||||
headerTmep = opts.header == null ? this.selfOpts.header : opts.header!
|
||||
// #ifdef WEB
|
||||
headerTmep = opts.header == undefined ? this.selfOpts.header : opts.header
|
||||
// #endif
|
||||
|
||||
|
||||
let dataTmep = opts?.data ?? ({} as UTSJSONObject)
|
||||
|
||||
if (xRequestCall.header != null) {
|
||||
let obj = xRequestCall.header as UTSJSONObject;
|
||||
let newObj = headerTmep! as UTSJSONObject;
|
||||
for (let key in newObj) {
|
||||
obj.set(key, newObj.get(key))
|
||||
}
|
||||
headerTmep = obj;
|
||||
}
|
||||
let firstIpv4 = (opts?.firstIpv4 ?? false) as boolean;
|
||||
let method = (opts?.method ?? this.selfOpts.method) as xRequestMethond
|
||||
let timeout = (opts?.timeout ?? this.selfOpts.timeout) as number
|
||||
let successStatusCode = (opts?.successStatusCode ?? this.selfOpts.successStatusCode) as number
|
||||
let url = (opts?.url ?? this.selfOpts.url) as string
|
||||
let showLoadToast = (opts?.showLoadToast ?? this.selfOpts.showSuccessToast) as boolean
|
||||
let showSuccessToast = (opts?.showSuccessToast ?? this.selfOpts.showSuccessToast) as boolean
|
||||
let showErrorToast = (opts?.showErrorToast ?? this.selfOpts.showErrorToast) as boolean
|
||||
let hostUrl = (opts?.hostUrl ?? xRequestCall.hostUrl) as string
|
||||
let cacheTime = (opts?.cacheTime ?? this.selfOpts.cacheTime) as number
|
||||
let useCache = (opts?.useCache ?? this.selfOpts.useCache) as boolean
|
||||
let loadToastText = (opts?.loadToastText ?? this.selfOpts!.loadToastText) as string
|
||||
let successToastText = (opts?.successToastText ?? this.selfOpts!.successToastText) as string
|
||||
let errorToastText = (opts?.errorToastText ?? this.selfOpts!.errorToastText) as string
|
||||
|
||||
this.selfOpts.firstIpv4 = firstIpv4!
|
||||
this.selfOpts.method = method! as xRequestMethond;
|
||||
this.selfOpts.timeout = timeout!
|
||||
this.selfOpts.cacheTime = cacheTime!
|
||||
this.selfOpts.useCache = useCache!
|
||||
this.selfOpts.successStatusCode = successStatusCode!
|
||||
this.selfOpts.data = dataTmep!
|
||||
this.selfOpts.header = headerTmep!
|
||||
this.selfOpts.url = url! as string
|
||||
this.selfOpts.showLoadToast = showLoadToast! as boolean;
|
||||
this.selfOpts.showSuccessToast = showSuccessToast! as boolean
|
||||
this.selfOpts.showErrorToast = showErrorToast! as boolean
|
||||
this.selfOpts.responseType = opts.responseType
|
||||
this.selfOpts.dataType = opts.dataType
|
||||
this.selfOpts.hostUrl = hostUrl
|
||||
this.selfOpts!.loadToastText = loadToastText
|
||||
this.selfOpts!.successToastText = successToastText
|
||||
this.selfOpts!.errorToastText = errorToastText
|
||||
|
||||
return this.selfOpts;
|
||||
}
|
||||
/**
|
||||
* 添加事件监听
|
||||
* 可以重复添加,被添加的函数会被按顺序执行。
|
||||
* 相当于Hooks
|
||||
*/
|
||||
addEventListener(event : xRequestEventType = "success", fun : (arg : any) => Promise<any>) : xRequest {
|
||||
this._lisentEventList.push({
|
||||
type: event,
|
||||
fun
|
||||
} as funCall)
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 中断当前的事件
|
||||
* @returns {boolean} true表示中断成功,false表示失败
|
||||
*/
|
||||
abort() : boolean {
|
||||
if (this.reqtask != null) {
|
||||
this.reqtask!.abort()
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* 设置请求授权失败还是通过
|
||||
* 如果设置了false
|
||||
* 接下来的所有请求全部会被中断掉。哪怕你异步或者跨页面发起请求。
|
||||
*/
|
||||
static setAuth(isPass : boolean) {
|
||||
xRequestCall.authPass = isPass
|
||||
}
|
||||
/**
|
||||
* 设置的头数据是会被所有请求共享
|
||||
* 包括跨页面。
|
||||
* 同时新增的数据会与旧的数据合并,如果之前不存在则新增。
|
||||
*/
|
||||
static setHeader(header : UTSJSONObject | null = null) : UTSJSONObject | null {
|
||||
if (xRequestCall.header == null) {
|
||||
xRequestCall.header = header;
|
||||
} else {
|
||||
let obj = xRequestCall.header as UTSJSONObject;
|
||||
let newObj = header! as UTSJSONObject;
|
||||
for (let key in newObj) {
|
||||
obj.set(key, newObj.get(key))
|
||||
}
|
||||
xRequestCall.header = obj;
|
||||
}
|
||||
|
||||
return xRequestCall.header
|
||||
}
|
||||
/**
|
||||
* 设置请求的主域名地址
|
||||
* 所有的请求共享,包括跨页面。
|
||||
*/
|
||||
static setHostUrl(url : string | null) {
|
||||
xRequestCall.hostUrl = url == null ? "" : url!;
|
||||
}
|
||||
static setDev(dev : boolean = false) {
|
||||
xRequestCall.dev = dev;
|
||||
if (dev) {
|
||||
console.warn('tmui4.0提醒:' + `你开启了请求调试模式`)
|
||||
} else {
|
||||
console.warn('tmui4.0提醒:' + `你关闭了请求调试模式`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取整个应用运行期间的所有请求日志列表
|
||||
*/
|
||||
static getHistory() : xRequestHistoryType[] {
|
||||
return xRequestCall.history.slice(0)
|
||||
}
|
||||
static setShowToast(showLoadToast : boolean = true, showSuccessToast : boolean = true, showErrorToast : boolean = true) {
|
||||
xRequestCall.showLoadToast = showLoadToast
|
||||
xRequestCall.showSuccessToast = showSuccessToast
|
||||
xRequestCall.showErrorToast = showErrorToast
|
||||
}
|
||||
/**
|
||||
* 执行Hooks函数。
|
||||
*/
|
||||
private _callFun_build() : Promise<any[]> {
|
||||
if (this.status == null) return Promise.resolve([true] as any[]);
|
||||
// "before"|"after"|"abort"|"timeout"|"error"|"auth"|"success"|"complete"
|
||||
let fun = this._lisentEventList.filter((el) : boolean => el.type == this.status!)
|
||||
let funCalls = fun.map((el) : funType => el.fun)
|
||||
|
||||
if (funCalls.length == 0) {
|
||||
if (this.status == 'auth' || this.status == 'before') {
|
||||
return Promise.resolve([true] as any[]);
|
||||
}
|
||||
return Promise.resolve([this.result] as any[]);
|
||||
}
|
||||
|
||||
return this._buildPromise(funCalls)
|
||||
}
|
||||
/**
|
||||
* 递归执行异步函数
|
||||
* 由于官方的Promise.all,不知道是bug还是什么,始终无法执行成功
|
||||
* 因此自己写了个。
|
||||
*/
|
||||
private _buildPromise(lst : funType[]) : Promise<any[]> {
|
||||
let _this = this;
|
||||
let len = lst.length;
|
||||
let i = 0;
|
||||
let p = [] as any[]
|
||||
async function customPromiseAll(evt : funType) : Promise<any> {
|
||||
if (i >= len) return Promise.resolve(p);
|
||||
let arg : any = _this.selfOpts
|
||||
// 非这两事件,全部传递结果集给监听事件中。
|
||||
if (_this.status != 'auth' && _this.status != 'before') {
|
||||
arg = _this.result as xRequestResult
|
||||
}
|
||||
let v = await evt(arg);
|
||||
// 如果这两事件中返回的不是boolean,则强制转换为true通过。
|
||||
if ((_this.status == 'auth') && typeof v != 'boolean') {
|
||||
v = true;
|
||||
}
|
||||
if ((_this.status == 'before') && typeof v != 'boolean') {
|
||||
|
||||
let beforeOpts = JSON.stringify(v)
|
||||
_this.setOptions(JSON.parse<xRequestOptions>(beforeOpts!)!)
|
||||
v = true;
|
||||
}
|
||||
// before中按顺序中断了前面的,后面的不要再执行,直接结束。
|
||||
if ((_this.status == 'before') && typeof v == 'boolean') {
|
||||
let iabort = v as boolean;
|
||||
if (!iabort) {
|
||||
p.push(v);
|
||||
return Promise.resolve(p);
|
||||
}
|
||||
}
|
||||
|
||||
// 请后,与事件中返回的修改后的数据覆盖原本的。
|
||||
if ((_this.status == 'after') && typeof v == 'object') {
|
||||
_this.result = v as xRequestResult
|
||||
|
||||
}
|
||||
// 其它事件则全部结果集。
|
||||
if (_this.status == 'success' || _this.status == 'timeout' || _this.status == 'error' || _this.status == 'abort' || _this.status == 'complete') {
|
||||
v = _this.result
|
||||
}
|
||||
|
||||
i += 1;
|
||||
p.push(v);
|
||||
if (i >= len) return Promise.resolve(p);
|
||||
return await customPromiseAll(lst[i]);
|
||||
}
|
||||
return new Promise((res, rej) => {
|
||||
|
||||
customPromiseAll(lst[i]).then(() => {
|
||||
res(p)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 请置指定请求的状态值。
|
||||
*/
|
||||
private _setDevReqStatus(id : string) {
|
||||
if (!xRequestCall.dev) return;
|
||||
let eventType = this.status;
|
||||
let index = -1;
|
||||
for (let i = 0; i < xRequestCall.history.length; i++) {
|
||||
let item = xRequestCall.history[i];
|
||||
if (id == item.id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index > -1 && eventType != null) {
|
||||
xRequestCall.history[index].status = eventType;
|
||||
if (eventType == 'complete' || eventType == 'abort' || eventType == 'auth') {
|
||||
xRequestCall.history[index].loaded = true;
|
||||
xRequestCall.history[index].loading = false;
|
||||
}
|
||||
if (eventType == 'before') {
|
||||
xRequestCall.history[index].loading = true;
|
||||
}
|
||||
if (eventType == 'success') {
|
||||
xRequestCall.history[index].time = Date.now() - xRequestCall.history[index].time;
|
||||
}
|
||||
if (eventType == 'complete' || eventType == 'success') {
|
||||
xRequestCall.history[index].result = this.result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
async request(opts : xRequestOptions | null = null) : Promise<xRequestResult> {
|
||||
let _this = this;
|
||||
|
||||
let _thisOpts = this.setOptions(opts)
|
||||
|
||||
let requestId = getUid()
|
||||
|
||||
if (xRequestCall.dev) {
|
||||
xRequestCall.history.push({
|
||||
id: requestId,
|
||||
loading: true,
|
||||
loaded: false,
|
||||
status: "",
|
||||
time: Date.now(),
|
||||
api: _thisOpts.url,
|
||||
result: {} as any
|
||||
} as xRequestHistoryType)
|
||||
}
|
||||
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.showLoading({ title: _thisOpts!.loadToastText!, mask: true })
|
||||
}
|
||||
|
||||
const url = _thisOpts.hostUrl + _thisOpts.url;
|
||||
|
||||
if (url == "" || _thisOpts.hostUrl == "") {
|
||||
this.status = 'error';
|
||||
this._setDevReqStatus(requestId);
|
||||
await this._callFun_build()
|
||||
console.warn("未填写请求接口")
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
if (_thisOpts.showErrorToast) {
|
||||
return new Promise<xRequestResult>((tres, trej) => {
|
||||
uni.showToast({ title: i18n.t("tmui4x.xRequest.hostFailEmpty"), icon: 'error', mask: true, complete() { trej(_this.result) } })
|
||||
})
|
||||
}
|
||||
return Promise.reject(_this.result)
|
||||
}
|
||||
|
||||
|
||||
if (xRequestCall.authPass == false) {
|
||||
this.status = 'auth'
|
||||
console.warn("授权失败,中断请求")
|
||||
this._setDevReqStatus(requestId);
|
||||
await this._callFun_build()
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
return Promise.reject(_thisOpts)
|
||||
}
|
||||
|
||||
// 检查是否使用缓存,只针对post及get方式进行缓存.
|
||||
if (_thisOpts.useCache && (_thisOpts.method == 'GET' || _thisOpts.method == 'POST')) {
|
||||
// 尝试从缓存获取响应
|
||||
const cachedResponse = this.cacheManager.get(_thisOpts);
|
||||
|
||||
if (cachedResponse != null) {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
console.log('缓存接口:', _thisOpts.url)
|
||||
}
|
||||
const cachedata = cachedResponse as UTSJSONObject
|
||||
const _cookies = cachedata.getArray<string>('cookies');
|
||||
const cacheResult = {
|
||||
data: cachedata.getAny('data'),
|
||||
statusCode: cachedata.getNumber('statusCode')!,
|
||||
header: cachedata.getAny('header')!,
|
||||
cookies: _cookies == null ? ([] as string[]) : _cookies!,
|
||||
} as xRequestResult
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
return Promise.resolve(cacheResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.status = 'before';
|
||||
this._setDevReqStatus(requestId);
|
||||
let jg2 = await this._callFun_build()
|
||||
let isAbor = (jg2 as boolean[]).some((el) : boolean => !el)
|
||||
|
||||
// 是否中断请求
|
||||
if (isAbor) {
|
||||
console.warn("事件before中断了请求")
|
||||
this.status = 'abort';
|
||||
this._setDevReqStatus(requestId);
|
||||
await this._callFun_build()
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
return Promise.reject(this.result)
|
||||
}
|
||||
|
||||
|
||||
// "before" | "after" | "abort" | "timeout" | "error" | "auth" | "success" | "complete"
|
||||
return new Promise((res, rej) => {
|
||||
_this.reqtask = uni.request({
|
||||
url: url,
|
||||
data: _thisOpts.data,
|
||||
header: _thisOpts.header,
|
||||
method: _thisOpts.method,
|
||||
timeout: _thisOpts.timeout,
|
||||
firstIpv4: _thisOpts.firstIpv4,
|
||||
sslVerify: false,
|
||||
withCredentials: false,
|
||||
dataType: _thisOpts.dataType,
|
||||
responseType: _thisOpts.responseType,
|
||||
success(rst) {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
console.log('请求接口成功:', _thisOpts.url, rst)
|
||||
}
|
||||
_this.result = {
|
||||
data: rst.data,
|
||||
statusCode: rst.statusCode,
|
||||
header: rst.header,
|
||||
cookies: rst.cookies as string[],
|
||||
} as xRequestResult
|
||||
|
||||
_this.status = 'after';
|
||||
_this._setDevReqStatus(requestId);
|
||||
_this._callFun_build()
|
||||
.then((v) => {
|
||||
if (_thisOpts.successStatusCode != rst.statusCode) {
|
||||
_this.status = 'error';
|
||||
_this.result.statusCode = rst.statusCode
|
||||
_this._setDevReqStatus(requestId);
|
||||
_this._callFun_build()
|
||||
.then(() => {
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
if (_thisOpts.showErrorToast) {
|
||||
let msg = _thisOpts!.errorToastText
|
||||
msg = msg==""?(i18n.t("tmui4x.xRequest.error")+rst.statusCode.toString()):msg
|
||||
uni.showToast({ title: msg!, mask: true, icon: 'error', complete() { rej(_this.result) } })
|
||||
} else {
|
||||
rej(_this.result)
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
let jgtss = v as xRequestResult[]
|
||||
let formartv = _this.result as xRequestResult
|
||||
if (jgtss.length > 0) {
|
||||
formartv = jgtss[jgtss.length - 1]
|
||||
}
|
||||
_this.result = formartv;
|
||||
|
||||
_this.status = 'success';
|
||||
_this._setDevReqStatus(requestId);
|
||||
_this._callFun_build()
|
||||
.then(() => {
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
_this.cacheManager.set(_thisOpts, _this.result);
|
||||
if (_thisOpts.showSuccessToast) {
|
||||
//默认读取data下的msg字段。
|
||||
let d = formartv.data;
|
||||
let msg = _thisOpts!.successToastText
|
||||
if (typeof d == 'object' && d != null && !Array.isArray(d)&&msg=="") {
|
||||
try {
|
||||
let s = d as UTSJSONObject;
|
||||
msg = s.getString('msg') != null ? s.getString('msg')! : msg
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
console.error("服务没有返回msg字段")
|
||||
}
|
||||
|
||||
}
|
||||
msg = msg==""?i18n.t("tmui4x.xRequest.success"):msg
|
||||
uni.showToast({ title: msg!, icon: 'none', mask: true, complete() { res(_this.result) } })
|
||||
} else {
|
||||
res(_this.result)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
fail(er) {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
console.error(er)
|
||||
}
|
||||
if (er.errCode == 5) {
|
||||
_this.status = 'timeout';
|
||||
} else {
|
||||
_this.status = 'error';
|
||||
}
|
||||
|
||||
_this.result.statusCode = er.errCode
|
||||
_this._setDevReqStatus(requestId);
|
||||
_this._callFun_build()
|
||||
.then(() => {
|
||||
if (_thisOpts.showLoadToast) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
if (_thisOpts.showErrorToast) {
|
||||
uni.showToast({ title: i18n.t("tmui4x.xRequest.error") + er.errCode.toString(), mask: true, icon: 'error', complete() { rej(_this.result) } })
|
||||
} else {
|
||||
rej(_this.result)
|
||||
}
|
||||
})
|
||||
},
|
||||
complete() {
|
||||
_this.status = 'complete';
|
||||
_this._setDevReqStatus(requestId);
|
||||
_this._callFun_build()
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* 动画过渡函数
|
||||
* @version 1.0.0
|
||||
* @author tmzdy
|
||||
* @host https://xui.tmui.design
|
||||
* @description 异常简便,没有繁杂的配置选项,只有快速的开发部署。
|
||||
*/
|
||||
import bezier from "./bezier.uts"
|
||||
import { xTweenStatus, xTweenCallbackFunType, xTweenAnimate, xTweenEventCallFunType, xTweenEventCall } from "../../interface.uts"
|
||||
import { getUid } from "./xCoreUtil.uts";
|
||||
|
||||
type FrameRequestCallback = (time:number)=>void;
|
||||
|
||||
export class xTween {
|
||||
private frameId : null | number = null;
|
||||
//当前全局动画是否在执行中,只要有一个动画在执行就会是true
|
||||
isRuning : boolean = false;
|
||||
//所有动画是否结束
|
||||
isStoping : boolean = true;
|
||||
//刷新率,不会立即得出结题,需要有至少执行16ms才可能计算出帖率
|
||||
frmae = 0
|
||||
// 舞台是否在执行中
|
||||
isRendering = false;
|
||||
|
||||
private _callListFun : xTweenEventCallFunType[] = []
|
||||
|
||||
private _frema_starttimes = 0
|
||||
|
||||
private enters : (listAni : xTweenEventCallFunType[], tims : number) => void = (listAni : xTweenEventCallFunType[], tims : number) => { }
|
||||
private complete : () => void = () => { }
|
||||
|
||||
private lastTime = 0
|
||||
private nextHandle = 0
|
||||
// 存储回调函数的映射
|
||||
private callbacks : Map<number, FrameRequestCallback> = new Map();
|
||||
|
||||
private easingList = new Map<string, number[]>([
|
||||
["linear", [0.250, 0.250, 0.750, 0.750]],
|
||||
["ease", [0.250, 0.100, 0.250, 1.000]],
|
||||
["easeIn", [0.420, 0.000, 1.000, 1.000]],
|
||||
["easeOut", [0.000, 0.000, 0.580, 1.000]],
|
||||
["easeInOut", [0.420, 0.000, 0.580, 1.000]],
|
||||
["easeInQuad", [0.550, 0.085, 0.680, 0.530]],
|
||||
["easeOutQuad", [0.250, 0.460, 0.450, 0.940]],
|
||||
["easeInOutQuad", [0.455, 0.030, 0.515, 0.955]],
|
||||
["easeInCubic", [0.550, 0.055, 0.675, 0.190]],
|
||||
["easeOutCubic", [0.215, 0.610, 0.355, 1.000]],
|
||||
["easeInOutCubic", [0.645, 0.045, 0.355, 1.000]],
|
||||
["easeInQuart", [0.895, 0.030, 0.685, 0.220]],
|
||||
["easeOutQuart", [0.165, 0.840, 0.440, 1.000]],
|
||||
["easeInOutQuart", [0.770, 0.000, 0.175, 1.000]],
|
||||
["easeInQuint", [0.755, 0.050, 0.855, 0.060]],
|
||||
["easeOutQuint", [0.230, 1.000, 0.320, 1.000]],
|
||||
["easeInOutQuint", [0.860, 0.000, 0.070, 1.000]],
|
||||
["easeInSine", [0.470, 0.000, 0.745, 0.715]],
|
||||
["easeOutSine", [0.390, 0.575, 0.565, 1.000]],
|
||||
["easeInOutSine", [0.445, 0.050, 0.550, 0.950]],
|
||||
["easeInExpo", [0.950, 0.050, 0.795, 0.035]],
|
||||
["easeOutExpo", [0.190, 1.000, 0.220, 1.000]],
|
||||
["easeInOutExpo", [1.000, 0.000, 0.000, 1.000]],
|
||||
["easeInCirc", [0.600, 0.040, 0.980, 0.335]],
|
||||
["easeOutCirc", [0.075, 0.820, 0.165, 1.000]],
|
||||
["easeInOutBack", [0.680, -0.550, 0.265, 1.550]],
|
||||
["tmxEase", [0.42, 0.38, 0.15, 0.93]]
|
||||
]);
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
private customRequestAnimationFrame(callback : FrameRequestCallback) : number {
|
||||
const currTime : number = Date.now();
|
||||
const timeToCall : number = Math.max(0, 16 - (currTime - this.lastTime));
|
||||
const handle : number = this.nextHandle++;
|
||||
|
||||
this.callbacks.set(handle, callback);
|
||||
|
||||
const id = setTimeout(() => {
|
||||
callback(currTime + timeToCall);
|
||||
}, timeToCall);
|
||||
|
||||
this.lastTime = currTime + timeToCall;
|
||||
return handle;
|
||||
}
|
||||
|
||||
private customCancelAnimationFrame(handle : number) {
|
||||
if (this.callbacks.has(handle)) {
|
||||
this.callbacks.delete(handle);
|
||||
clearTimeout(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动渲染,并非执行动画,执行动画需要使用play
|
||||
* 但如果不启动渲染,play中的动画都无法执行.
|
||||
*/
|
||||
startRender() : xTween {
|
||||
this.isRendering = true;
|
||||
if (this.frameId == null) {
|
||||
this._run(this)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 销毁渲染.
|
||||
*/
|
||||
destroy() {
|
||||
if (this.frameId != null) {
|
||||
// #ifdef APP||WEB
|
||||
cancelAnimationFrame(this.frameId!)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
this.customCancelAnimationFrame(this.frameId!)
|
||||
// #endif
|
||||
}
|
||||
this.frameId = null;
|
||||
this.isRendering = false;
|
||||
|
||||
}
|
||||
getFrame() : number {
|
||||
return this.frmae
|
||||
}
|
||||
/**
|
||||
* 所有事件动画执行完毕
|
||||
*/
|
||||
setComplete(call : () => void) : xTween {
|
||||
this.complete = call
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置帖动画,舞台渲染执行帖函数
|
||||
* 它不是animate动画,没有进度,只会不停的执行.直接舞台渲染被注销.
|
||||
*/
|
||||
setEnter(call : (listAni : xTweenEventCallFunType[], tims : number) => void) : xTween {
|
||||
this.enters = call
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 添加一个动画,可以重复加达到多个动画联合执行的效果
|
||||
* 并且 每一个动画都是独立存在,这样可以细微的控制每个动画
|
||||
* 来达到联合处理的效果.
|
||||
*/
|
||||
addAnimate(opts : xTweenAnimate) : string {
|
||||
let uid = getUid();
|
||||
let call : xTweenEventCall = (item : xTweenEventCallFunType) => { };
|
||||
|
||||
this._callListFun.push({
|
||||
id: uid,
|
||||
ease: this._getEasing(opts?.ease ?? 'linear'),
|
||||
duration: opts.duration,
|
||||
status: 1,
|
||||
progress: 0,
|
||||
oldProgeress: 0,
|
||||
startTime: 0,
|
||||
autoRemove: opts?.autoRemove != null ? (opts!.autoRemove as boolean) : true,
|
||||
complete: opts?.complete != null ? (opts!.complete!) : call,
|
||||
enter: opts?.enter != null ? (opts!.enter!) : call,
|
||||
start: opts?.start != null ? (opts!.start!) : call,
|
||||
pause: opts?.pause != null ? (opts!.pause!) : call,
|
||||
loop: opts?.loop != null ? (opts!.loop!) : 1,
|
||||
tyty: opts?.tyty != null ? (opts!.tyty!) : false,
|
||||
step: opts?.step != null ? (opts!.step!) : 1,
|
||||
_finishLoop: 0,
|
||||
reverse: false
|
||||
} as xTweenEventCallFunType)
|
||||
return uid;
|
||||
}
|
||||
/**
|
||||
* 删除一个事件.
|
||||
* @param {string} uid 如果为null表示删除所有.
|
||||
*/
|
||||
removeAnimate(uid : string | null = null) : xTween {
|
||||
if (uid == null) {
|
||||
this._callListFun = [] as xTweenEventCallFunType[]
|
||||
} else {
|
||||
let index = this._getCallIndex(uid as string);
|
||||
if (index > -1) {
|
||||
this._callListFun.splice(index, 1)
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 播放动画。
|
||||
* @param {string|null} uid 动画id,如果不填写或者为null表示播放所有动画
|
||||
*/
|
||||
play(uid : string | null = null) {
|
||||
if (uid != null && uid != '') {
|
||||
let index = this._getCallIndex(uid! as string)
|
||||
if (index > -1) {
|
||||
let item = this._callListFun[index]
|
||||
if (item.status == 1) {
|
||||
this._by_run_set_status(item, 1)
|
||||
this._by_run_item_callFun(item, 0)
|
||||
|
||||
}
|
||||
// 马上要执行运行
|
||||
this._by_run_set_status(item, 4)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._callListFun.length; i++) {
|
||||
let item = this._callListFun[i]
|
||||
if (item.status == 1) {
|
||||
this._by_run_set_status(item, 1)
|
||||
this._by_run_item_callFun(item, 0)
|
||||
}
|
||||
|
||||
this._by_run_set_status(item, 4)
|
||||
|
||||
}
|
||||
}
|
||||
this._setGlobaleStatus(1)
|
||||
}
|
||||
getAnimationListLen() : number {
|
||||
return this._callListFun.length;
|
||||
}
|
||||
/**
|
||||
* 暂停动画。
|
||||
* @param {string|null} uid 动画id,如果不填写或者为null表示暂停所有动画
|
||||
*/
|
||||
pause(uid : string | null = null) : xTween {
|
||||
if (uid != null && uid != '') {
|
||||
let index = this._getCallIndex(uid! as string)
|
||||
if (index > -1) {
|
||||
let item = this._callListFun[index]
|
||||
|
||||
this._by_run_set_status(item, this._isLoopPauseing(item) ? 6 : 3)
|
||||
this._by_run_item_callFun(item, item.progress)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._callListFun.length; i++) {
|
||||
let item = this._callListFun[i]
|
||||
this._by_run_set_status(item, this._isLoopPauseing(item) ? 6 : 3)
|
||||
this._by_run_item_callFun(item, item.progress)
|
||||
}
|
||||
this._setGlobaleStatus(2)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 结束动画。
|
||||
* @param {string|null} uid 动画id,如果不填写或者为null表示结束所有动画
|
||||
*/
|
||||
stop(uid : string | null = null) : xTween {
|
||||
|
||||
if (uid != null && uid != '') {
|
||||
let index = this._getCallIndex(uid! as string)
|
||||
if (index > -1) {
|
||||
let item = this._callListFun[index]
|
||||
this._by_run_set_status(item, 2)
|
||||
this._by_run_item_callFun(item, 1)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._callListFun.length; i++) {
|
||||
let item = this._callListFun[i]
|
||||
this._by_run_set_status(item, 2)
|
||||
this._by_run_item_callFun(item, 1)
|
||||
}
|
||||
this._callListFun = this._callListFun.filter((el : xTweenEventCallFunType) : boolean => !el.autoRemove)
|
||||
this.enters(this._callListFun, 0)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private _getEasing(args : any) : xTweenCallbackFunType | null {
|
||||
let fun : xTweenCallbackFunType | null = (x : number) : number => x;
|
||||
if (typeof args == 'string') {
|
||||
let animateNumber = this.easingList.get(args as string)
|
||||
if (animateNumber != null) {
|
||||
let ease = animateNumber! as number[]
|
||||
fun = bezier(ease[0], ease[1], ease[2], ease[3])
|
||||
}
|
||||
} else if (Array.isArray(args)) {
|
||||
let argsar = args as number[]
|
||||
if (argsar.length == 4) {
|
||||
fun = bezier(argsar[0], argsar[1], argsar[2], argsar[3])
|
||||
}
|
||||
} else {
|
||||
let animateNumber = this.easingList.get('linear')
|
||||
if (animateNumber != null) {
|
||||
let ease = animateNumber! as number[]
|
||||
fun = bezier(ease[0], ease[1], ease[2], ease[3])
|
||||
}
|
||||
}
|
||||
|
||||
return fun;
|
||||
}
|
||||
private _getCallIndex(uid : string) : number {
|
||||
let index = -1;
|
||||
for (let i = 0; i < this._callListFun.length; i++) {
|
||||
let item = this._callListFun[i]
|
||||
if (item.id == uid) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private _isLoopPauseing(item : xTweenEventCallFunType) : boolean {
|
||||
if (item.loop == -1) return true;
|
||||
if (item._finishLoop < item.loop) return true;
|
||||
return false;
|
||||
}
|
||||
private _by_run_set_status(item : xTweenEventCallFunType, status : xTweenStatus) {
|
||||
|
||||
if (status == 1) {
|
||||
item.progress = 0
|
||||
item.startTime = 0
|
||||
item._finishLoop = 0
|
||||
} else if (status == 2) {
|
||||
item.progress = 1;
|
||||
item.oldProgeress = 0;
|
||||
item.startTime = 0
|
||||
item._finishLoop = item.loop
|
||||
} else if (status == 3) {
|
||||
item.startTime = 0
|
||||
item.oldProgeress = item.progress
|
||||
} else if (status == 5) {
|
||||
item.progress = 0
|
||||
item.oldProgeress = 0
|
||||
item.startTime = 0
|
||||
} else if (status == 6) {
|
||||
item.oldProgeress = item.progress
|
||||
item.startTime = 0
|
||||
}
|
||||
item.status = status;
|
||||
}
|
||||
|
||||
private _by_run_item_callFun(item : xTweenEventCallFunType, progress : number) {
|
||||
if (item.status == 1) {
|
||||
item.start(item)
|
||||
}
|
||||
if (item.status == 2) {
|
||||
item.enter(item)
|
||||
item.complete(item)
|
||||
if (item.autoRemove) {
|
||||
let index = this._getCallIndex(item.id)
|
||||
if (index > -1) {
|
||||
this._callListFun.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.status == 3) {
|
||||
item.pause(item)
|
||||
}
|
||||
if (item.status == 4 || item.status == 5) {
|
||||
item.enter(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1:执行中
|
||||
* 2:未在执行动画。
|
||||
*/
|
||||
private _setGlobaleStatus(type : number) {
|
||||
if (type == 1) {
|
||||
this.isRuning = true;
|
||||
this.isStoping = false
|
||||
} else if (type == 2) {
|
||||
this.isRuning = false;
|
||||
this.isStoping = true
|
||||
}
|
||||
}
|
||||
|
||||
private _run(_this : xTween) {
|
||||
if (!_this.isRendering) {
|
||||
return;
|
||||
}
|
||||
function actions(times:number){
|
||||
_this.enters(_this._callListFun, times)
|
||||
if (_this._frema_starttimes == 0) {
|
||||
_this._frema_starttimes = times
|
||||
}
|
||||
let isAllCompelted = true;
|
||||
for (let i = 0; i < _this._callListFun.length; i++) {
|
||||
let item = _this._callListFun[i]
|
||||
let isFinishStatus = 1
|
||||
if (item.status == 4 || item.status == 5) {
|
||||
if (item.startTime == 0) {
|
||||
item.startTime = times
|
||||
}
|
||||
// 计算当前进度
|
||||
if (_this.frmae > 0) {
|
||||
let progress = Math.min((times - (item.startTime)) / item.duration + item.oldProgeress, 1);
|
||||
|
||||
let eas = item.ease!
|
||||
item.progress = eas(progress)
|
||||
|
||||
if (progress == isFinishStatus) {
|
||||
item._finishLoop += 1
|
||||
if (item.loop > 0) {
|
||||
if (item.loop == item._finishLoop) {
|
||||
_this._by_run_set_status(item, 2)
|
||||
_this._by_run_item_callFun(item, isFinishStatus)
|
||||
|
||||
} else {
|
||||
_this._by_run_set_status(item, 5)
|
||||
}
|
||||
} else if (item.loop == -1) {
|
||||
_this._by_run_set_status(item, 5)
|
||||
}
|
||||
if (item.tyty) {
|
||||
item.reverse = !item.reverse
|
||||
}
|
||||
}
|
||||
_this._by_run_item_callFun(item, item.progress)
|
||||
}
|
||||
|
||||
}
|
||||
if (item.progress != 2) {
|
||||
isAllCompelted = false;
|
||||
}
|
||||
}
|
||||
if (isAllCompelted && _this.isRuning) {
|
||||
_this.complete()
|
||||
_this._setGlobaleStatus(2)
|
||||
}
|
||||
_this.frmae = Math.ceil(1000 / (times - _this._frema_starttimes))
|
||||
_this._frema_starttimes = times
|
||||
_this._run(_this)
|
||||
}
|
||||
// #ifdef APP||WEB
|
||||
_this.frameId = requestAnimationFrame((times : number) => {
|
||||
actions(times)
|
||||
})
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
_this.frameId = this.customRequestAnimationFrame((times : number) => {
|
||||
actions(times)
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
import { XUPLOADFILE_INFO, XUPLOADFILE_FILE_VALUE, XUPLOADFILE_FILE_INFO, XUPLOADFILE_EVENT_NAME } from "../../interface.uts"
|
||||
import { getUid } from "./xCoreUtil.uts"
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
type CONFIG = {
|
||||
count : number,
|
||||
sourceType : Array<string>,
|
||||
sizeType : Array<string>,
|
||||
hostUrl : string,
|
||||
name : string,
|
||||
header : UTSJSONObject,
|
||||
formData : UTSJSONObject,
|
||||
multipart : boolean,
|
||||
autoUpload : boolean,
|
||||
statusCode : number,
|
||||
compress:boolean,
|
||||
quality:number|null,
|
||||
compressedHeight:number|null,
|
||||
compressedWidth:number|null
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体文件上传
|
||||
*/
|
||||
export class xUploadMedia {
|
||||
model = 'photo'
|
||||
videoOps={
|
||||
pageOrientation:'auto',
|
||||
albumMode:'system',
|
||||
sourceType:['album', 'camera'] as string[],
|
||||
compressed:true,
|
||||
maxDuration:60,
|
||||
camera:'back'
|
||||
} as UTSJSONObject
|
||||
config = {
|
||||
count: 9,
|
||||
sourceType: ['album', 'camera'],
|
||||
sizeType: ['original', 'compressed'],
|
||||
hostUrl: "",
|
||||
name: "file",
|
||||
header: {} as UTSJSONObject,
|
||||
multipart: false,
|
||||
formData:{} as UTSJSONObject,
|
||||
autoUpload: true,
|
||||
statusCode: 200,
|
||||
compress:true,
|
||||
quality:80,
|
||||
compressedHeight:null,
|
||||
compressedWidth:null
|
||||
} as CONFIG
|
||||
fileList : Array<XUPLOADFILE_FILE_INFO> = []
|
||||
currentIndex = 0;
|
||||
uploading = false;
|
||||
uploadObj = null as null | UploadTask
|
||||
systemError = xConfig.i18n.t("tmui4x.uploadMedia.systemError")
|
||||
limitMaxCount = xConfig.i18n.t("tmui4x.uploadMedia.limitMaxCount")
|
||||
constructor(opts : XUPLOADFILE_INFO = {} as XUPLOADFILE_INFO) {
|
||||
this.chuliConfigArgs(opts)
|
||||
}
|
||||
chooseBefore = (res : string[]) : Promise<string[]> => Promise.resolve(res)
|
||||
complete = (res : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => Promise.resolve(res)
|
||||
change = (res : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => Promise.resolve(res)
|
||||
beforeComplete = (res : XUPLOADFILE_FILE_INFO) :XUPLOADFILE_FILE_INFO => res
|
||||
myChangeSync = function (res : XUPLOADFILE_FILE_INFO[]) { }
|
||||
beforeUpload = (res : XUPLOADFILE_FILE_INFO) : Promise<XUPLOADFILE_FILE_INFO> => Promise.resolve(res)
|
||||
events:Map<XUPLOADFILE_EVENT_NAME,(res : any) => Promise<any>> = new Map();
|
||||
|
||||
|
||||
/**
|
||||
* 处理配置参数
|
||||
* @param opts 配置参数
|
||||
*/
|
||||
private chuliConfigArgs(opts : XUPLOADFILE_INFO) : CONFIG {
|
||||
this.config = {
|
||||
count: opts.count == null ? this.config.count : opts.count as number,
|
||||
statusCode: opts.statusCode == null ? this.config.statusCode : opts.statusCode as number,
|
||||
sourceType: opts.sourceType == null ? this.config.sourceType : opts.sourceType as string[],
|
||||
sizeType: opts.sizeType == null ? this.config.sizeType : opts.sizeType as string[],
|
||||
hostUrl: opts.hostUrl == null ? this.config.hostUrl : opts.hostUrl as string,
|
||||
name: opts.name == null ? this.config.name : opts.name as string,
|
||||
header: opts.header == null ? this.config.header : opts.header as UTSJSONObject,
|
||||
formData: opts.formData == null ? this.config.formData : opts.formData as UTSJSONObject,
|
||||
multipart: opts.multipart == null ? this.config.multipart : opts.multipart as boolean,
|
||||
autoUpload: opts.autoUpload == null ? this.config.autoUpload : opts.autoUpload as boolean,
|
||||
compress: opts.compress == null ? this.config.compress : opts.compress as boolean,
|
||||
quality: opts.quality == null ? this.config.quality : opts.quality as number,
|
||||
compressedHeight: opts.compressedHeight,
|
||||
compressedWidth: opts.compressedWidth,
|
||||
} as CONFIG
|
||||
return this.config
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.uploadObj == null) return
|
||||
this.currentIndex = 0;
|
||||
this.uploading = false;
|
||||
this.uploadObj!.abort()
|
||||
this.uploadObj = null;
|
||||
console.info("xUploadMedia:中断上传")
|
||||
}
|
||||
chooseMedia() {
|
||||
if (this.fileList.length == this.config.count) {
|
||||
// "已超最大上传数量"
|
||||
uni.showToast({ title: this.limitMaxCount, mask: true, icon: 'none' })
|
||||
console.warn("xUploadMedia:已经超过最大上传数量")
|
||||
return;
|
||||
}
|
||||
if(this.model == 'photo'){
|
||||
uni.chooseImage({
|
||||
count: Math.max(this.config.count - this.fileList.length, 0),
|
||||
sourceType: this.config.sourceType,
|
||||
sizeType: this.config.sizeType,
|
||||
success: (res) => {
|
||||
|
||||
let temps = [] as UTSJSONObject[]
|
||||
// #ifdef uniVersion >= 4.31
|
||||
let items = res.tempFiles;
|
||||
for(let i=0;i<res.tempFiles.length;i++){
|
||||
temps.push({path:items[i].path,size:items[i].size} as UTSJSONObject)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef uniVersion < 4.31
|
||||
temps = res.tempFiles as UTSJSONObject[]
|
||||
// #endif
|
||||
let chooseBefore = this.events.get('chooseBefore')
|
||||
if(chooseBefore!=null){
|
||||
// 处理新文件。
|
||||
if (Array.isArray(temps)) {
|
||||
chooseBefore(res.tempFilePaths).then(() => {
|
||||
|
||||
this.addNewFile(temps)
|
||||
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}).catch(er => {
|
||||
console.error(er)
|
||||
uni.showModal({
|
||||
title:this.systemError,
|
||||
content:er as string,
|
||||
showCancel:false
|
||||
})
|
||||
})
|
||||
|
||||
} else {
|
||||
// @ts-ignore
|
||||
let tps = res.tempFilePaths as UTSJSONObject
|
||||
chooseBefore([tps] as UTSJSONObject[]).then(() => {
|
||||
this.addNewFile(temps)
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}).catch(er => {
|
||||
console.error(er)
|
||||
uni.showModal({
|
||||
title:this.systemError,
|
||||
content:er as string,
|
||||
showCancel:false
|
||||
})
|
||||
})
|
||||
}
|
||||
}else{
|
||||
|
||||
if (Array.isArray(temps)) {
|
||||
this.addNewFile(temps)
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}else{
|
||||
// @ts-ignore
|
||||
let tps = res.tempFilePaths as UTSJSONObject
|
||||
this.addNewFile([tps] as UTSJSONObject[])
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
fail: (err) => {
|
||||
this.chooseBefore([] as string[])
|
||||
console.warn("xUploadMedia:", err)
|
||||
}
|
||||
})
|
||||
}else if(this.model == 'video'){
|
||||
let pageOrientation = this.videoOps.getString('pageOrientation')
|
||||
pageOrientation = pageOrientation==null?'auto':pageOrientation
|
||||
let albumMode = this.videoOps.getString('albumMode')
|
||||
albumMode = albumMode==null?'system':albumMode
|
||||
let sourceType = this.videoOps.getArray<string>('sourceType')
|
||||
sourceType = sourceType==null?(['album', 'camera'] as string[]):sourceType
|
||||
let compressed = this.videoOps.getBoolean('compressed')
|
||||
compressed = compressed==null?true:compressed
|
||||
let maxDuration = this.videoOps.getNumber('maxDuration')
|
||||
maxDuration = maxDuration==null?60:maxDuration
|
||||
let camera = this.videoOps.getString('camera')
|
||||
camera = camera==null?'back':camera
|
||||
uni.chooseVideo({
|
||||
pageOrientation,
|
||||
albumMode,
|
||||
sourceType,
|
||||
compressed,
|
||||
maxDuration,
|
||||
camera,
|
||||
success: (res) => {
|
||||
|
||||
let temps = [
|
||||
{path:res.tempFilePath,size:res.size} as UTSJSONObject
|
||||
] as UTSJSONObject[]
|
||||
|
||||
let chooseBefore = this.events.get('chooseBefore')
|
||||
if(chooseBefore!=null){
|
||||
chooseBefore([res.tempFilePath] as string[]).then(() => {
|
||||
this.addNewFile(temps)
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}).catch(er => {
|
||||
console.error(er)
|
||||
uni.showModal({
|
||||
title:this.systemError,
|
||||
content:er as string,
|
||||
showCancel:false
|
||||
})
|
||||
})
|
||||
}else{
|
||||
this.addNewFile(temps)
|
||||
if (this.config.autoUpload && !this.uploading){
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
fail: (err) => {
|
||||
this.chooseBefore([] as string[])
|
||||
console.warn("xUploadMedia:", err)
|
||||
}
|
||||
} as ChooseVideoOptions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加事件监听
|
||||
* @param eventName 事件名称
|
||||
* @param callback(res : T) => Promise<T> 回调,注意如果是chooseBefore,外部回调时res是string[],其它为 XUPLOADFILE_FILE_INFO[]
|
||||
*/
|
||||
addListenEvent(eventName : XUPLOADFILE_EVENT_NAME, callback : (res : any) => Promise<any>) {
|
||||
this.events.set(eventName,callback)
|
||||
// if (eventName == 'chooseBefore') {
|
||||
// this.chooseBefore = (res2 : string[]) : Promise<string[]> => callback(res2 as unknown[] as T) as Promise<string[]>
|
||||
// } else if (eventName == 'complete') {
|
||||
// this.complete = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => callback(res2 as unknown[] as T) as Promise<XUPLOADFILE_FILE_INFO[]>
|
||||
// } else if (eventName == 'change') {
|
||||
// this.change = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => callback(res2 as unknown[] as T) as Promise<XUPLOADFILE_FILE_INFO[]>
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加事件监听
|
||||
* @param eventName 事件名称
|
||||
* @param callback(res : T) => Promise<T> 回调,注意如果是chooseBefore,外部回调时res是string[],其它为 XUPLOADFILE_FILE_INFO[]
|
||||
*/
|
||||
addListenEventsss<T extends unknown[]>(eventName : XUPLOADFILE_EVENT_NAME, callback : (res : T) => Promise<T>) {
|
||||
if (eventName == 'chooseBefore') {
|
||||
this.chooseBefore = (res2 : string[]) : Promise<string[]> => callback(res2 as unknown[] as T) as Promise<string[]>
|
||||
} else if (eventName == 'complete') {
|
||||
this.complete = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => callback(res2 as unknown[] as T) as Promise<XUPLOADFILE_FILE_INFO[]>
|
||||
} else if (eventName == 'change') {
|
||||
this.change = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise<XUPLOADFILE_FILE_INFO[]> => callback(res2 as unknown[] as T) as Promise<XUPLOADFILE_FILE_INFO[]>
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 设置上传的配置参数
|
||||
* @param opts 配置参数-XUPLOADFILE_INFO
|
||||
*/
|
||||
setConfig(opts : XUPLOADFILE_INFO) {
|
||||
this.chuliConfigArgs(opts)
|
||||
}
|
||||
|
||||
setVideoOps(config:UTSJSONObject,model:string){
|
||||
this.videoOps = config
|
||||
this.model = model;
|
||||
}
|
||||
setChangeSync(callBack : (res : XUPLOADFILE_FILE_INFO[]) => void) {
|
||||
this.myChangeSync = function (res2 : XUPLOADFILE_FILE_INFO[]) {
|
||||
callBack(res2)
|
||||
}
|
||||
}
|
||||
delFile(id : string) : boolean {
|
||||
let index = this.fileList.findIndex((item : XUPLOADFILE_FILE_INFO) : boolean => item.id == id)
|
||||
if (index >= 0) {
|
||||
let item = this.fileList[index];
|
||||
if (item.status == 1) {
|
||||
item.status = 4
|
||||
item.statusText = "取消上传"
|
||||
this.stop()
|
||||
}
|
||||
this.fileList.splice(index, 1)
|
||||
this.myChangeSync(this.fileList)
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}
|
||||
clear(){
|
||||
this.fileList = [] as XUPLOADFILE_FILE_INFO[]
|
||||
this.myChangeSync(this.fileList)
|
||||
}
|
||||
/**
|
||||
* 添加新选择的未上传文件
|
||||
*/
|
||||
private addNewFile(files : UTSJSONObject[]) {
|
||||
if (files.length == 0) return;
|
||||
let t = this;
|
||||
// #ifdef APP-ANDROID||APP-IOS
|
||||
if(this.model == 'photo'){
|
||||
let i=0;
|
||||
function compress(){
|
||||
if(i>=files.length) return;
|
||||
let item = files[i]! as UTSJSONObject;
|
||||
uni.compressImage({
|
||||
src: item.getString('path')!,
|
||||
quality: t.config.quality,
|
||||
compressedHeight: t.config.compressedHeight,
|
||||
compressedWidth: t.config.compressedWidth,
|
||||
success: (res:CompressImageSuccess) => {
|
||||
item.set("path",res.tempFilePath)
|
||||
i+=1;
|
||||
compress()
|
||||
},
|
||||
fail: (err) => {
|
||||
i+=1;
|
||||
compress()
|
||||
}
|
||||
})
|
||||
}
|
||||
if(this.config.compress){
|
||||
compress()
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
files.forEach((item : UTSJSONObject) => {
|
||||
let id = getUid();
|
||||
let name = "";
|
||||
let size = 0
|
||||
// #ifdef APP
|
||||
let items = item as UTSJSONObject
|
||||
name = items.getString('path')!
|
||||
size = items.getNumber('size')!
|
||||
// #endif
|
||||
|
||||
// #ifndef APP
|
||||
name = item.path!
|
||||
size = item.size!
|
||||
// #endif
|
||||
|
||||
this.fileList.push({
|
||||
id: id,
|
||||
type: "",
|
||||
size: size,
|
||||
extension: name.substring(name.lastIndexOf(".") + 1),
|
||||
statusText: '待上传',
|
||||
status: 0,
|
||||
path: name,
|
||||
progress: 0,
|
||||
response: "",
|
||||
name: name.substring(name.lastIndexOf("/") + 1),
|
||||
model:this.model
|
||||
} as XUPLOADFILE_FILE_INFO)
|
||||
})
|
||||
|
||||
this.myChangeSync(this.fileList)
|
||||
|
||||
}
|
||||
addFile(files:XUPLOADFILE_FILE_VALUE[]){
|
||||
let ids = this.fileList.map((el:XUPLOADFILE_FILE_INFO):string=>el.id)
|
||||
let i=0;
|
||||
files.forEach((el:XUPLOADFILE_FILE_VALUE)=>{
|
||||
let oldId = el.id == null?"":el.id! as string;
|
||||
if(!ids.includes(oldId)){
|
||||
let id = getUid();
|
||||
let name = el.url
|
||||
let status = el?.status??2;
|
||||
this.fileList.push({
|
||||
id: id,
|
||||
type: "",
|
||||
size: 0,
|
||||
extension: name.substring(name.lastIndexOf(".") + 1),
|
||||
statusText: status==2?'上传成功':'待上传',
|
||||
status: el?.status??2,
|
||||
path: name,
|
||||
progress: status==2?100:0,
|
||||
response: el.response == null ?"":el.response! as string,
|
||||
name: name.substring(name.lastIndexOf("/") + 1)
|
||||
} as XUPLOADFILE_FILE_INFO)
|
||||
++i;
|
||||
}
|
||||
})
|
||||
|
||||
if(i>0){
|
||||
this.myChangeSync(this.fileList)
|
||||
}
|
||||
|
||||
}
|
||||
_addFilesByself(files:XUPLOADFILE_FILE_INFO[]){
|
||||
this.fileList = files.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待上传的文件数量。
|
||||
*/
|
||||
private getWaitUploadFilesNumber() : number {
|
||||
let num = 0;
|
||||
this.fileList.forEach((item : XUPLOADFILE_FILE_INFO) => {
|
||||
if (item.status == 0 || item.status == 3 || item.status == 4) {
|
||||
num += 1;
|
||||
}
|
||||
})
|
||||
return num;
|
||||
}
|
||||
start() {
|
||||
if (this.uploading) return;
|
||||
this.uploading = true;
|
||||
this.currentIndex = 0
|
||||
this.uploadFile();
|
||||
}
|
||||
private uploadFile() {
|
||||
// if (!this.config.autoUpload) return;
|
||||
|
||||
// if (this.fileList.length == 0 || this.getWaitUploadFilesNumber() == 0) {
|
||||
// this.uploadObj = null
|
||||
// this.uploading = false;
|
||||
// console.warn("xUploadMedia:上传结束了")
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
if (this.currentIndex >= this.fileList.length) {
|
||||
this.uploadObj = null;
|
||||
this.uploading = false;
|
||||
|
||||
this.complete(this.fileList)
|
||||
this.myChangeSync(this.fileList)
|
||||
let complete = this.events.get('complete')
|
||||
|
||||
if(complete!=null){
|
||||
complete(this.fileList.slice(0))
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.beforeUpload(this.fileList[this.currentIndex]).then((beforeUploadFileRes:XUPLOADFILE_FILE_INFO)=>{
|
||||
this.fileList[this.currentIndex] = beforeUploadFileRes;
|
||||
let nowitemStatus = this.fileList[this.currentIndex].status
|
||||
if (nowitemStatus == 1 || nowitemStatus == 2 || nowitemStatus == 5) {
|
||||
this.uploadObj = null
|
||||
this.uploading = false;
|
||||
this.currentIndex += 1;
|
||||
this.uploadFile();
|
||||
return;
|
||||
}
|
||||
this.fileList[this.currentIndex].status = 1;
|
||||
this.fileList[this.currentIndex].statusText = "上传中";
|
||||
this.myChangeSync(this.fileList)
|
||||
this.uploadObj = uni.uploadFile({
|
||||
url: this.config.hostUrl,
|
||||
filePath: this.fileList[this.currentIndex].path,
|
||||
name: this.config.name,
|
||||
formData: this.config.formData,
|
||||
header:this.config.header,
|
||||
success: (uploadFileRes) => {
|
||||
if (uploadFileRes.statusCode != 200) {
|
||||
this.fileList[this.currentIndex].status = 3;
|
||||
this.fileList[this.currentIndex].statusText = "上传失败";
|
||||
this.currentIndex += 1;
|
||||
this.uploadFile();
|
||||
this.myChangeSync(this.fileList)
|
||||
return;
|
||||
}
|
||||
this.fileList[this.currentIndex].status = 2;
|
||||
this.fileList[this.currentIndex].response = uploadFileRes.data;
|
||||
this.fileList[this.currentIndex].statusText = "上传成功";
|
||||
let itemtemp = this.fileList.slice(0)[this.currentIndex]
|
||||
let calllItem = this.beforeComplete(itemtemp)
|
||||
this.fileList[this.currentIndex] = calllItem
|
||||
this.currentIndex += 1;
|
||||
this.uploadFile();
|
||||
this.myChangeSync(this.fileList)
|
||||
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("上传失败了,请检查配置:",err)
|
||||
this.fileList[this.currentIndex].status = 3;
|
||||
this.fileList[this.currentIndex].statusText = "上传失败";
|
||||
this.currentIndex += 1;
|
||||
this.uploadFile();
|
||||
},
|
||||
complete: () => {
|
||||
this.myChangeSync(this.fileList)
|
||||
}
|
||||
})
|
||||
|
||||
this.uploadObj?.onProgressUpdate((res) => {
|
||||
|
||||
this.fileList[this.currentIndex].progress = res.progress;
|
||||
// if(res.progress == 100){
|
||||
// this.fileList[this.currentIndex].status = 1;
|
||||
// this.fileList[this.currentIndex].statusText = "上传中";
|
||||
// }else{
|
||||
// this.fileList[this.currentIndex].status = 1;
|
||||
// this.fileList[this.currentIndex].statusText = "上传中";
|
||||
// }
|
||||
this.myChangeSync(this.fileList)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user