1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-06-11 12:49:30 +08:00

v1.0.0-beta 源码正式开源

This commit is contained in:
小莫唐尼
2022-12-06 15:08:29 +08:00
commit 636ae7b169
461 changed files with 116817 additions and 0 deletions
+553
View File
@@ -0,0 +1,553 @@
/**
* 日历库
* 作者:tmzdy
* 时间:2021-7-27
* 联系:zhongjihan@sina.com
*/
class calendar{
value= new Date();
now_day_month = new Date();
start_time = new Date(1900,0,1)
end_time = new Date(2100,11,31)
txtdateArray = [];
/**
* value:初始化时间
* start:开始始间 ,提供了后,会在返回的日历上标记每个日期是否是在start 和 end之间。
* end:结束时间
*/
constructor({value,start,end}) {
if(arguments.length===1){
let arg = arguments[0]
if(arg?.value){
value = value.replace(/-/g,'/');
let dobj = new Date(value);
this.value = new Date(dobj.getFullYear(),dobj.getMonth(),dobj.getDate());
this.now_day_month = this.value;
}
if(arg?.start){
let sv = start;
if(typeof sv ==='string'){
sv = new Date(sv.replace(/-/g,'/'))
}else if(typeof sv === 'object'){
sv = new Date(sv)
}
this.start_time = sv;
}
if(arg?.end){
let sv = end;
if(typeof sv ==='string'){
sv = new Date(sv.replace(/-/g,'/'))
}else if(typeof sv === 'object'){
sv = new Date(sv)
}
this.end_time = sv;
}
}
}
/**
* 日期转化为字符串, 4位年+2位月+2位日
*/
getDateStr(date) {
var _year = date.getFullYear();
var _month = date.getMonth(); // 月从0开始计数
var _d = date.getDate();
_month = (_month > 9) ? ("" + _month) : ("0" + _month);
_d = (_d > 9) ? ("" + _d) : ("0" + _d);
return _year + _month + _d;
}
// 设置当前计算的日历的时间 。格式为时间 格式。
setValue(value){
if(value){
let dobj = new Date();
if(typeof value === 'object'){
dobj = new Date(value);
}else{
value = value.replace(/-/g,'/');
dobj = new Date(value);
}
this.value = new Date(dobj.getFullYear(),dobj.getMonth(),dobj.getDate());
this.now_day_month = this.value;
}
return this;
}
// 未设置
setStart(start){
let sv = start;
if(typeof sv ==='string'){
sv = new Date(sv.replace(/-/g,'/'))
}else if(typeof sv === 'object'){
sv = new Date(sv)
}
this.start_time = sv;
return this;
}
setEnd(end){
let sv = end;
if(typeof sv ==='string'){
sv = new Date(sv.replace(/-/g,'/'))
}else if(typeof sv === 'object'){
sv = new Date(sv)
}
this.end_time = sv;
return this;
}
// 设置文本数据。携带在对象 中。
setTimeArrayText(textArray){
if(!Array.isArray(textArray)) return;
/**
* textArray
* {date:"2021-7-1",text:"你好"}
*/
this.txtdateArray = textArray;
return this;
}
monthDay(year, month) {
var date = new Date(year, month, 1, 0, 0, 0)
var yesterDay = new Date(date - 1000)
return yesterDay.getDate()
}
nongli(year,month,day){
const calendarobj = {
gregorianYear: null, //公历年
gregorianMonth: null, //公历月
gregorianDay: null, //公历日
weekday: null, //星期
hours: null,
minutes: null,
seconds: null,
lunarYear: null, //农历年
lunarMonth: null, //农历月
lunarDay: null, //农历日
lunarYearCn: '', //农历天干地支纪年
lunarMonthCn: '', //农历中文月
lunarDayCn: '', //农历中文日
zodiacYear: '', //农历生肖年
solarTerm: '', //节气
gregorianFestival: '', //公历节日
lunarFestival: '' //农历节日
}
let lunarInfo = [
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0,
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0]
let zodiacs = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
let Gan = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
let Zhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
let weekday = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
let now = new Date()
//用于计算农历年月日的数据
let GY = year
let GM = month
let GD = day
//==== 传入 offset 传回干支, 0=甲子
function cyclical(num) {
return(Gan[num % 10] + Zhi[num % 12])
}
//==== 传回农历 year年的总天数
function lYearDays(year) {
let i, sum = 348
for(i = 0x8000; i > 0x8; i >>= 1) {
sum += (lunarInfo[year - 1900] & i) ? 1: 0
}
return(sum + leapDays(year))
}
//==== 传回农历 year年闰月的天数
function leapDays(year) {
if(leapMonth(year)) {
return((lunarInfo[year-1900] & 0x10000)? 30: 29)
}
else {
return 0
}
}
//==== 传回农历 year年闰哪个月 1-12 , 没闰传回 0
function leapMonth(year) {
return(lunarInfo[year - 1900] & 0xf)
}
//==== 传回农历 year年month月的总天数
function monthDays(year, month) {
return( (lunarInfo[year - 1900] & (0x10000 >> month))? 30: 29 )
}
//==== 算出农历, 传入日期对象, 传回农历日期对象
// 该对象属性有 农历年year 农历月month 农历日day 是否闰年isLeap yearCyl dayCyl monCyl
function Lunar(objDate) {
let i, temp = 0
let baseDate = new Date(1900,0,31)
let offset = Math.floor((objDate - baseDate)/86400000)
let dayCyl = offset + 40
let monCyl = 14
for(i = 1900; i < 2050 && offset > 0; i++) {
temp = lYearDays(i)
offset -= temp
monCyl += 12
}
if(offset < 0) {
offset += temp;
i--;
monCyl -= 12
}
//农历年
let year = i
let yearCyl = i-1864
let leap = leapMonth(i) //闰哪个月
let isLeap = false //是否闰年
for(i=1; i<13 && offset>0; i++) {
//闰月
if(leap>0 && i === (leap+1) && isLeap === false) {
--i; isLeap = true; temp = leapDays(year);
}
else {
temp = monthDays(year, i);
}
//解除闰月
if(isLeap === true && i === (leap + 1)) {
isLeap = false
}
offset -= temp
if(isLeap === false) {
monCyl ++
}
}
if(offset === 0 && leap>0 && i===leap+1)
if(isLeap) {
isLeap = false
}
else {
isLeap = true
--i
--monCyl
}
if(offset<0){
offset += temp
--i
--monCyl
}
//农历月
let month = i
//农历日
let day = offset + 1
return {
year: year,
month: month,
day: day,
isLeap: isLeap,
leap: leap,
yearCyl: yearCyl,
dayCyl: dayCyl,
monCyl: monCyl
}
}
//==== 中文日期 m为传入月份,d为传入日期
function cDay(m, d){
let nStr1 = ['日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
let nStr2 = ['初', '十', '廿', '卅', '']
//农历中文月
let lunarMonthCn
//农历中文日
let lunarDayCn
if (m > 10){
lunarMonthCn = '十' + nStr1[m - 10]
} else {
lunarMonthCn = nStr1[m]
}
lunarMonthCn += '月'
switch (d) {
case 10: lunarDayCn = '初十'; break;
case 20: lunarDayCn = '二十'; break;
case 30: lunarDayCn = '三十'; break;
default: lunarDayCn = nStr2[Math.floor(d/10)] + nStr1[d % 10]
}
return {
lunarMonthCn: lunarMonthCn,
lunarDayCn: lunarDayCn
}
}
//节气
function getSolarTerm() {
let sTermInfo = [
0, 21208, 42467, 63836, 85337, 107014,
128867, 150921, 173149, 195551, 218072, 240693,
263343, 285989, 308563, 331033, 353350, 375494,
397447, 419210, 440795, 462224, 483532, 504758
]
let solarTerm = [
'小寒', '大寒', '立春', '雨水', '惊蛰', '春分',
'清明', '谷雨', '立夏', '小满', '芒种', '夏至',
'小暑', '大暑', '立秋', '处暑', '白露', '秋分',
'寒露', '霜降', '立冬', '小雪', '大雪', '冬至'
]
let solarTerms = ''
let tmp1 = new Date(
(31556925974.7 * (GY - 1900) + sTermInfo[(GM-1) * 2 + 1] * 60000) + Date.UTC(1900,0,6,2,5)
)
let tmp2 = tmp1.getUTCDate()
if (tmp2 === GD) solarTerms = solarTerm[(GM-1) * 2 + 1]
tmp1 = new Date(
(31556925974.7 * (GY - 1900) + sTermInfo[(GM-1) * 2] * 60000) + Date.UTC(1900,0,6,2,5)
)
tmp2= tmp1.getUTCDate()
if (tmp2 === GD) solarTerms = solarTerm[(GM-1) * 2]
return solarTerms
}
//==== 中文日期 m为传入月份,d为传入日期
function cDay(m ,d ){
let nStr1 = ['日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
let nStr2 = ['初', '十', '廿', '卅', '']
//农历中文月
let lunarMonthCn
//农历中文日
let lunarDayCn
if (m > 10){
lunarMonthCn = '十' + nStr1[m - 10]
} else {
lunarMonthCn = nStr1[m]
}
lunarMonthCn += '月'
switch (d) {
case 10: lunarDayCn = '初十'; break;
case 20: lunarDayCn = '二十'; break;
case 30: lunarDayCn = '三十'; break;
default: lunarDayCn = nStr2[Math.floor(d/10)] + nStr1[d % 10]
}
return {
lunarMonthCn: lunarMonthCn,
lunarDayCn: lunarDayCn
}
}
//去掉时分秒的日期
let sDObj = new Date(GY, GM-1, GD);
let lDObj = new Lunar(sDObj);
//节气
// calendar.solarTerm = getSolarTerm()
let n = cDay(lDObj.month,lDObj.day);
let y = cyclical( GY - 1900 + 36);
return {
year:y,
month:n.lunarMonthCn,
day:n.lunarDayCn,
shengxiao:zodiacs[(GY - 4) % 12],
jieqi:getSolarTerm()
};
}
//下个月,可以一直操作
nextMonth(){
this.value = new Date(this.value.getFullYear(),this.value.getMonth()+1,1);
return this;
}
//上个月,可以一直操作
prevMonth(){
this.value = new Date(this.value.getFullYear(),this.value.getMonth()-1,1);
return this;
}
//下一年
nexYear(){
this.value = new Date(this.value.getFullYear()+1,this.value.getMonth(),this.value.getDate());
return this;
}
//上一年
prevYear(){
this.value = new Date(this.value.getFullYear()-1,this.value.getMonth(),this.value.getDate());
return this;
}
// 把之前设置的上一年,下一年,上一月下一月等数据清除,恢复 到最原始的月份年份数据。
setInit(){
this.value = this.now_day_month;
return this;
}
// 返回初始化时的月份
getNowData(){
// 当前时间 。
let week = [7,1,2,3,4,5,6]
let text_week = ['周日','周一','周二','周三','周四','周五','周六']
let _thisdateStr = this.now_day_month.toLocaleString();
let _thisMothn = this.now_day_month.getMonth();//当前月
let _thisDay = this.now_day_month.getDate();//当前日
let _thisYear = this.now_day_month.getFullYear();//当前年
let _thisWeek = this.now_day_month.getDay();//当前周
let _thisMothn_day = new Date(_thisYear,_thisMothn,1) ; ///当月第一天数据。
let _thisDayDate = new Date(_thisYear,_thisMothn,_thisDay);
let months = [31,this.monthDay(_thisYear,_thisMothn),31,30,31,30,31,31,30,31,30,31];
let _thisMothn_lastDay = new Date(_thisYear,_thisMothn,months[_thisMothn]) ; ///当月最后数据。
let dateArray = [];//当前日历表数据。
let llineDate = [];//行数据。
let j=1;
let ishs = true //如果第一排没有本月数据需要切换模式。把本月放第一位。
for(let i=1 ;i <8;i++){
let tdy = new Date(_thisYear, _thisMothn, i - 6 - _thisMothn_day.getDay()); //当前循环日期。
if(i==7){
let tf = tdy.getTime()>= _thisMothn_day.getTime() && tdy.getTime() <= _thisMothn_lastDay.getTime() ?true:false;
if(!tf){
ishs = false;
break;
}
}
}
for(let i=1 ;i <43;i++){
var Day = null;
if(!ishs){
Day = new Date(_thisYear, _thisMothn, i + 1 - _thisMothn_day.getDay()); //当前循环日期。
}else{
Day = new Date(_thisYear, _thisMothn, i - 6 - _thisMothn_day.getDay()); //当前循环日期。
}
dateArray.push({
year:Day.getFullYear(),//年
month:Day.getMonth()+1,//月1-12
week:week[Day.getDay()],//周的数字1~7
week_text:text_week[Day.getDay()],//周的中文
day:Day.getDate(),//几号
prevMoth: Day.getTime() < _thisMothn_day.getTime() ?true:false,//是否是上月。
nowMonth: Day.getTime()>= _thisMothn_day.getTime() && Day.getTime() <= _thisMothn_lastDay.getTime() ?true:false,//是否当月
nowDay:Day.getTime() == _thisDayDate.getTime() ?true:false,//是否是当天。
nowYear:0,//是否当年
nextMoth: Day.getTime() > _thisMothn_lastDay.getTime()?true:false,//是否下月
beginEnd: Day.getTime() >= this.start_time.getTime() && Day.getTime() <= this.end_time.getTime()?true:false,//是否在开始和结束区间范围内。
nongli:this.nongli(Day.getFullYear(),Day.getMonth()+1,Day.getDate())
})
}
return dateArray;
}
// 返回当前选中月的日历数组。如果你不设置value和数据getNowData和getData相等。
/**
* 返回当前月数据。
* @return {
beginEnd: false,//是否在规定范围时间内
day: 16,//日
month: 7,//月
nextMoth: false,//是否下月
nowDay: false,//是否当天
nowMonth: true,//是否当月
prevMoth: false,/是否下月
week: 5,//周1-7
week_text: "周五",//同上
year: 2021,//年
nongli:{ //农历
day: "初七",//日
jieqi: "",//节气
month: "六月",//月
shengxiao: "牛",//生肖
year: "辛丑" //农历年
}
}
*/
getData(){
// 当前时间 。
let week = [7,1,2,3,4,5,6]
let text_week = ['周日','周一','周二','周三','周四','周五','周六']
let _thisdateStr = this.value.toLocaleString();
let _thisMothn = this.value.getMonth();//当前月
let _thisDay = this.value.getDate();//当前日
let _thisYear = this.value.getFullYear();//当前年
let _thisWeek = this.value.getDay();//当前周
let _thisMothn_day = new Date(_thisYear,_thisMothn,1) ; ///当月第一天数据。
let _thisDayDate = new Date(_thisYear,_thisMothn,_thisDay);
let months = [31,this.monthDay(_thisYear,_thisMothn),31,30,31,30,31,31,30,31,30,31];
let _thisMothn_lastDay = new Date(_thisYear,_thisMothn,months[_thisMothn]) ; ///当月最后数据。
let dateArray = [];//当前日历表数据。
let llineDate = [];//行数据。
let j=1;
let ishs = true //如果第一排没有本月数据需要切换模式。把本月放第一位。
for(let i=1 ;i <8;i++){
let tdy = new Date(_thisYear, _thisMothn, i - 6 - _thisMothn_day.getDay()); //当前循环日期。
if(i==7){
let tf = tdy.getTime()>= _thisMothn_day.getTime() && tdy.getTime() <= _thisMothn_lastDay.getTime() ?true:false;
if(!tf){
ishs = false;
break;
}
}
}
for(let i=1 ;i <43;i++){
var Day = null;
if(!ishs){
Day = new Date(_thisYear, _thisMothn, i + 1 - _thisMothn_day.getDay()); //当前循环日期。
}else{
Day = new Date(_thisYear, _thisMothn, i - 6 - _thisMothn_day.getDay()); //当前循环日期。
}
let dstr = Day.getFullYear() + "-" + (Day.getMonth()+1) + "-" + Day.getDate();
let TxtIndex = this.txtdateArray.findIndex(item=>{
return item.date == dstr
})
dateArray.push({
year:Day.getFullYear(),//年
month:Day.getMonth()+1,//月1-12
week:week[Day.getDay()],//周的数字1~7
week_text:text_week[Day.getDay()],//周的中文
day:Day.getDate(),//几号
prevMoth: Day.getTime() < _thisMothn_day.getTime() ?true:false,//是否是上月。
nowMonth: Day.getTime()>= _thisMothn_day.getTime() && Day.getTime() <= _thisMothn_lastDay.getTime() ?true:false,//是否当月
nowDay:Day.getTime() == _thisDayDate.getTime() ?true:false,//是否是当天。
nextMoth: Day.getTime() > _thisMothn_lastDay.getTime()?true:false,//是否下月
beginEnd: Day.getTime() >= this.start_time.getTime() && Day.getTime() <= this.end_time.getTime()?true:false,//是否在开始和结束区间范围内。
nongli:this.nongli(Day.getFullYear(),Day.getMonth()+1,Day.getDate()),
text:TxtIndex>-1? this.txtdateArray[TxtIndex]['text']:""
})
}
return dateArray;
}
}
export default calendar;
@@ -0,0 +1,41 @@
function choujiang(prizes) {
var prizeList = [] //按照权重分解后的奖品数组
prizes.map(function(item){
prizeList.push({
...item
})
for(var i=0; i< item.gailv; i++) {
prizeList.push({
...item
})
}
});
prizeList = reset(prizeList);
// 范围随机数
function randomFrom(lowerValue, upperValue) {
return Math.floor(Math.random() * (upperValue - lowerValue + 1) + lowerValue);
};
// 随机打乱数组
function reset(arr) {
var eachArr = arr.concat([])
var lastArr = []
function deepEach(deepArr) {
if (deepArr.length) {
var randomIndex = randomFrom(0, eachArr.length - 1)
lastArr.push(eachArr[randomIndex])
eachArr.splice(randomIndex, 1)
deepEach(eachArr)
}
}
deepEach(eachArr)
return lastArr
}
this.getResult = function() {
var random = randomFrom(0, prizeList.length - 1);
return prizeList[random]
}
}
export default choujiang;
File diff suppressed because one or more lines are too long
+272
View File
@@ -0,0 +1,272 @@
function hsvtoRgb(h, s, v) {
var r;
var g;
var b;
var i;
var f;
var p;
var q;
var t;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
default:
break;
}
return [r,
g,
b
];
}
function rgbtoHsv(r, g, b) {
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var d = max - min;
var h;
var s = (max === 0 ? 0 : d / max);
var v = max / 255;
switch (max) {
case min:
h = 0;
break;
case r:
h = (g - b) + d * (g < b ? 6 : 0);
h /= 6 * d;
break;
case g:
h = (b - r) + d * 2;
h /= 6 * d;
break;
case b:
h = (r - g) + d * 4;
h /= 6 * d;
break;
default:
break;
}
return [
h,
s,
v,
];
}
function addSaturationToRgb(color, offset) {
var hsv = rgbtoHsv(color[0] * 255, color[1] * 255, color[2] * 255);
hsv[1] += offset;
if (hsv[1] > 1) {
hsv[1] = 1;
} else if (hsv[1] <= 0) {
hsv[1] = 0;
}
return hsvtoRgb(hsv[0], hsv[1], hsv[2]);
}
function addBrightnessToRgb(color, offset) {
var hsv = rgbtoHsv(color[0] * 255, color[1] * 255, color[2] * 255);
hsv[2] += offset;
if (hsv[2] > 1) {
hsv[2] = 1;
} else if (hsv[2] < 0) {
hsv[2] = 0;
}
return hsvtoRgb(hsv[0], hsv[1], hsv[2]);
}
function addHueToRgb(color, offset) {
var hsv = rgbtoHsv(color[0] * 255, color[1] * 255, color[2] * 255);
hsv[0] += offset / 360;
if (hsv[0] > 1) {
hsv[0] -= 1;
} else if (hsv[0] < 0) {
hsv[0] += 1;
}
return hsvtoRgb(hsv[0], hsv[1], hsv[2]);
}
function rgbToHex() {
var colorMap = [];
var i;
var hex;
for (i = 0; i < 256; i += 1) {
hex = i.toString(16);
colorMap[i] = hex.length === 1 ? '0' + hex : hex;
}
return function(r, g, b) {
if (r < 0) {
r = 0;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
return '#' + colorMap[r] + colorMap[g] + colorMap[b];
};
}
function hexToRgb(sColor, str = true) {
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
sColor = sColor.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 = [];
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
}
if(!str) {
return sColorChange;
} else {
return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`;
}
} else if (/^(rgb|RGB)/.test(sColor)) {
let arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",")
return arr.map(val => Number(val));
} else {
return sColor;
}
};
function rgbToHsl(r,g,b){
r=r/255;
g=g/255;
b=b/255;
var min=Math.min(r,g,b);
var max=Math.max(r,g,b);
var l=(min+max)/2;
var difference = max-min;
var h,s,l;
if(max==min){
h=0;
s=0;
}else{
s=l>0.5?difference/(2.0-max-min):difference/(max+min);
switch(max){
case r: h=(g-b)/difference+(g < b ? 6 : 0);break;
case g: h=2.0+(b-r)/difference;break;
case b: h=4.0+(r-g)/difference;break;
}
h=Math.round(h*60);
}
s=Math.round(s*100);//转换成百分比的形式
l=Math.round(l*100);
return [h,s,l];
}
function hslToRgb(h,s,l){
var h=h/360;
var s=s/100;
var l=l/100;
var rgb=[];
if(s==0){
rgb=[Math.round(l*255),Math.round(l*255),Math.round(l*255)];
}else{
var q=l>=0.5?(l+s-l*s):(l*(1+s));
var p=2*l-q;
var tr=rgb[0]=h+1/3;
var tg=rgb[1]=h;
var tb=rgb[2]=h-1/3;
for(var i=0; i<rgb.length;i++){
var tc=rgb[i];
console.log(tc);
if(tc<0){
tc=tc+1;
}else if(tc>1){
tc=tc-1;
}
switch(true){
case (tc<(1/6)):
tc=p+(q-p)*6*tc;
break;
case ((1/6)<=tc && tc<0.5):
tc=q;
break;
case (0.5<=tc && tc<(2/3)):
tc=p+(q-p)*(4-6*tc);
break;
default:
tc=p;
break;
}
rgb[i]=Math.round(tc*255);
}
}
return rgb;
}
/**
* 求两个颜色之间的渐变值
* @param {string} startColor 开始的颜色
* @param {string} endColor 结束的颜色
* @param {number} step 颜色等分的份额
* */
function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
let startRGB = hexToRgb(startColor, false); //转换为rgb数组模式
let startR = startRGB[0];
let startG = startRGB[1];
let startB = startRGB[2];
let endRGB = hexToRgb(endColor, false);
let endR = endRGB[0];
let endG = endRGB[1];
let endB = endRGB[2];
let sR = (endR - startR) / step; //总差值
let sG = (endG - startG) / step;
let sB = (endB - startB) / step;
let colorArr = [];
for (let i = 0; i < step; i++) {
//计算每一步的hex值
let hex = rgbToHex('rgb(' + Math.round((sR * i + startR)) + ',' + Math.round((sG * i + startG)) + ',' + Math.round((sB *
i + startB)) + ')');
colorArr.push(hex);
}
return colorArr;
}
@@ -0,0 +1,310 @@
const { sqrt, pow, ceil, abs } = Math
// Initialize the number of points per curve
const defaultSegmentPointsNum = 50
/**
* @example data structure of bezierCurve
* bezierCurve = [
* // Starting point of the curve
* [10, 10],
* // BezierCurve segment data (controlPoint1, controlPoint2, endPoint)
* [
* [20, 20], [40, 20], [50, 10]
* ],
* ...
* ]
*/
/**
* @description Abstract the curve as a polyline consisting of N points
* @param {Array} bezierCurve bezierCurve data
* @param {Number} precision calculation accuracy. Recommended for 1-20. Default = 5
* @return {Object} Calculation results and related data
* @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
* @return {Number} Option.cycles Number of iterations
* @return {Number} Option.rounds The number of recursions for the last iteration
*/
function abstractBezierCurveToPolyline (bezierCurve, precision = 5) {
const segmentsNum = bezierCurve.length - 1
const startPoint = bezierCurve[0]
const endPoint = bezierCurve[segmentsNum][2]
const segments = bezierCurve.slice(1)
const getSegmentTPointFuns = segments.map((seg, i) => {
let beginPoint = (i === 0) ? startPoint : segments[i - 1][2]
return createGetBezierCurveTPointFun(beginPoint, ...seg)
})
// Initialize the curve to a polyline
let segmentPointsNum = new Array(segmentsNum).fill(defaultSegmentPointsNum)
let segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum)
// Calculate uniformly distributed points by iteratively
const result = calcUniformPointsByIteration(segmentPoints, getSegmentTPointFuns, segments, precision)
result.segmentPoints.push(endPoint)
return result
}
/**
* @description Generate a method for obtaining corresponding point by t according to curve data
* @param {Array} beginPoint BezierCurve begin point. [x, y]
* @param {Array} controlPoint1 BezierCurve controlPoint1. [x, y]
* @param {Array} controlPoint2 BezierCurve controlPoint2. [x, y]
* @param {Array} endPoint BezierCurve end point. [x, y]
* @return {Function} Expected function
*/
function createGetBezierCurveTPointFun (beginPoint, controlPoint1, controlPoint2, endPoint) {
return function (t) {
const tSubed1 = 1 - t
const tSubed1Pow3 = pow(tSubed1, 3)
const tSubed1Pow2 = pow(tSubed1, 2)
const tPow3 = pow(t, 3)
const tPow2 = pow(t, 2)
return [
beginPoint[0] * tSubed1Pow3 + 3 * controlPoint1[0] * t * tSubed1Pow2 + 3 * controlPoint2[0] * tPow2 * tSubed1 + endPoint[0] * tPow3,
beginPoint[1] * tSubed1Pow3 + 3 * controlPoint1[1] * t * tSubed1Pow2 + 3 * controlPoint2[1] * tPow2 * tSubed1 + endPoint[1] * tPow3
]
}
}
/**
* @description Get the distance between two points
* @param {Array} point1 BezierCurve begin point. [x, y]
* @param {Array} point2 BezierCurve controlPoint1. [x, y]
* @return {Number} Expected distance
*/
function getTwoPointDistance ([ax, ay], [bx, by]) {
return sqrt(pow(ax - bx, 2) + pow(ay - by, 2))
}
/**
* @description Get the sum of the array of numbers
* @param {Array} nums An array of numbers
* @return {Number} Expected sum
*/
function getNumsSum (nums) {
return nums.reduce((sum, num) => sum + num, 0)
}
/**
* @description Get the distance of multiple sets of points
* @param {Array} segmentPoints Multiple sets of point data
* @return {Array} Distance of multiple sets of point data
*/
function getSegmentPointsDistance (segmentPoints) {
return segmentPoints.map((points, i) => {
return new Array(points.length - 1)
.fill(0)
.map((temp, j) => getTwoPointDistance(points[j], points[j + 1]))
})
}
/**
* @description Get the distance of multiple sets of points
* @param {Array} segmentPoints Multiple sets of point data
* @return {Array} Distance of multiple sets of point data
*/
function getSegmentPointsByNum (getSegmentTPointFuns, segmentPointsNum) {
return getSegmentTPointFuns.map((getSegmentTPointFun, i) => {
const tGap = 1 / segmentPointsNum[i]
return new Array(segmentPointsNum[i])
.fill('')
.map((foo, j) => getSegmentTPointFun(j * tGap))
})
}
/**
* @description Get the sum of deviations between line segment and the average length
* @param {Array} segmentPointsDistance Segment length of polyline
* @param {Number} avgLength Average length of the line segment
* @return {Number} Deviations
*/
function getAllDeviations (segmentPointsDistance, avgLength) {
return segmentPointsDistance
.map(seg => seg.map(s => abs(s - avgLength)))
.map(seg => getNumsSum(seg))
.reduce((total, v) => total + v, 0)
}
/**
* @description Calculate uniformly distributed points by iteratively
* @param {Array} segmentPoints Multiple setd of points that make up a polyline
* @param {Array} getSegmentTPointFuns Functions of get a point on the curve with t
* @param {Array} segments BezierCurve data
* @param {Number} precision Calculation accuracy
* @return {Object} Calculation results and related data
* @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
* @return {Number} Option.cycles Number of iterations
* @return {Number} Option.rounds The number of recursions for the last iteration
*/
function calcUniformPointsByIteration (segmentPoints, getSegmentTPointFuns, segments, precision) {
// The number of loops for the current iteration
let rounds = 4
// Number of iterations
let cycles = 1
do {
// Recalculate the number of points per curve based on the last iteration data
let totalPointsNum = segmentPoints.reduce((total, seg) => total + seg.length, 0)
// Add last points of segment to calc exact segment length
segmentPoints.forEach((seg, i) => seg.push(segments[i][2]))
let segmentPointsDistance = getSegmentPointsDistance(segmentPoints)
let lineSegmentNum = segmentPointsDistance.reduce((total, seg) => total + seg.length, 0)
let segmentlength = segmentPointsDistance.map(seg => getNumsSum(seg))
let totalLength = getNumsSum(segmentlength)
let avgLength = totalLength / lineSegmentNum
// Check if precision is reached
let allDeviations = getAllDeviations(segmentPointsDistance, avgLength)
if (allDeviations <= precision) break
totalPointsNum = ceil(avgLength / precision * totalPointsNum * 1.1)
const segmentPointsNum = segmentlength.map(length => ceil(length / totalLength * totalPointsNum))
// Calculate the points after redistribution
segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum)
totalPointsNum = segmentPoints.reduce((total, seg) => total + seg.length, 0)
let segmentPointsForLength = JSON.parse(JSON.stringify(segmentPoints))
segmentPointsForLength.forEach((seg, i) => seg.push(segments[i][2]))
segmentPointsDistance = getSegmentPointsDistance(segmentPointsForLength)
lineSegmentNum = segmentPointsDistance.reduce((total, seg) => total + seg.length, 0)
segmentlength = segmentPointsDistance.map(seg => getNumsSum(seg))
totalLength = getNumsSum(segmentlength)
avgLength = totalLength / lineSegmentNum
const stepSize = 1 / totalPointsNum / 10
// Recursively for each segment of the polyline
getSegmentTPointFuns.forEach((getSegmentTPointFun, i) => {
const currentSegmentPointsNum = segmentPointsNum[i]
const t = new Array(currentSegmentPointsNum).fill('').map((foo, j) => j / segmentPointsNum[i])
// Repeated recursive offset
for (let r = 0; r < rounds; r++) {
let distance = getSegmentPointsDistance([segmentPoints[i]])[0]
const deviations = distance.map(d => d - avgLength)
let offset = 0
for (let j = 0; j < currentSegmentPointsNum; j++) {
if (j === 0) return
offset += deviations[j - 1]
t[j] -= stepSize * offset
if (t[j] > 1) t[j] = 1
if (t[j] < 0) t[j] = 0
segmentPoints[i][j] = getSegmentTPointFun(t[j])
}
}
})
rounds *= 4
cycles++
} while (rounds <= 1025)
segmentPoints = segmentPoints.reduce((all, seg) => all.concat(seg), [])
return {
segmentPoints,
cycles,
rounds
}
}
/**
* @description Get the polyline corresponding to the Bezier curve
* @param {Array} bezierCurve BezierCurve data
* @param {Number} precision Calculation accuracy. Recommended for 1-20. Default = 5
* @return {Array|Boolean} Point data that constitutes a polyline after calculation (Invalid input will return false)
*/
export function bezierCurveToPolyline (bezierCurve, precision = 5) {
if (!bezierCurve) {
console.error('bezierCurveToPolyline: Missing parameters!')
return false
}
if (!(bezierCurve instanceof Array)) {
console.error('bezierCurveToPolyline: Parameter bezierCurve must be an array!')
return false
}
if (typeof precision !== 'number') {
console.error('bezierCurveToPolyline: Parameter precision must be a number!')
return false
}
const { segmentPoints } = abstractBezierCurveToPolyline(bezierCurve, precision)
return segmentPoints
}
/**
* @description Get the bezier curve length
* @param {Array} bezierCurve bezierCurve data
* @param {Number} precision calculation accuracy. Recommended for 5-10. Default = 5
* @return {Number|Boolean} BezierCurve length (Invalid input will return false)
*/
export function getBezierCurveLength (bezierCurve, precision = 5) {
if (!bezierCurve) {
console.error('getBezierCurveLength: Missing parameters!')
return false
}
if (!(bezierCurve instanceof Array)) {
console.error('getBezierCurveLength: Parameter bezierCurve must be an array!')
return false
}
if (typeof precision !== 'number') {
console.error('getBezierCurveLength: Parameter precision must be a number!')
return false
}
const { segmentPoints } = abstractBezierCurveToPolyline(bezierCurve, precision)
// Calculate the total length of the points that make up the polyline
const pointsDistance = getSegmentPointsDistance([segmentPoints])[0]
const length = getNumsSum(pointsDistance)
return length
}
export default bezierCurveToPolyline
@@ -0,0 +1,465 @@
/**
* 点方法。
* 此库来自konva
* 移植:https://jx2d.cn
* tmzdy
*/
export function parsePathData(data) {
if (!data) {
return [];
}
var cs = data;
var cc = [
'm',
'M',
'l',
'L',
'v',
'V',
'h',
'H',
'z',
'Z',
'c',
'C',
'q',
'Q',
't',
'T',
's',
'S',
'a',
'A',
];
cs = cs.replace(new RegExp(' ', 'g'), ',');
for (var n = 0; n < cc.length; n++) {
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
}
var arr = cs.split('|');
var ca = [];
var coords = [];
var cpx = 0;
var cpy = 0;
var re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
var match;
for (n = 1; n < arr.length; n++) {
var str = arr[n];
var c = str.charAt(0);
str = str.slice(1);
coords.length = 0;
while ((match = re.exec(str))) {
coords.push(match[0]);
}
var p = [];
for (var j = 0, jlen = coords.length; j < jlen; j++) {
if (coords[j] === '00') {
p.push(0, 0);
continue;
}
var parsed = parseFloat(coords[j]);
if (!isNaN(parsed)) {
p.push(parsed);
} else {
p.push(0);
}
}
while (p.length > 0) {
if (isNaN(p[0])) {
break;
}
var cmd = null;
var points = [];
var startX = cpx,
startY = cpy;
var prevCmd, ctlPtx, ctlPty;
var rx, ry, psi, fa, fs, x1, y1;
switch (c) {
case 'l':
cpx += p.shift();
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'L':
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'm':
var dx = p.shift();
var dy = p.shift();
cpx += dx;
cpy += dy;
cmd = 'M';
if (ca.length > 2 && ca[ca.length - 1].command === 'z') {
for (var idx = ca.length - 2; idx >= 0; idx--) {
if (ca[idx].command === 'M') {
cpx = ca[idx].points[0] + dx;
cpy = ca[idx].points[1] + dy;
break;
}
}
}
points.push(cpx, cpy);
c = 'l';
break;
case 'M':
cpx = p.shift();
cpy = p.shift();
cmd = 'M';
points.push(cpx, cpy);
c = 'L';
break;
case 'h':
cpx += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'H':
cpx = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'v':
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'V':
cpy = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'C':
points.push(p.shift(), p.shift(), p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'c':
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'S':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 's':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'Q':
points.push(p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'q':
points.push(cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(cpx, cpy);
break;
case 'T':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx = p.shift();
cpy = p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 't':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 'A':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx = p.shift();
cpy = p.shift();
cmd = 'A';
points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
case 'a':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx += p.shift();
cpy += p.shift();
cmd = 'A';
points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
}
ca.push({
command: cmd || c,
points: points,
start: {
x: startX,
y: startY,
},
pathLength: calcLength(startX, startY, cmd || c, points),
});
}
if (c === 'z' || c === 'Z') {
ca.push({
command: 'z',
points: [],
start: undefined,
pathLength: 0,
});
}
}
return ca;
}
export function calcLength(x, y, cmd, points) {
var len, p1, p2, t;
switch (cmd) {
case 'L':
return getLineLength(x, y, points[0], points[1]);
case 'C':
len = 0.0;
p1 = getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
for (t = 0.01; t <= 1; t += 0.01) {
p2 = getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[
5]);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'Q':
len = 0.0;
p1 = getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
for (t = 0.01; t <= 1; t += 0.01) {
p2 = getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'A':
len = 0.0;
var start = points[4];
var dTheta = points[5];
var end = points[4] + dTheta;
var inc = Math.PI / 180.0;
if (Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
p1 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
if (dTheta < 0) {
for (t = start - inc; t > end; t -= inc) {
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
} else {
for (t = start + inc; t < end; t += inc) {
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
return len;
}
return 0;
}
export function convertEndpointToCenterParameterization(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
var psi = psiDeg * (Math.PI / 180.0);
var xp = (Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
var yp = (-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
(Math.cos(psi) * (y1 - y2)) / 2.0;
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
if (lambda > 1) {
rx *= Math.sqrt(lambda);
ry *= Math.sqrt(lambda);
}
var f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) /
(rx * rx * (yp * yp) + ry * ry * (xp * xp)));
if (fa === fs) {
f *= -1;
}
if (isNaN(f)) {
f = 0;
}
var cxp = (f * rx * yp) / ry;
var cyp = (f * -ry * xp) / rx;
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
var vMag = function(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
};
var vRatio = function(u, v) {
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
};
var vAngle = function(u, v) {
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
};
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
var dTheta = vAngle(u, v);
if (vRatio(u, v) <= -1) {
dTheta = Math.PI;
}
if (vRatio(u, v) >= 1) {
dTheta = 0;
}
if (fs === 0 && dTheta > 0) {
dTheta = dTheta - 2 * Math.PI;
}
if (fs === 1 && dTheta < 0) {
dTheta = dTheta + 2 * Math.PI;
}
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
}
export function getSelfRect() {
var points = [];
this.dataArray.forEach(function(data) {
if (data.command === 'A') {
var start = data.points[4];
var dTheta = data.points[5];
var end = data.points[4] + dTheta;
var inc = Math.PI / 180.0;
if (Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
if (dTheta < 0) {
for (let t = start - inc; t > end; t -= inc) {
const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
data.points[3], t, 0);
points.push(point.x, point.y);
}
} else {
for (let t = start + inc; t < end; t += inc) {
const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
data.points[3], t, 0);
points.push(point.x, point.y);
}
}
} else if (data.command === 'C') {
for (let t = 0.0; t <= 1; t += 0.01) {
const point = Path.getPointOnCubicBezier(t, data.start.x, data.start.y, data.points[0], data
.points[1], data.points[2], data.points[3], data.points[4], data.points[5]);
points.push(point.x, point.y);
}
} else {
points = points.concat(data.points);
}
});
var minX = points[0];
var maxX = points[0];
var minY = points[1];
var maxY = points[1];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2];
y = points[i * 2 + 1];
if (!isNaN(x)) {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
}
if (!isNaN(y)) {
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
return {
x: Math.round(minX),
y: Math.round(minY),
width: Math.round(maxX - minX),
height: Math.round(maxY - minY),
};
}
export function expandPoints(p, tension) {
var len = p.length, allPoints = [], n, cp;
for (n = 2; n < len - 2; n += 2) {
cp = getControlPoints(p[n - 2], p[n - 1], p[n], p[n + 1], p[n + 2], p[n + 3], tension);
if (isNaN(cp[0])) {
continue;
}
allPoints.push(cp[0]);
allPoints.push(cp[1]);
allPoints.push(p[n]);
allPoints.push(p[n + 1]);
allPoints.push(cp[2]);
allPoints.push(cp[3]);
}
return allPoints;
}
export function getControlPoints(x0, y0, x1, y1, x2, y2, t) {
var d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)), d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)), fa = (t * d01) / (d01 + d12), fb = (t * d12) / (d01 + d12), p1x = x1 - fa * (x2 - x0), p1y = y1 - fa * (y2 - y0), p2x = x1 + fb * (x2 - x0), p2y = y1 + fb * (y2 - y0);
return [p1x, p1y, p2x, p2y];
}
export function getTensionPointsClosed(points,tension) {
var p = points ,len = p.length, firstControlPoints = getControlPoints(p[len - 2], p[len - 1], p[0], p[1], p[2], p[3], tension), lastControlPoints = getControlPoints(p[len - 4], p[len - 3], p[len - 2], p[len - 1], p[0], p[1], tension), middle = expandPoints(p, tension), tp = [firstControlPoints[2], firstControlPoints[3]]
.concat(middle)
.concat([
lastControlPoints[0],
lastControlPoints[1],
p[len - 2],
p[len - 1],
lastControlPoints[2],
lastControlPoints[3],
firstControlPoints[0],
firstControlPoints[1],
p[0],
p[1],
]);
return tp;
}
@@ -0,0 +1,111 @@
/**
* @description Abstract the polyline formed by N points into a set of bezier curve
* @param {Array} polyline A set of points that make up a polyline
* @param {Boolean} close Closed curve
* @param {Number} offsetA Smoothness
* @param {Number} offsetB Smoothness
* @return {Array|Boolean} A set of bezier curve (Invalid input will return false)
*/
function polylineToBezierCurve (polyline, close = false, offsetA = 0.25, offsetB = 0.25) {
if (!(polyline instanceof Array)) {
console.error('polylineToBezierCurve: Parameter polyline must be an array!')
return false
}
if (polyline.length <= 2) {
console.error('polylineToBezierCurve: Converting to a curve requires at least 3 points!')
return false
}
const startPoint = polyline[0]
const bezierCurveLineNum = polyline.length - 1
const bezierCurvePoints = new Array(bezierCurveLineNum).fill(0).map((foo, i) =>
[...getBezierCurveLineControlPoints(polyline, i, close, offsetA, offsetB), polyline[i + 1]])
if (close) closeBezierCurve(bezierCurvePoints, startPoint)
bezierCurvePoints.unshift(polyline[0])
return bezierCurvePoints
}
/**
* @description Get the control points of the Bezier curve
* @param {Array} polyline A set of points that make up a polyline
* @param {Number} index The index of which get controls points's point in polyline
* @param {Boolean} close Closed curve
* @param {Number} offsetA Smoothness
* @param {Number} offsetB Smoothness
* @return {Array} Control points
*/
function getBezierCurveLineControlPoints (polyline, index, close = false, offsetA = 0.25, offsetB = 0.25) {
const pointNum = polyline.length
if (pointNum < 3 || index >= pointNum) return
let beforePointIndex = index - 1
if (beforePointIndex < 0) beforePointIndex = (close ? pointNum + beforePointIndex : 0)
let afterPointIndex = index + 1
if (afterPointIndex >= pointNum) afterPointIndex = (close ? afterPointIndex - pointNum : pointNum - 1)
let afterNextPointIndex = index + 2
if (afterNextPointIndex >= pointNum) afterNextPointIndex = (close ? afterNextPointIndex - pointNum : pointNum - 1)
const pointBefore = polyline[beforePointIndex]
const pointMiddle = polyline[index]
const pointAfter = polyline[afterPointIndex]
const pointAfterNext = polyline[afterNextPointIndex]
return [
[
pointMiddle[0] + offsetA * (pointAfter[0] - pointBefore[0]),
pointMiddle[1] + offsetA * (pointAfter[1] - pointBefore[1])
],
[
pointAfter[0] - offsetB * (pointAfterNext[0] - pointMiddle[0]),
pointAfter[1] - offsetB * (pointAfterNext[1] - pointMiddle[1])
]
]
}
/**
* @description Get the last curve of the closure
* @param {Array} bezierCurve A set of sub-curve
* @param {Array} startPoint Start point
* @return {Array} The last curve for closure
*/
function closeBezierCurve (bezierCurve, startPoint) {
const firstSubCurve = bezierCurve[0]
const lastSubCurve = bezierCurve.slice(-1)[0]
bezierCurve.push([
getSymmetryPoint(lastSubCurve[1], lastSubCurve[2]),
getSymmetryPoint(firstSubCurve[0], startPoint),
startPoint
])
return bezierCurve
}
/**
* @description Get the symmetry point
* @param {Array} point Symmetric point
* @param {Array} centerPoint Symmetric center
* @return {Array} Symmetric point
*/
function getSymmetryPoint (point, centerPoint) {
const [px, py] = point
const [cx, cy] = centerPoint
const minusX = cx - px
const minusY = cy - py
return [cx + minusX, cy + minusY]
}
export default polylineToBezierCurve
@@ -0,0 +1,11 @@
import { bezierCurveToPolyline, getBezierCurveLength } from './core/bezierCurveToPolyline'
import polylineToBezierCurve from './core/polylineToBezierCurve'
export { bezierCurveToPolyline, getBezierCurveLength } from './core/bezierCurveToPolyline'
export { default as polylineToBezierCurve } from './core/polylineToBezierCurve'
export default {
bezierCurveToPolyline,
getBezierCurveLength,
polylineToBezierCurve
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,377 @@
import Style from './style.class'
import transition from '../transition'
import {
deepClone,
getRotatePointPos,
getScalePointPos,
getTranslatePointPos,
checkPointIsInRect
} from '../plugin/util'
/**
* @description Class Graph
* @param {Object} graph Graph default configuration
* @param {Object} config Graph config
* @return {Graph} Instance of Graph
*/
export default class Graph {
constructor (graph, config) {
config = deepClone(config, true)
config = {animationFrame:24,...config};
const defaultConfig = {
/**
* @description Weather to render graph
* @type {Boolean}
* @default visible = true
*/
visible: true,
/**
* @description Whether to enable drag
* @type {Boolean}
* @default drag = false
*/
drag: false,
/**
* @description Whether to enable hover
* @type {Boolean}
* @default hover = false
*/
hover: false,
/**
* @description Graph rendering index
* Give priority to index high graph in rendering
* @type {Number}
* @example index = 1
*/
index: 1,
/**
* @description Animation delay time(ms)
* @type {Number}
* @default animationDelay = 0
*/
animationDelay: 0,
/**
* @description Number of animation frames
* @type {Number}
* @default animationFrame = 30
*/
animationFrame: 30,
/**
* @description Animation dynamic curve (Supported by transition)
* @type {String}
* @default animationCurve = 'linear'
* @link https://github.com/jiaming743/Transition
*/
animationCurve: 'linear',
/**
* @description Weather to pause graph animation
* @type {Boolean}
* @default animationPause = false
*/
animationPause: false,
/**
* @description Rectangular hover detection zone
* Use this method for hover detection first
* @type {Null|Array}
* @default hoverRect = null
* @example hoverRect = [0, 0, 100, 100] // [Rect start x, y, Rect width, height]
*/
hoverRect: null,
/**
* @description Mouse enter event handler
* @type {Function|Null}
* @default mouseEnter = null
*/
mouseEnter: null,
/**
* @description Mouse outer event handler
* @type {Function|Null}
* @default mouseOuter = null
*/
mouseOuter: null,
/**
* @description Mouse click event handler
* @type {Function|Null}
* @default click = null
*/
click: null
}
const configAbleNot = {
status: 'static',
animationRoot: [],
animationKeys: [],
animationFrameState: [],
cache: {}
}
if (!config.shape) config.shape = {}
if (!config.style) config.style = {}
const shape = Object.assign({}, graph.shape, config.shape)
Object.assign(defaultConfig, config, configAbleNot)
Object.assign(this, graph, defaultConfig)
this.shape = shape
this.style = new Style(config.style)
this.addedProcessor()
}
}
/**
* @description Processor of added
* @return {Undefined} Void
*/
Graph.prototype.addedProcessor = function () {
if (typeof this.setGraphCenter === 'function') this.setGraphCenter(null, this)
// The life cycle 'added"
if (typeof this.added === 'function') this.added(this)
}
/**
* @description Processor of draw
* @param {CRender} render Instance of CRender
* @param {Graph} graph Instance of Graph
* @return {Undefined} Void
*/
Graph.prototype.drawProcessor = function (render, graph) {
const { ctx } = render
const { shape } = graph
graph.style.initStyle(ctx,shape)
if (typeof this.beforeDraw === 'function') this.beforeDraw(this, render)
graph.draw(render, graph)
// ctx.draw(true)
if (typeof this.drawed === 'function') this.drawed(this, render)
graph.style.restoreTransform(ctx)
}
/**
* @description Processor of hover check
* @param {Array} position Mouse Position
* @param {Graph} graph Instance of Graph
* @return {Boolean} Result of hover check
*/
Graph.prototype.hoverCheckProcessor = function (position, { hoverRect, style, hoverCheck }) {
const { graphCenter, rotate, scale, translate } = style
if (graphCenter) {
if (rotate) position = getRotatePointPos(-rotate, position, graphCenter)
if (scale) position = getScalePointPos(scale.map(s => 1 / s), position, graphCenter)
if (translate) position = getTranslatePointPos(translate.map(v => v * -1), position)
}
if (hoverRect) return checkPointIsInRect(position, ...hoverRect)
return hoverCheck(position, this)
}
/**
* @description Processor of move
* @param {Event} e Mouse movement event
* @return {Undefined} Void
*/
Graph.prototype.moveProcessor = function (e) {
this.move(e, this)
if (typeof this.beforeMove === 'function') this.beforeMove(e, this)
if (typeof this.setGraphCenter === 'function') this.setGraphCenter(e, this)
if (typeof this.moved === 'function') this.moved(e, this)
}
/**
* @description Update graph state
* @param {String} attrName Updated attribute name
* @param {Any} change Updated value
* @return {Undefined} Void
*/
Graph.prototype.attr = function (attrName, change = undefined) {
if (!attrName || change === undefined) return false
const isObject = typeof this[attrName] === 'object'
if (isObject) change = deepClone(change, true)
const { render } = this
if (attrName === 'style') {
this.style.update(change)
} else if (isObject) {
Object.assign(this[attrName], change)
} else {
this[attrName] = change
}
if (attrName === 'index') render.sortGraphsByIndex()
render.drawAllGraph()
}
/**
* @description Update graphics state (with animation)
* Only shape and style attributes are supported
* @param {String} attrName Updated attribute name
* @param {Any} change Updated value
* @param {Boolean} wait Whether to store the animation waiting
* for the next animation request
* @return {Promise} Animation Promise
*/
Graph.prototype.animation = async function (attrName, change, wait = false) {
if (attrName !== 'shape' && attrName !== 'style') {
console.error('Only supported shape and style animation!')
return
}
change = deepClone(change, true)
if (attrName === 'style') this.style.colorProcessor(change)
const changeRoot = this[attrName]
const changeKeys = Object.keys(change)
const beforeState = {}
changeKeys.forEach(key => (beforeState[key] = changeRoot[key]))
const { animationFrame, animationCurve, animationDelay } = this
const animationFrameState = transition(animationCurve, beforeState, change, animationFrame, true)
this.animationRoot.push(changeRoot)
this.animationKeys.push(changeKeys)
this.animationFrameState.push(animationFrameState)
if (wait) return
if (animationDelay > 0) await delay(animationDelay)
const { render } = this
return new Promise(async resolve => {
await render.launchAnimation()
resolve()
})
}
/**
* @description Extract the next frame of data from the animation queue
* and update the graph state
* @return {Undefined} Void
*/
Graph.prototype.turnNextAnimationFrame = function (timeStamp) {
const { animationDelay, animationRoot, animationKeys, animationFrameState, animationPause } = this
if (animationPause) return
if (Date.now() - timeStamp < animationDelay) return
animationRoot.forEach((root, i) => {
animationKeys[i].forEach(key => {
root[key] = animationFrameState[i][0][key]
})
})
animationFrameState.forEach((stateItem, i) => {
stateItem.shift()
const noFrame = stateItem.length === 0
if (noFrame) animationRoot[i] = null
if (noFrame) animationKeys[i] = null
})
this.animationFrameState = animationFrameState.filter(state => state.length)
this.animationRoot = animationRoot.filter(root => root)
this.animationKeys = animationKeys.filter(keys => keys)
}
/**
* @description Skip to the last frame of animation
* @return {Undefined} Void
*/
Graph.prototype.animationEnd = function () {
const { animationFrameState, animationKeys, animationRoot, render } = this
animationRoot.forEach((root, i) => {
const currentKeys = animationKeys[i]
const lastState = animationFrameState[i].pop()
currentKeys.forEach(key => (root[key] = lastState[key]))
})
this.animationFrameState = []
this.animationKeys = []
this.animationRoot = []
return render.drawAllGraph()
}
/**
* @description Pause animation behavior
* @return {Undefined} Void
*/
Graph.prototype.pauseAnimation = function () {
this.attr('animationPause', true)
}
/**
* @description Try animation behavior
* @return {Undefined} Void
*/
Graph.prototype.playAnimation = function () {
const { render } = this
this.attr('animationPause', false)
return new Promise(async resolve => {
await render.launchAnimation()
resolve()
})
}
/**
* @description Processor of delete
* @param {CRender} render Instance of CRender
* @return {Undefined} Void
*/
Graph.prototype.delProcessor = function (render) {
const { graphs } = render
const index = graphs.findIndex(graph => graph === this)
if (index === -1) return
if (typeof this.beforeDelete === 'function') this.beforeDelete(this)
graphs.splice(index, 1, null)
if (typeof this.deleted === 'function') this.deleted(this)
}
/**
* @description Return a timed release Promise
* @param {Number} time Release time
* @return {Promise} A timed release Promise
*/
function delay (time) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
@@ -0,0 +1,467 @@
import { getRgbaValue, getColorFromRgbValue } from '../color'
import { deepClone } from '../plugin/util'
/**
* @description Class Style
* @param {Object} style Style configuration
* @return {Style} Instance of Style
*/
export default class Style {
constructor (style) {
this.colorProcessor(style)
const defaultStyle = {
/**
* @description Rgba value of graph fill color
* @type {Array}
* @default fill = [0, 0, 0, 1]
*/
fill: [0, 0, 0, 1],
/**
* @description Rgba value of graph stroke color
* @type {Array}
* @default stroke = [0, 0, 0, 1]
*/
stroke: [0, 0, 0, 0],
/**
* @description Opacity of graph
* @type {Number}
* @default opacity = 1
*/
opacity: 1,
/**
* @description LineCap of Ctx
* @type {String}
* @default lineCap = null
* @example lineCap = 'butt'|'round'|'square'
*/
lineCap: null,
/**
* @description Linejoin of Ctx
* @type {String}
* @default lineJoin = null
* @example lineJoin = 'round'|'bevel'|'miter'
*/
lineJoin: null,
/**
* @description LineDash of Ctx
* @type {Array}
* @default lineDash = null
* @example lineDash = [10, 10]
*/
lineDash: null,
/**
* @description LineDashOffset of Ctx
* @type {Number}
* @default lineDashOffset = null
* @example lineDashOffset = 10
*/
lineDashOffset: null,
/**
* @description ShadowBlur of Ctx
* @type {Number}
* @default shadowBlur = 0
*/
shadowBlur: 0,
/**
* @description Rgba value of graph shadow color
* @type {Array}
* @default shadowColor = [0, 0, 0, 0]
*/
shadowColor: [0, 0, 0, 0],
/**
* @description ShadowOffsetX of Ctx
* @type {Number}
* @default shadowOffsetX = 0
*/
shadowOffsetX: 0,
/**
* @description ShadowOffsetY of Ctx
* @type {Number}
* @default shadowOffsetY = 0
*/
shadowOffsetY: 0,
/**
* @description LineWidth of Ctx
* @type {Number}
* @default lineWidth = 0
*/
lineWidth: 0,
/**
* @description Center point of the graph
* @type {Array}
* @default graphCenter = null
* @example graphCenter = [10, 10]
*/
graphCenter: null,
/**
* @description Graph scale
* @type {Array}
* @default scale = null
* @example scale = [1.5, 1.5]
*/
scale: null,
/**
* @description Graph rotation degree
* @type {Number}
* @default rotate = null
* @example rotate = 10
*/
rotate: null,
/**
* @description Graph translate distance
* @type {Array}
* @default translate = null
* @example translate = [10, 10]
*/
translate: null,
/**
* @description Cursor status when hover
* @type {String}
* @default hoverCursor = 'pointer'
* @example hoverCursor = 'default'|'pointer'|'auto'|'crosshair'|'move'|'wait'|...
*/
hoverCursor: 'pointer',
/**
* @description Font style of Ctx
* @type {String}
* @default fontStyle = 'normal'
* @example fontStyle = 'normal'|'italic'|'oblique'
*/
fontStyle: 'normal',
/**
* @description Font varient of Ctx
* @type {String}
* @default fontVarient = 'normal'
* @example fontVarient = 'normal'|'small-caps'
*/
fontVarient: 'normal',
/**
* @description Font weight of Ctx
* @type {String|Number}
* @default fontWeight = 'normal'
* @example fontWeight = 'normal'|'bold'|'bolder'|'lighter'|Number
*/
fontWeight: 'normal',
/**
* @description Font size of Ctx
* @type {Number}
* @default fontSize = 10
*/
fontSize: 10,
/**
* @description Font family of Ctx
* @type {String}
* @default fontFamily = 'Arial'
*/
padding:0,
lineHeight:1,//行高
wrap:true,//是否自动断行。
ellipsis:false,//一行,超过省略号。
letterSpacing:0,
fontFamily: 'Arial',
textDecoration:'none',
/**
* @description TextAlign of Ctx
* @type {String}
* @default textAlign = 'center'
* @example textAlign = 'start'|'end'|'left'|'right'|'center'
*/
textAlign: 'left',
/**
* @description TextBaseline of Ctx
* @type {String}
* @default textBaseline = 'middle'
* @example textBaseline = 'top'|'bottom'|'middle'|'alphabetic'|'hanging'
*/
textBaseline: 'middle',
/**
* @description The color used to create the gradient
* @type {Array}
* @default gradientColor = null
* @example gradientColor = ['#000', '#111', '#222']
*/
gradientColor: null,
/**
* @description Gradient type
* @type {String}
* @default gradientType = 'linear'
* @example gradientType = 'linear' | 'radial'
*/
gradientType: 'linear',
/**
* @description Gradient params
* @type {Array}
* @default gradientParams = null
* @example gradientParams = [x0, y0, x1, y1] (Linear Gradient)
* @example gradientParams = [x0, y0, r0, x1, y1, r1] (Radial Gradient)
*/
gradientParams: null,
/**
* @description When to use gradients
* @type {String}
* @default gradientWith = 'stroke'
* @example gradientWith = 'stroke' | 'fill'
*/
gradientWith: 'stroke',
/**
* @description Gradient color stops
* @type {String}
* @default gradientStops = 'auto'
* @example gradientStops = 'auto' | [0, .2, .3, 1]
*/
gradientStops: 'auto',
/**
* @description Extended color that supports animation transition
* @type {Array|Object}
* @default colors = null
* @example colors = ['#000', '#111', '#222', 'red' ]
* @example colors = { a: '#000', b: '#111' }
*/
colors: null
}
Object.assign(this, defaultStyle, style)
}
}
/**
* @description Set colors to rgba value
* @param {Object} style style config
* @param {Boolean} reverse Whether to perform reverse operation
* @return {Undefined} Void
*/
Style.prototype.colorProcessor = function (style, reverse = false) {
const processor = reverse ? getColorFromRgbValue : getRgbaValue
const colorProcessorKeys = ['fill', 'stroke', 'shadowColor']
const allKeys = Object.keys(style)
const colorKeys = allKeys.filter(key => colorProcessorKeys.find(k => k === key))
colorKeys.forEach(key => (style[key] = processor(style[key])))
const { gradientColor, colors } = style
if (gradientColor) style.gradientColor = gradientColor.map(c => processor(c))
if (colors) {
const colorsKeys = Object.keys(colors)
colorsKeys.forEach(key => (colors[key] = processor(colors[key])))
}
}
/**
* @description Init graph style
* @param {Object} ctx Context of canvas
* @return {Undefined} Void
*/
Style.prototype.initStyle = function (ctx,shape) {
initTransform(ctx, this)
initGraphStyle(ctx, this)
initGradient(ctx, this,shape)
}
/**
* @description Init canvas transform
* @param {Object} ctx Context of canvas
* @param {Style} style Instance of Style
* @return {Undefined} Void
*/
function initTransform (ctx, style) {
ctx.save()
const { graphCenter, rotate, scale, translate } = style
if (!(graphCenter instanceof Array)) return
if(graphCenter.length>0) ctx.translate(...graphCenter);
if (rotate) ctx.rotate(rotate * Math.PI / 180)
if (scale instanceof Array) ctx.scale(...scale)
if (translate) ctx.translate(...translate)
ctx.translate(-graphCenter[0], -graphCenter[1])
}
const autoSetStyleKeys = [
'lineCap', 'lineJoin', 'lineDashOffset',
'shadowOffsetX', 'shadowOffsetY', 'lineWidth',
'textAlign', 'textBaseline'
]
/**
* @description Set the style of canvas ctx
* @param {Object} ctx Context of canvas
* @param {Style} style Instance of Style
* @return {Undefined} Void
*/
function initGraphStyle (ctx, style) {
let { fill, stroke, shadowColor, opacity } = style
autoSetStyleKeys.forEach(key => {
if (key || typeof key === 'number') ctx[key] = style[key]
})
fill = [...fill]
stroke = [...stroke]
shadowColor = [...shadowColor]
fill[3] *= opacity
stroke[3] *= opacity
shadowColor[3] *= opacity
ctx.fillStyle = getColorFromRgbValue(fill)
ctx.strokeStyle = getColorFromRgbValue(stroke)
ctx.shadowColor = getColorFromRgbValue(shadowColor)
let { lineDash, shadowBlur } = style
if (lineDash) {
lineDash = lineDash.map(v => v >= 0 ? v : 0)
ctx.setLineDash(lineDash)
}
if (typeof shadowBlur === 'number') ctx.shadowBlur = shadowBlur > 0 ? shadowBlur : 0.001
const { fontStyle, fontVarient, fontWeight, fontSize, fontFamily } = style
ctx.font = fontStyle + ' ' + fontVarient + ' ' + fontWeight + ' ' + fontSize + 'px' + ' ' + fontFamily
}
/**
* @description Set the gradient color of canvas ctx
* @param {Object} ctx Context of canvas
* @param {Style} style Instance of Style
* @return {Undefined} Void
*/
function initGradient (ctx, style,shape) {
if (!gradientValidator(style)) return
let { gradientColor, gradientParams, gradientType, gradientWith, gradientStops, opacity } = style
gradientColor = gradientColor.map(color => {
let colorOpacity = color[3] * opacity
let clonedColor = [...color]
clonedColor[3] = colorOpacity
return clonedColor
})
gradientColor = gradientColor.map(c => getColorFromRgbValue(c))
if (gradientStops === 'auto') gradientStops = getAutoColorStops(gradientColor)
let gra = [...gradientParams];
if(gradientType =='linear'){
gra[0] =shape.x + gradientParams[0]
gra[1] =shape.y + gradientParams[1]
gra[2] =shape.x +gradientParams[2]
gra[3] =shape.y +gradientParams[3]
}
const gradient = ctx[`create${gradientType.slice(0, 1).toUpperCase() + gradientType.slice(1)}Gradient`](...gra)
gradientStops.forEach((stop, i) => gradient.addColorStop(stop, gradientColor[i]))
ctx[`${gradientWith}Style`] = gradient
}
/**
* @description Check if the gradient configuration is legal
* @param {Style} style Instance of Style
* @return {Boolean} Check Result
*/
function gradientValidator (style) {
const { gradientColor, gradientParams, gradientType, gradientWith, gradientStops } = style
if (!gradientColor || !gradientParams) return false
if (gradientColor.length === 1) {
console.warn('The gradient needs to provide at least two colors')
return false
}
if (gradientType !== 'linear' && gradientType !== 'radial') {
console.warn('GradientType only supports linear or radial, current value is ' + gradientType)
return false
}
const gradientParamsLength = gradientParams.length
if (
(gradientType === 'linear' && gradientParamsLength !== 4) ||
(gradientType === 'radial' && gradientParamsLength !== 6)
) {
console.warn('The expected length of gradientParams is ' + (gradientType === 'linear' ? '4' : '6'))
return false
}
if (gradientWith !== 'fill' && gradientWith !== 'stroke') {
console.warn('GradientWith only supports fill or stroke, current value is ' + gradientWith)
return false
}
if (gradientStops !== 'auto' && !(gradientStops instanceof Array)) {
console.warn(`gradientStops only supports 'auto' or Number Array ([0, .5, 1]), current value is ` + gradientStops)
return false
}
return true
}
/**
* @description Get a uniform gradient color stop
* @param {Array} color Gradient color
* @return {Array} Gradient color stop
*/
function getAutoColorStops (color) {
const stopGap = 1 / (color.length - 1)
return color.map((foo, i) => stopGap * i)
}
/**
* @description Restore canvas ctx transform
* @param {Object} ctx Context of canvas
* @return {Undefined} Void
*/
Style.prototype.restoreTransform = function (ctx) {
ctx.restore()
}
/**
* @description Update style data
* @param {Object} change Changed data
* @return {Undefined} Void
*/
Style.prototype.update = function (change) {
this.colorProcessor(change)
Object.assign(this, change)
}
/**
* @description Get the current style configuration
* @return {Object} Style configuration
*/
Style.prototype.getStyle = function () {
const clonedStyle = deepClone(this, true)
this.colorProcessor(clonedStyle, true)
return clonedStyle
}
@@ -0,0 +1,150 @@
export default new Map([
['transparent', 'rgba(0,0,0,0)'],
['black', '#000000'],
['silver', '#C0C0C0'],
['gray', '#808080'],
['white', '#FFFFFF'],
['maroon', '#800000'],
['red', '#FF0000'],
['purple', '#800080'],
['fuchsia', '#FF00FF'],
['green', '#008000'],
['lime', '#00FF00'],
['olive', '#808000'],
['yellow', '#FFFF00'],
['navy', '#000080'],
['blue', '#0000FF'],
['teal', '#008080'],
['aqua', '#00FFFF'],
['aliceblue', '#f0f8ff'],
['antiquewhite', '#faebd7'],
['aquamarine', '#7fffd4'],
['azure', '#f0ffff'],
['beige', '#f5f5dc'],
['bisque', '#ffe4c4'],
['blanchedalmond', '#ffebcd'],
['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'],
['darkgrey', '#a9a9a9'],
['darkkhaki', '#bdb76b'],
['darkmagenta', '#8b008b'],
['darkolivegreen', '#556b2f'],
['darkorange', '#ff8c00'],
['darkorchid', '#9932cc'],
['darkred', '#8b0000'],
['darksalmon', '#e9967a'],
['darkseagreen', '#8fbc8f'],
['darkslateblue', '#483d8b'],
['darkslategray', '#2f4f4f'],
['darkslategrey', '#2f4f4f'],
['darkturquoise', '#00ced1'],
['darkviolet', '#9400d3'],
['deeppink', '#ff1493'],
['deepskyblue', '#00bfff'],
['dimgray', '#696969'],
['dimgrey', '#696969'],
['dodgerblue', '#1e90ff'],
['firebrick', '#b22222'],
['floralwhite', '#fffaf0'],
['forestgreen', '#228b22'],
['gainsboro', '#dcdcdc'],
['ghostwhite', '#f8f8ff'],
['gold', '#ffd700'],
['goldenrod', '#daa520'],
['greenyellow', '#adff2f'],
['grey', '#808080'],
['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'],
['lightgrey', '#d3d3d3'],
['lightpink', '#ffb6c1'],
['lightsalmon', '#ffa07a'],
['lightseagreen', '#20b2aa'],
['lightskyblue', '#87cefa'],
['lightslategray', '#778899'],
['lightslategrey', '#778899'],
['lightsteelblue', '#b0c4de'],
['lightyellow', '#ffffe0'],
['limegreen', '#32cd32'],
['linen', '#faf0e6'],
['magenta', '#ff00ff'],
['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'],
['oldlace', '#fdf5e6'],
['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'],
['rosybrown', '#bc8f8f'],
['royalblue', '#4169e1'],
['saddlebrown', '#8b4513'],
['salmon', '#fa8072'],
['sandybrown', '#f4a460'],
['seagreen', '#2e8b57'],
['seashell', '#fff5ee'],
['sienna', '#a0522d'],
['skyblue', '#87ceeb'],
['slateblue', '#6a5acd'],
['slategray', '#708090'],
['slategrey', '#708090'],
['snow', '#fffafa'],
['springgreen', '#00ff7f'],
['steelblue', '#4682b4'],
['tan', '#d2b48c'],
['thistle', '#d8bfd8'],
['tomato', '#ff6347'],
['turquoise', '#40e0d0'],
['violet', '#ee82ee'],
['wheat', '#f5deb3'],
['whitesmoke', '#f5f5f5'],
['yellowgreen', '#9acd32']
])
@@ -0,0 +1,299 @@
import colorKeywords from './config/keywords'
const hexReg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
const rgbReg = /^(rgb|rgba|RGB|RGBA)/
const rgbaReg = /^(rgba|RGBA)/
/**
* @description Color validator
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {String|Boolean} Valid color Or false
*/
function validator (color) {
let isHex = hexReg.test(color)
let isRgb = rgbReg.test(color)
if (isHex || isRgb) return color
color = getColorByKeyword(color)
if (!color) {
console.error('Color: Invalid color!')
return false
}
return color
}
/**
* @description Get color by keyword
* @param {String} keyword Color keyword like red, green and etc.
* @return {String|Boolean} Hex or rgba color (Invalid keyword will return false)
*/
function getColorByKeyword (keyword) {
if (!keyword) {
console.error('getColorByKeywords: Missing parameters!')
return false
}
if (!colorKeywords.has(keyword)) return false
return colorKeywords.get(keyword)
}
/**
* @description Get the Rgb value of the color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {Array<Number>|Boolean} Rgb value of the color (Invalid input will return false)
*/
export function getRgbValue (color) {
if (!color) {
console.error('getRgbValue: Missing parameters!')
return false
}
color = validator(color)
if (!color) return false
const isHex = hexReg.test(color)
const isRgb = rgbReg.test(color)
const lowerColor = color.toLowerCase()
if (isHex) return getRgbValueFromHex(lowerColor)
if (isRgb) return getRgbValueFromRgb(lowerColor)
}
/**
* @description Get the rgb value of the hex color
* @param {String} color Hex color
* @return {Array<Number>} Rgb value of the color
*/
function getRgbValueFromHex (color) {
color = color.replace('#', '')
if (color.length === 3) color = Array.from(color).map(hexNum => hexNum + hexNum).join('')
color = color.split('')
return new Array(3).fill(0).map((t, i) => parseInt(`0x${color[i * 2]}${color[i * 2 + 1]}`))
}
/**
* @description Get the rgb value of the rgb/rgba color
* @param {String} color Hex color
* @return {Array} Rgb value of the color
*/
function getRgbValueFromRgb (color) {
return color
.replace(/rgb\(|rgba\(|\)/g, '')
.split(',')
.slice(0, 3)
.map(n => parseInt(n))
}
/**
* @description Get the Rgba value of the color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {Array<Number>|Boolean} Rgba value of the color (Invalid input will return false)
*/
export function getRgbaValue (color) {
if (!color) {
console.error('getRgbaValue: Missing parameters!')
return false
}
const colorValue = getRgbValue(color)
if (!colorValue) return false
colorValue.push(getOpacity(color))
return colorValue
}
/**
* @description Get the opacity of color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {Number|Boolean} Color opacity (Invalid input will return false)
*/
export function getOpacity (color) {
if (!color) {
console.error('getOpacity: Missing parameters!')
return false
}
color = validator(color)
if (!color) return false
const isRgba = rgbaReg.test(color)
if (!isRgba) return 1
color = color.toLowerCase()
return Number(color.split(',').slice(-1)[0].replace(/[)|\s]/g, ''))
}
/**
* @description Convert color to Rgb|Rgba color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @param {Number} opacity The opacity of color
* @return {String|Boolean} Rgb|Rgba color (Invalid input will return false)
*/
export function toRgb (color, opacity) {
if (!color) {
console.error('toRgb: Missing parameters!')
return false
}
const rgbValue = getRgbValue(color)
if (!rgbValue) return false
const addOpacity = typeof opacity === 'number'
if (addOpacity) return 'rgba(' + rgbValue.join(',') + `,${opacity})`
return 'rgb(' + rgbValue.join(',') + ')'
}
/**
* @description Convert color to Hex color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {String|Boolean} Hex color (Invalid input will return false)
*/
export function toHex (color) {
if (!color) {
console.error('toHex: Missing parameters!')
return false
}
if (hexReg.test(color)) return color
color = getRgbValue(color)
if (!color) return false
return '#' + color
.map(n => Number(n).toString(16))
.map(n => n === '0' ? '00' : n)
.join('')
}
/**
* @description Get Color from Rgb|Rgba value
* @param {Array<Number>} value Rgb|Rgba color value
* @return {String|Boolean} Rgb|Rgba color (Invalid input will return false)
*/
export function getColorFromRgbValue (value) {
if (!value) {
console.error('getColorFromRgbValue: Missing parameters!')
return false
}
const valueLength = value.length
if (valueLength !== 3 && valueLength !== 4) {
console.error('getColorFromRgbValue: Value is illegal!')
return false
}
let color = (valueLength === 3 ? 'rgb(' : 'rgba(')
color += value.join(',') + ')'
return color
}
/**
* @description Deepen color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {Number} Percent of Deepen (1-100)
* @return {String|Boolean} Rgba color (Invalid input will return false)
*/
export function darken (color, percent = 0) {
if (!color) {
console.error('darken: Missing parameters!')
return false
}
let rgbaValue = getRgbaValue(color)
if (!rgbaValue) return false
rgbaValue = rgbaValue
.map((v, i) => (i === 3 ? v : v - Math.ceil(2.55 * percent)))
.map(v => (v < 0 ? 0 : v))
return getColorFromRgbValue(rgbaValue)
}
/**
* @description Brighten color
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @return {Number} Percent of brighten (1-100)
* @return {String|Boolean} Rgba color (Invalid input will return false)
*/
export function lighten (color, percent = 0) {
if (!color) {
console.error('lighten: Missing parameters!')
return false
}
let rgbaValue = getRgbaValue(color)
if (!rgbaValue) return false
rgbaValue = rgbaValue
.map((v, i) => (i === 3 ? v : v + Math.ceil(2.55 * percent)))
.map(v => (v > 255 ? 255 : v))
return getColorFromRgbValue(rgbaValue)
}
/**
* @description Adjust color opacity
* @param {String} color Hex|Rgb|Rgba color or color keyword
* @param {Number} Percent of opacity
* @return {String|Boolean} Rgba color (Invalid input will return false)
*/
export function fade (color, percent = 100) {
if (!color) {
console.error('fade: Missing parameters!')
return false
}
const rgbValue = getRgbValue(color)
if (!rgbValue) return false
const rgbaValue = [...rgbValue, percent / 100]
return getColorFromRgbValue(rgbaValue)
}
export default {
fade,
toHex,
toRgb,
darken,
lighten,
getOpacity,
getRgbValue,
getRgbaValue,
getColorFromRgbValue
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,679 @@
/**
* svg路径绘制。
* 作者:tmzdy
* url:https://jx2d.cn
*/
function parsePathData(data) {
if (!data) {
return [];
}
var cs = data;
var cc = [
'm',
'M',
'l',
'L',
'v',
'V',
'h',
'H',
'z',
'Z',
'c',
'C',
'q',
'Q',
't',
'T',
's',
'S',
'a',
'A',
];
cs = cs.replace(new RegExp(' ', 'g'), ',');
for (var n = 0; n < cc.length; n++) {
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
}
var arr = cs.split('|');
var ca = [];
var coords = [];
var cpx = 0;
var cpy = 0;
var re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
var match;
for (n = 1; n < arr.length; n++) {
var str = arr[n];
var c = str.charAt(0);
str = str.slice(1);
coords.length = 0;
while ((match = re.exec(str))) {
coords.push(match[0]);
}
var p = [];
for (var j = 0, jlen = coords.length; j < jlen; j++) {
if (coords[j] === '00') {
p.push(0, 0);
continue;
}
var parsed = parseFloat(coords[j]);
if (!isNaN(parsed)) {
p.push(parsed);
} else {
p.push(0);
}
}
while (p.length > 0) {
if (isNaN(p[0])) {
break;
}
var cmd = null;
var points = [];
var startX = cpx,
startY = cpy;
var prevCmd, ctlPtx, ctlPty;
var rx, ry, psi, fa, fs, x1, y1;
switch (c) {
case 'l':
cpx += p.shift();
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'L':
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'm':
var dx = p.shift();
var dy = p.shift();
cpx += dx;
cpy += dy;
cmd = 'M';
if (ca.length > 2 && ca[ca.length - 1].command === 'z') {
for (var idx = ca.length - 2; idx >= 0; idx--) {
if (ca[idx].command === 'M') {
cpx = ca[idx].points[0] + dx;
cpy = ca[idx].points[1] + dy;
break;
}
}
}
points.push(cpx, cpy);
c = 'l';
break;
case 'M':
cpx = p.shift();
cpy = p.shift();
cmd = 'M';
points.push(cpx, cpy);
c = 'L';
break;
case 'h':
cpx += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'H':
cpx = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'v':
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'V':
cpy = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'C':
points.push(p.shift(), p.shift(), p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'c':
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'S':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 's':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'Q':
points.push(p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'q':
points.push(cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(cpx, cpy);
break;
case 'T':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx = p.shift();
cpy = p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 't':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if (prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 'A':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx = p.shift();
cpy = p.shift();
cmd = 'A';
points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
case 'a':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx += p.shift();
cpy += p.shift();
cmd = 'A';
points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
}
ca.push({
command: cmd || c,
points: points,
start: {
x: startX,
y: startY,
},
pathLength: calcLength(startX, startY, cmd || c, points),
});
}
if (c === 'z' || c === 'Z') {
ca.push({
command: 'z',
points: [],
start: undefined,
pathLength: 0,
});
}
}
return ca;
}
function calcLength(x, y, cmd, points) {
var len, p1, p2, t;
switch (cmd) {
case 'L':
return getLineLength(x, y, points[0], points[1]);
case 'C':
len = 0.0;
p1 = getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
for (t = 0.01; t <= 1; t += 0.01) {
p2 = getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[
5]);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'Q':
len = 0.0;
p1 = getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
for (t = 0.01; t <= 1; t += 0.01) {
p2 = getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'A':
len = 0.0;
var start = points[4];
var dTheta = points[5];
var end = points[4] + dTheta;
var inc = Math.PI / 180.0;
if (Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
p1 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
if (dTheta < 0) {
for (t = start - inc; t > end; t -= inc) {
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
} else {
for (t = start + inc; t < end; t += inc) {
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
len += getLineLength(p1.x, p1.y, p2.x, p2.y);
return len;
}
return 0;
}
function convertEndpointToCenterParameterization(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
var psi = psiDeg * (Math.PI / 180.0);
var xp = (Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
var yp = (-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
(Math.cos(psi) * (y1 - y2)) / 2.0;
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
if (lambda > 1) {
rx *= Math.sqrt(lambda);
ry *= Math.sqrt(lambda);
}
var f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) /
(rx * rx * (yp * yp) + ry * ry * (xp * xp)));
if (fa === fs) {
f *= -1;
}
if (isNaN(f)) {
f = 0;
}
var cxp = (f * rx * yp) / ry;
var cyp = (f * -ry * xp) / rx;
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
var vMag = function(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
};
var vRatio = function(u, v) {
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
};
var vAngle = function(u, v) {
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
};
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
var dTheta = vAngle(u, v);
if (vRatio(u, v) <= -1) {
dTheta = Math.PI;
}
if (vRatio(u, v) >= 1) {
dTheta = 0;
}
if (fs === 0 && dTheta > 0) {
dTheta = dTheta - 2 * Math.PI;
}
if (fs === 1 && dTheta < 0) {
dTheta = dTheta + 2 * Math.PI;
}
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
}
function getSelfRect() {
var points = [];
this.dataArray.forEach(function(data) {
if (data.command === 'A') {
var start = data.points[4];
var dTheta = data.points[5];
var end = data.points[4] + dTheta;
var inc = Math.PI / 180.0;
if (Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
if (dTheta < 0) {
for (let t = start - inc; t > end; t -= inc) {
const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
data.points[3], t, 0);
points.push(point.x, point.y);
}
} else {
for (let t = start + inc; t < end; t += inc) {
const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
data.points[3], t, 0);
points.push(point.x, point.y);
}
}
} else if (data.command === 'C') {
for (let t = 0.0; t <= 1; t += 0.01) {
const point = Path.getPointOnCubicBezier(t, data.start.x, data.start.y, data.points[0], data
.points[1], data.points[2], data.points[3], data.points[4], data.points[5]);
points.push(point.x, point.y);
}
} else {
points = points.concat(data.points);
}
});
var minX = points[0];
var maxX = points[0];
var minY = points[1];
var maxY = points[1];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2];
y = points[i * 2 + 1];
if (!isNaN(x)) {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
}
if (!isNaN(y)) {
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
return {
x: Math.round(minX),
y: Math.round(minY),
width: Math.round(maxX - minX),
height: Math.round(maxY - minY),
};
}
function getPointAtLength(length) {
var point, i = 0,
ii = this.dataArray.length;
if (!ii) {
return null;
}
while (i < ii && length > this.dataArray[i].pathLength) {
length -= this.dataArray[i].pathLength;
++i;
}
if (i === ii) {
point = this.dataArray[i - 1].points.slice(-2);
return {
x: point[0],
y: point[1],
};
}
if (length < 0.01) {
point = this.dataArray[i].points.slice(0, 2);
return {
x: point[0],
y: point[1],
};
}
var cp = this.dataArray[i];
var p = cp.points;
switch (cp.command) {
case 'L':
return Path.getPointOnLine(length, cp.start.x, cp.start.y, p[0], p[1]);
case 'C':
return Path.getPointOnCubicBezier(length / cp.pathLength, cp.start.x, cp.start.y, p[0], p[1], p[2], p[3], p[
4], p[5]);
case 'Q':
return Path.getPointOnQuadraticBezier(length / cp.pathLength, cp.start.x, cp.start.y, p[0], p[1], p[2], p[
3]);
case 'A':
var cx = p[0],
cy = p[1],
rx = p[2],
ry = p[3],
theta = p[4],
dTheta = p[5],
psi = p[6];
theta += (dTheta * length) / cp.pathLength;
return Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi);
}
return null;
}
function getLineLength(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
function getPointOnLine(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
if (fromX === undefined) {
fromX = P1x;
}
if (fromY === undefined) {
fromY = P1y;
}
var m = (P2y - P1y) / (P2x - P1x + 0.00000001);
var run = Math.sqrt((dist * dist) / (1 + m * m));
if (P2x < P1x) {
run *= -1;
}
var rise = m * run;
var pt;
if (P2x === P1x) {
pt = {
x: fromX,
y: fromY + rise,
};
} else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) {
pt = {
x: fromX + run,
y: fromY + rise,
};
} else {
var ix, iy;
var len = this.getLineLength(P1x, P1y, P2x, P2y);
var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y);
u = u / (len * len);
ix = P1x + u * (P2x - P1x);
iy = P1y + u * (P2y - P1y);
var pRise = this.getLineLength(fromX, fromY, ix, iy);
var pRun = Math.sqrt(dist * dist - pRise * pRise);
run = Math.sqrt((pRun * pRun) / (1 + m * m));
if (P2x < P1x) {
run *= -1;
}
rise = m * run;
pt = {
x: ix + run,
y: iy + rise,
};
}
return pt;
}
function getPointOnCubicBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
function CB1(t) {
return t * t * t;
}
function CB2(t) {
return 3 * t * t * (1 - t);
}
function CB3(t) {
return 3 * t * (1 - t) * (1 - t);
}
function CB4(t) {
return (1 - t) * (1 - t) * (1 - t);
}
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
return {
x: x,
y: y,
};
}
function getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
function QB1(t) {
return t * t;
}
function QB2(t) {
return 2 * t * (1 - t);
}
function QB3(t) {
return (1 - t) * (1 - t);
}
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
return {
x: x,
y: y,
};
}
function getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi) {
var cosPsi = Math.cos(psi),
sinPsi = Math.sin(psi);
var pt = {
x: rx * Math.cos(theta),
y: ry * Math.sin(theta),
};
return {
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
y: cy + (pt.x * sinPsi + pt.y * cosPsi),
};
}
let path2d = function(render, config = {}) {
const [w, h] = render.area;
let cfg = {
name: 'path',
animationCurve: 'easeOutBack',
hover: true,
drag: true,
shape: {
path: '',
close:true,
points:[]
},
style: {
stroke: '#000',
fill:'#000',
lineWidth: 1,
hoverCursor: 'pointer',
},
...config,
draw({ ctx }, { shape, style: { lineWidth } }){
let { points, close,x,y ,path} = shape
var ca=[]
if(this.shape['points'].length>0&&this.shape['points']){
ca = this.shape['svg']
}else{
ca = parsePathData(path);
// ca = ca.map(el=>{
// if(el.points.length){
// return {...el,points:[el.points[0]+x,el.points[1]+y]}
// }
// return el
// })
let ar = ca.map(el=> el.points)
this.shape['points'] = ar.filter((el)=>el.length==2)
this.shape['svg'] = ca;
}
const context=ctx
context.beginPath();
for (var n = 0; n < ca.length; n++) {
var c = ca[n].command;
var p = ca[n].points;
switch (c) {
case 'L':
context.lineTo(p[0], p[1]);
break;
case 'M':
context.moveTo(p[0], p[1]);
break;
case 'C':
context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
break;
case 'Q':
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
break;
case 'A':
var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
var r = rx > ry ? rx : ry;
var scaleX = rx > ry ? 1 : rx / ry;
var scaleY = rx > ry ? ry / rx : 1;
context.translate(cx, cy);
context.rotate(psi);
context.scale(scaleX, scaleY);
context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
context.scale(1 / scaleX, 1 / scaleY);
context.rotate(-psi);
context.translate(-cx, -cy);
break;
case 'z':
close = true;
context.closePath();
break;
}
}
if (close) {
ctx.closePath()
ctx.fill()
ctx.stroke()
} else {
ctx.stroke()
}
},
};
return cfg;
}
export default path2d;
@@ -0,0 +1,136 @@
/**
* 环形图。
* 作者:tmzdy
* url:https://jx2d.cn
*/
let ring = function (render, config={}) {
let defauletcfg = {
tooltip: {
trigger: 'item'
},
color:["#E91E63","#2196F3","#311B92","#FDD835","#BCAAA4"],
legend: {
top: '5%',
left: 'center'
},
series: [{
name: 'Access From',
type: 'pie',
radius: ['20%', '35%'],
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [{
value: 1048,
name: 'Search Engine'
},
{
value: 735,
name: 'Direct'
},
{
value: 580,
name: 'Email'
},
{
value: 484,
name: 'Union Ads'
},
{
value: 300,
name: 'Video Ads'
}
]
}]
}
defauletcfg = {...defauletcfg,...config}
const [w, h] = render.area;
//最大圆环厚度。等于圆。
let lineMaxHeight = w / 2;
let lineStoreWidth = (parseFloat(defauletcfg.series[0].radius[1]) - parseFloat(defauletcfg.series[0].radius[0]))/100;
lineStoreWidth =Math.floor( lineStoreWidth*lineMaxHeight);
let r = parseFloat(defauletcfg.series[0].radius[1])/100 * lineMaxHeight;
let data = defauletcfg.series[0].data;
let maxValue = data.map(el => parseInt(el.value));
//总数量。
maxValue = maxValue.reduce((p,c)=>p+c);
//计算各自的比例数量。保留两位小数点。
let rang = Math.PI *2;
//每一个元素的圆弧的长度。
let rangArray = data.map(el=>{
let num = parseInt(el.value)/maxValue;
return rang*num
})
let dataBlv = data.map((el,index) => {
let st = index;
let num = parseFloat(el.value)/maxValue;
let blv = parseFloat(num.toFixed(2));
let startAngle = 0;
let endAngle = 0;
if(st==0){
startAngle = 0;
endAngle = rangArray[st]
}else{
let s = rangArray.slice(0,st)
startAngle = s.reduce((p,c)=>p+c);
endAngle = startAngle+rangArray[st]
}
return {...el,
blv:blv,
startAngle:startAngle,
endAngle:endAngle
};
});
let xf = [];
dataBlv.forEach((el,index)=>{
let cfg = {
name: 'arc',
animationCurve: 'liner',
hover: false,
drag: false,
shape: {
rx: w/2,
ry: h/2,
r: r,
startAngle: el.startAngle,
endAngle: el.endAngle,
},
style: {
stroke: defauletcfg.color[index],
lineWidth: lineStoreWidth,
shadowBlur: 0,
rotate: 0,
shadowColor: '#66eece',
hoverCursor: 'pointer',
},
};
xf.push(cfg)
})
return xf;
}
export default ring;
+11
View File
@@ -0,0 +1,11 @@
import CRender from './class/crender.class'
import {
extendNewGraph
} from './config/graphs'
export default {
CRender,
extendNewGraph
}
@@ -0,0 +1,50 @@
/**
* @description Draw a polyline path
* @param {Object} ctx Canvas 2d context
* @param {Array} points The points that makes up a polyline
* @param {Boolean} beginPath Whether to execute beginPath
* @param {Boolean} closePath Whether to execute closePath
* @return {Undefined} Void
*/
export function drawPolylinePath (ctx, points, beginPath = false, closePath = false) {
if (!ctx || points.length < 2) return false
if (beginPath) ctx.beginPath()
points.forEach((point, i) =>
point && (i === 0 ? ctx.moveTo(...point) : ctx.lineTo(...point)))
if (closePath) {
ctx.closePath()
ctx.draw()
}
}
/**
* @description Draw a bezier curve path
* @param {Object} ctx Canvas 2d context
* @param {Array} points The points that makes up a bezier curve
* @param {Array} moveTo The point need to excute moveTo
* @param {Boolean} beginPath Whether to execute beginPath
* @param {Boolean} closePath Whether to execute closePath
* @return {Undefined} Void
*/
export function drawBezierCurvePath (ctx, points, moveTo = false, beginPath = false, closePath = false) {
if (!ctx || !points) return false
if (beginPath) ctx.beginPath()
if (moveTo) ctx.moveTo(...moveTo)
points.forEach(item => (item && ctx.bezierCurveTo(...item[0], ...item[1], ...item[2])))
if (closePath) {
ctx.closePath()
ctx.draw()
}
}
export default {
drawPolylinePath,
drawBezierCurvePath
}
@@ -0,0 +1,332 @@
const { abs, sqrt, sin, cos, max, min, PI } = Math
/**
* @description Clone an object or array
* @param {Object|Array} object Cloned object
* @param {Boolean} recursion Whether to use recursive cloning
* @return {Object|Array} Clone object
*/
export function deepClone (object, recursion = false) {
if (!object) return object
const { parse, stringify } = JSON
if (!recursion) return parse(stringify(object))
const clonedObj = object instanceof Array ? [] : {}
if (object && typeof object === 'object') {
for (let key in object) {
if (object.hasOwnProperty(key)) {
if (object[key] && typeof object[key] === 'object') {
clonedObj[key] = deepClone(object[key], true)
} else {
clonedObj[key] = object[key]
}
}
}
}
return clonedObj
}
/**
* @description Eliminate line blur due to 1px line width
* @param {Array} points Line points
* @return {Array} Line points after processed
*/
export function eliminateBlur (points) {
return points.map(([x, y]) => [parseInt(x) + 0.5, parseInt(y) + 0.5])
}
/**
* @description Check if the point is inside the circle
* @param {Array} point Postion of point
* @param {Number} rx Circle x coordinate
* @param {Number} ry Circle y coordinate
* @param {Number} r Circle radius
* @return {Boolean} Result of check
*/
export function checkPointIsInCircle (point, rx, ry, r) {
return getTwoPointDistance(point, [rx, ry]) <= r
}
/**
* @description Get the distance between two points
* @param {Array} point1 point1
* @param {Array} point2 point2
* @return {Number} Distance between two points
*/
export function getTwoPointDistance ([xa, ya], [xb, yb]) {
const minusX = abs(xa - xb)
const minusY = abs(ya - yb)
return sqrt(minusX * minusX + minusY * minusY)
}
/**
* @description Check if the point is inside the polygon
* @param {Array} point Postion of point
* @param {Array} points The points that makes up a polyline
* @return {Boolean} Result of check
*/
export function checkPointIsInPolygon (point, polygon) {
let counter = 0
const [x, y] = point
const pointNum = polygon.length
for (let i = 1, p1 = polygon[0]; i <= pointNum; i++) {
const p2 = polygon[i % pointNum]
if (x > min(p1[0], p2[0]) && x <= max(p1[0], p2[0])) {
if (y <= max(p1[1], p2[1])) {
if (p1[0] !== p2[0]) {
const xinters = (x - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1]
if (p1[1] === p2[1] || y <= xinters) {
counter++
}
}
}
}
p1 = p2
}
return counter % 2 === 1
}
/**
* @description Check if the point is inside the sector
* @param {Array} point Postion of point
* @param {Number} rx Sector x coordinate
* @param {Number} ry Sector y coordinate
* @param {Number} r Sector radius
* @param {Number} startAngle Sector start angle
* @param {Number} endAngle Sector end angle
* @param {Boolean} clockWise Whether the sector angle is clockwise
* @return {Boolean} Result of check
*/
export function checkPointIsInSector (point, rx, ry, r, startAngle, endAngle, clockWise) {
if (!point) return false
if (getTwoPointDistance(point, [rx, ry]) > r) return false
if (!clockWise) [startAngle, endAngle] = deepClone([endAngle, startAngle])
const reverseBE = startAngle > endAngle
if (reverseBE) [startAngle, endAngle] = [endAngle, startAngle]
const minus = endAngle - startAngle
if (minus >= PI * 2) return true
const [x, y] = point
const [bx, by] = getCircleRadianPoint(rx, ry, r, startAngle)
const [ex, ey] = getCircleRadianPoint(rx, ry, r, endAngle)
const vPoint = [x - rx, y - ry]
let vBArm = [bx - rx, by - ry]
let vEArm = [ex - rx, ey - ry]
const reverse = minus > PI
if (reverse) [vBArm, vEArm] = deepClone([vEArm, vBArm])
let inSector = isClockWise(vBArm, vPoint) && !isClockWise(vEArm, vPoint)
if (reverse) inSector = !inSector
if (reverseBE) inSector = !inSector
return inSector
}
/**
* @description Determine if the point is in the clockwise direction of the vector
* @param {Array} vArm Vector
* @param {Array} vPoint Point
* @return {Boolean} Result of check
*/
function isClockWise (vArm, vPoint) {
const [ax, ay] = vArm
const [px, py] = vPoint
return -ay * px + ax * py > 0
}
/**
* @description Check if the point is inside the polyline
* @param {Array} point Postion of point
* @param {Array} polyline The points that makes up a polyline
* @param {Number} lineWidth Polyline linewidth
* @return {Boolean} Result of check
*/
export function checkPointIsNearPolyline (point, polyline, lineWidth) {
const halfLineWidth = lineWidth / 2
const moveUpPolyline = polyline.map(([x, y]) => [x, y - halfLineWidth])
const moveDownPolyline = polyline.map(([x, y]) => [x, y + halfLineWidth])
const polygon = [...moveUpPolyline, ...moveDownPolyline.reverse()]
return checkPointIsInPolygon(point, polygon)
}
/**
* @description Check if the point is inside the rect
* @param {Array} point Postion of point
* @param {Number} x Rect start x coordinate
* @param {Number} y Rect start y coordinate
* @param {Number} width Rect width
* @param {Number} height Rect height
* @return {Boolean} Result of check
*/
export function checkPointIsInRect ([px, py], x, y, width, height) {
if (px < x) return false
if (py < y) return false
if (px > x + width) return false
if (py > y + height) return false
return true
}
/**
* @description Get the coordinates of the rotated point
* @param {Number} rotate Degree of rotation
* @param {Array} point Postion of point
* @param {Array} origin Rotation center
* @param {Array} origin Rotation center
* @return {Number} Coordinates after rotation
*/
export function getRotatePointPos (rotate = 0, point, origin = [0, 0]) {
if (!point) return false
if (rotate % 360 === 0) return point
const [x, y] = point
const [ox, oy] = origin
rotate *= PI / 180
return [
(x - ox) * cos(rotate) - (y - oy) * sin(rotate) + ox,
(x - ox) * sin(rotate) + (y - oy) * cos(rotate) + oy
]
}
/**
* @description Get the coordinates of the scaled point
* @param {Array} scale Scale factor
* @param {Array} point Postion of point
* @param {Array} origin Scale center
* @return {Number} Coordinates after scale
*/
export function getScalePointPos (scale = [1, 1], point, origin = [0, 0]) {
if (!point) return false
if (scale === 1) return point
const [x, y] = point
const [ox, oy] = origin
const [xs, ys] = scale
const relativePosX = x - ox
const relativePosY = y - oy
return [
relativePosX * xs + ox,
relativePosY * ys + oy
]
}
/**
* @description Get the coordinates of the scaled point
* @param {Array} translate Translation distance
* @param {Array} point Postion of point
* @return {Number} Coordinates after translation
*/
export function getTranslatePointPos (translate, point) {
if (!translate || !point) return false
const [x, y] = point
const [tx, ty] = translate
return [x + tx, y + ty]
}
/**
* @description Get the distance from the point to the line
* @param {Array} point Postion of point
* @param {Array} lineBegin Line start position
* @param {Array} lineEnd Line end position
* @return {Number} Distance between point and line
*/
export function getDistanceBetweenPointAndLine (point, lineBegin, lineEnd) {
if (!point || !lineBegin || !lineEnd) return false
const [x, y] = point
const [x1, y1] = lineBegin
const [x2, y2] = lineEnd
const a = y2 - y1
const b = x1 - x2
const c = y1 * (x2 - x1) - x1 * (y2 - y1)
const molecule = abs(a * x + b * y + c)
const denominator = sqrt(a * a + b * b)
return molecule / denominator
}
/**
* @description Get the coordinates of the specified radian on the circle
* @param {Number} x Circle x coordinate
* @param {Number} y Circle y coordinate
* @param {Number} radius Circle radius
* @param {Number} radian Specfied radian
* @return {Array} Postion of point
*/
export function getCircleRadianPoint (x, y, radius, radian) {
return [x + cos(radian) * radius, y + sin(radian) * radius]
}
/**
* @description Get the points that make up a regular polygon
* @param {Number} x X coordinate of the polygon inscribed circle
* @param {Number} y Y coordinate of the polygon inscribed circle
* @param {Number} r Radius of the polygon inscribed circle
* @param {Number} side Side number
* @param {Number} minus Radian offset
* @return {Array} Points that make up a regular polygon
*/
export function getRegularPolygonPoints (rx, ry, r, side, minus = PI * -0.5) {
const radianGap = PI * 2 / side
const radians = new Array(side).fill('').map((t, i) => i * radianGap + minus)
return radians.map(radian => getCircleRadianPoint(rx, ry, r, radian))
}
export default {
deepClone,
eliminateBlur,
checkPointIsInCircle,
checkPointIsInPolygon,
checkPointIsInSector,
checkPointIsNearPolyline,
getTwoPointDistance,
getRotatePointPos,
getScalePointPos,
getTranslatePointPos,
getCircleRadianPoint,
getRegularPolygonPoints,
getDistanceBetweenPointAndLine
}
@@ -0,0 +1,109 @@
export const linear = [[[0, 1], '', [0.33, 0.67]], [[1, 0], [0.67, 0.33]]]
/**
* @description Sine
*/
export const easeInSine = [[[0, 1]], [[0.538, 0.564], [0.169, 0.912], [0.880, 0.196]], [[1, 0]]]
export const easeOutSine = [[[0, 1]], [[0.444, 0.448], [0.169, 0.736], [0.718, 0.16]], [[1, 0]]]
export const easeInOutSine = [[[0, 1]], [[0.5, 0.5], [0.2, 1], [0.8, 0]], [[1, 0]]]
/**
* @description Quad
*/
export const easeInQuad = [[[0, 1]], [[0.550, 0.584], [0.231, 0.904], [0.868, 0.264]], [[1, 0]]]
export const easeOutQuad = [[[0, 1]], [[0.413, 0.428], [0.065, 0.816], [0.760, 0.04]], [[1, 0]]]
export const easeInOutQuad = [[[0, 1]], [[0.5, 0.5], [0.3, 0.9], [0.7, 0.1]], [[1, 0]]]
/**
* @description Cubic
*/
export const easeInCubic = [[[0, 1]], [[0.679, 0.688], [0.366, 0.992], [0.992, 0.384]], [[1, 0]]]
export const easeOutCubic = [[[0, 1]], [[0.321, 0.312], [0.008, 0.616], [0.634, 0.008]], [[1, 0]]]
export const easeInOutCubic = [[[0, 1]], [[0.5, 0.5], [0.3, 1], [0.7, 0]], [[1, 0]]]
/**
* @description Quart
*/
export const easeInQuart = [[[0, 1]], [[0.812, 0.74], [0.611, 0.988], [1.013, 0.492]], [[1, 0]]]
export const easeOutQuart = [[[0, 1]], [[0.152, 0.244], [0.001, 0.448], [0.285, -0.02]], [[1, 0]]]
export const easeInOutQuart = [[[0, 1]], [[0.5, 0.5], [0.4, 1], [0.6, 0]], [[1, 0]]]
/**
* @description Quint
*/
export const easeInQuint = [[[0, 1]], [[0.857, 0.856], [0.714, 1], [1, 0.712]], [[1, 0]]]
export const easeOutQuint = [[[0, 1]], [[0.108, 0.2], [0.001, 0.4], [0.214, -0.012]], [[1, 0]]]
export const easeInOutQuint = [[[0, 1]], [[0.5, 0.5], [0.5, 1], [0.5, 0]], [[1, 0]]]
/**
* @description Back
*/
export const easeInBack = [[[0, 1]], [[0.667, 0.896], [0.380, 1.184], [0.955, 0.616]], [[1, 0]]]
export const easeOutBack = [[[0, 1]], [[0.335, 0.028], [0.061, 0.22], [0.631, -0.18]], [[1, 0]]]
export const easeInOutBack = [[[0, 1]], [[0.5, 0.5], [0.4, 1.4], [0.6, -0.4]], [[1, 0]]]
/**
* @description Elastic
*/
export const easeInElastic = [[[0, 1]], [[0.474, 0.964], [0.382, 0.988], [0.557, 0.952]], [[0.619, 1.076], [0.565, 1.088], [0.669, 1.08]], [[0.770, 0.916], [0.712, 0.924], [0.847, 0.904]], [[0.911, 1.304], [0.872, 1.316], [0.961, 1.34]], [[1, 0]]]
export const easeOutElastic = [[[0, 1]], [[0.073, -0.32], [0.034, -0.328], [0.104, -0.344]], [[0.191, 0.092], [0.110, 0.06], [0.256, 0.08]], [[0.310, -0.076], [0.260, -0.068], [0.357, -0.076]], [[0.432, 0.032], [0.362, 0.028], [0.683, -0.004]], [[1, 0]]]
export const easeInOutElastic = [[[0, 1]], [[0.210, 0.94], [0.167, 0.884], [0.252, 0.98]], [[0.299, 1.104], [0.256, 1.092], [0.347, 1.108]], [[0.5, 0.496], [0.451, 0.672], [0.548, 0.324]], [[0.696, -0.108], [0.652, -0.112], [0.741, -0.124]], [[0.805, 0.064], [0.756, 0.012], [0.866, 0.096]], [[1, 0]]]
/**
* @description Bounce
*/
export const easeInBounce = [[[0, 1]], [[0.148, 1], [0.075, 0.868], [0.193, 0.848]], [[0.326, 1], [0.276, 0.836], [0.405, 0.712]], [[0.600, 1], [0.511, 0.708], [0.671, 0.348]], [[1, 0]]]
export const easeOutBounce = [[[0, 1]], [[0.357, 0.004], [0.270, 0.592], [0.376, 0.252]], [[0.604, -0.004], [0.548, 0.312], [0.669, 0.184]], [[0.820, 0], [0.749, 0.184], [0.905, 0.132]], [[1, 0]]]
export const easeInOutBounce = [[[0, 1]], [[0.102, 1], [0.050, 0.864], [0.117, 0.86]], [[0.216, 0.996], [0.208, 0.844], [0.227, 0.808]], [[0.347, 0.996], [0.343, 0.8], [0.480, 0.292]], [[0.635, 0.004], [0.511, 0.676], [0.656, 0.208]], [[0.787, 0], [0.760, 0.2], [0.795, 0.144]], [[0.905, -0.004], [0.899, 0.164], [0.944, 0.144]], [[1, 0]]]
export default new Map([
['linear', linear],
['easeInSine', easeInSine],
['easeOutSine', easeOutSine],
['easeInOutSine', easeInOutSine],
['easeInQuad', easeInQuad],
['easeOutQuad', easeOutQuad],
['easeInOutQuad', easeInOutQuad],
['easeInCubic', easeInCubic],
['easeOutCubic', easeOutCubic],
['easeInOutCubic', easeInOutCubic],
['easeInQuart', easeInQuart],
['easeOutQuart', easeOutQuart],
['easeInOutQuart', easeInOutQuart],
['easeInQuint', easeInQuint],
['easeOutQuint', easeOutQuint],
['easeInOutQuint', easeInOutQuint],
['easeInBack', easeInBack],
['easeOutBack', easeOutBack],
['easeInOutBack', easeInOutBack],
['easeInElastic', easeInElastic],
['easeOutElastic', easeOutElastic],
['easeInOutElastic', easeInOutElastic],
['easeInBounce', easeInBounce],
['easeOutBounce', easeOutBounce],
['easeInOutBounce', easeInOutBounce]
])
@@ -0,0 +1,310 @@
import curves from './config/curves'
const defaultTransitionBC = 'linear'
/**
* @description Get the N-frame animation state by the start and end state
* of the animation and the easing curve
* @param {String|Array} tBC Easing curve name or data
* @param {Number|Array|Object} startState Animation start state
* @param {Number|Array|Object} endState Animation end state
* @param {Number} frameNum Number of Animation frames
* @param {Boolean} deep Whether to use recursive mode
* @return {Array|Boolean} State of each frame of the animation (Invalid input will return false)
*/
export function transition (tBC, startState = null, endState = null, frameNum = 30, deep = false) {
if (!checkParams(...arguments)) return false
try {
// Get the transition bezier curve
const bezierCurve = getBezierCurve(tBC)
// Get the progress of each frame state
const frameStateProgress = getFrameStateProgress(bezierCurve, frameNum)
// If the recursion mode is not enabled or the state type is Number, the shallow state calculation is performed directly.
if (!deep || typeof endState === 'number') return getTransitionState(startState, endState, frameStateProgress)
return recursionTransitionState(startState, endState, frameStateProgress)
} catch(e) {
console.warn('Transition parameter may be abnormal!')
return [endState]
}
}
/**
* @description Check if the parameters are legal
* @param {String} tBC Name of transition bezier curve
* @param {Any} startState Transition start state
* @param {Any} endState Transition end state
* @param {Number} frameNum Number of transition frames
* @return {Boolean} Is the parameter legal
*/
function checkParams (tBC, startState = false, endState = false, frameNum = 30) {
if (!tBC || startState === false || endState === false || !frameNum) {
console.error('transition: Missing Parameters!')
return false
}
if (typeof startState !== typeof endState) {
console.error('transition: Inconsistent Status Types!')
return false
}
const stateType = typeof endState
if (stateType === 'string' || stateType === 'boolean' || !tBC.length) {
console.error('transition: Unsupported Data Type of State!')
return false
}
if (!curves.has(tBC) && !(tBC instanceof Array)) {
// console.warn('transition: Transition curve not found, default curve will be used!')
}
return true
}
/**
* @description Get the transition bezier curve
* @param {String} tBC Name of transition bezier curve
* @return {Array} Bezier curve data
*/
function getBezierCurve (tBC) {
let bezierCurve = ''
if (curves.has(tBC)) {
bezierCurve = curves.get(tBC)
} else if (tBC instanceof Array) {
bezierCurve = tBC
} else {
bezierCurve = curves.get(defaultTransitionBC)
}
return bezierCurve
}
/**
* @description Get the progress of each frame state
* @param {Array} bezierCurve Transition bezier curve
* @param {Number} frameNum Number of transition frames
* @return {Array} Progress of each frame state
*/
function getFrameStateProgress (bezierCurve, frameNum) {
const tMinus = 1 / (frameNum - 1)
const tState = new Array(frameNum).fill(0).map((t, i) => i * tMinus)
const frameState = tState.map(t => getFrameStateFromT(bezierCurve, t))
return frameState
}
/**
* @description Get the progress of the corresponding frame according to t
* @param {Array} bezierCurve Transition bezier curve
* @param {Number} t Current frame t
* @return {Number} Progress of current frame
*/
function getFrameStateFromT (bezierCurve, t) {
const tBezierCurvePoint = getBezierCurvePointFromT(bezierCurve, t)
const bezierCurvePointT = getBezierCurvePointTFromReT(tBezierCurvePoint, t)
return getBezierCurveTState(tBezierCurvePoint, bezierCurvePointT)
}
/**
* @description Get the corresponding sub-curve according to t
* @param {Array} bezierCurve Transition bezier curve
* @param {Number} t Current frame t
* @return {Array} Sub-curve of t
*/
function getBezierCurvePointFromT (bezierCurve, t) {
const lastIndex = bezierCurve.length - 1
let [begin, end] = ['', '']
bezierCurve.findIndex((item, i) => {
if (i === lastIndex) return
begin = item
end = bezierCurve[i + 1]
const currentMainPointX = begin[0][0]
const nextMainPointX = end[0][0]
return t >= currentMainPointX && t < nextMainPointX
})
const p0 = begin[0]
const p1 = begin[2] || begin[0]
const p2 = end[1] || end[0]
const p3 = end[0]
return [p0, p1, p2, p3]
}
/**
* @description Get local t based on t and sub-curve
* @param {Array} bezierCurve Sub-curve
* @param {Number} t Current frame t
* @return {Number} local t of sub-curve
*/
function getBezierCurvePointTFromReT (bezierCurve, t) {
const reBeginX = bezierCurve[0][0]
const reEndX = bezierCurve[3][0]
const xMinus = reEndX - reBeginX
const tMinus = t - reBeginX
return tMinus / xMinus
}
/**
* @description Get the curve progress of t
* @param {Array} bezierCurve Sub-curve
* @param {Number} t Current frame t
* @return {Number} Progress of current frame
*/
function getBezierCurveTState ([[, p0], [, p1], [, p2], [, p3]], t) {
const { pow } = Math
const tMinus = 1 - t
const result1 = p0 * pow(tMinus, 3)
const result2 = 3 * p1 * t * pow(tMinus, 2)
const result3 = 3 * p2 * pow(t, 2) * tMinus
const result4 = p3 * pow(t, 3)
return 1 - (result1 + result2 + result3 + result4)
}
/**
* @description Get transition state according to frame progress
* @param {Any} startState Transition start state
* @param {Any} endState Transition end state
* @param {Array} frameState Frame state progress
* @return {Array} Transition frame state
*/
function getTransitionState (begin, end, frameState) {
let stateType = 'object'
if (typeof begin === 'number') stateType = 'number'
if (begin instanceof Array) stateType = 'array'
if (stateType === 'number') return getNumberTransitionState(begin, end, frameState)
if (stateType === 'array') return getArrayTransitionState(begin, end, frameState)
if (stateType === 'object') return getObjectTransitionState(begin, end, frameState)
return frameState.map(t => end)
}
/**
* @description Get the transition data of the number type
* @param {Number} startState Transition start state
* @param {Number} endState Transition end state
* @param {Array} frameState Frame state progress
* @return {Array} Transition frame state
*/
function getNumberTransitionState (begin, end, frameState) {
const minus = end - begin
return frameState.map(s => begin + minus * s)
}
/**
* @description Get the transition data of the array type
* @param {Array} startState Transition start state
* @param {Array} endState Transition end state
* @param {Array} frameState Frame state progress
* @return {Array} Transition frame state
*/
function getArrayTransitionState (begin, end, frameState) {
const minus = end.map((v, i) => {
if (typeof v !== 'number') return false
return v - begin[i]
})
return frameState.map(s =>
minus.map((v, i) => {
if (v === false) return end[i]
return begin[i] + v * s
}))
}
/**
* @description Get the transition data of the object type
* @param {Object} startState Transition start state
* @param {Object} endState Transition end state
* @param {Array} frameState Frame state progress
* @return {Array} Transition frame state
*/
function getObjectTransitionState (begin, end, frameState) {
const keys = Object.keys(end)
const beginValue = keys.map(k => begin[k])
const endValue = keys.map(k => end[k])
const arrayState = getArrayTransitionState(beginValue, endValue, frameState)
return arrayState.map(item => {
const frameData = {}
item.forEach((v, i) => (frameData[keys[i]] = v))
return frameData
})
}
/**
* @description Get the transition state data by recursion
* @param {Array|Object} startState Transition start state
* @param {Array|Object} endState Transition end state
* @param {Array} frameState Frame state progress
* @return {Array} Transition frame state
*/
function recursionTransitionState (begin, end, frameState) {
const state = getTransitionState(begin, end, frameState)
for (let key in end) {
const bTemp = begin[key]
const eTemp = end[key]
if (typeof eTemp !== 'object') continue
const data = recursionTransitionState(bTemp, eTemp, frameState)
state.forEach((fs, i) => (fs[key] = data[i]))
}
return state
}
/**
* @description Inject new curve into curves as config
* @param {Any} key The key of curve
* @param {Array} curve Bezier curve data
* @return {Undefined} No return
*/
export function injectNewCurve (key, curve) {
if (!key || !curve) {
console.error('InjectNewCurve Missing Parameters!')
return
}
curves.set(key, curve)
}
export default transition
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
var pc = require('./zh-cn.min')
var relativeTime = require('./relativeTime.min')
var isBetween = require('./isBetween.min')
var toObject = require('./toObject.min')
var dayjs = require('./dayjs.min')
dayjs.locale(pc)
// dayjs.locale('zh-cn')
dayjs.extend(relativeTime)
dayjs.extend(isBetween)
dayjs.extend(toObject)
export default {dayjs};
+5
View File
@@ -0,0 +1,5 @@
/**
* Skipped minification because the original files appears to be already minified.
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(e="undefined"!=typeof globalThis?globalThis:e||self).dayjs_plugin_isBetween=i()}(this,(function(){"use strict";return function(e,i,t){i.prototype.isBetween=function(e,i,s,f){var n=t(e),o=t(i),r="("===(f=f||"()")[0],u=")"===f[1];return(r?this.isAfter(n,s):!this.isBefore(n,s))&&(u?this.isBefore(o,s):!this.isAfter(o,s))||(r?this.isBefore(n,s):!this.isAfter(n,s))&&(u?this.isAfter(o,s):!this.isBefore(o,s))}}}));
+5
View File
@@ -0,0 +1,5 @@
/**
* Skipped minification because the original files appears to be already minified.
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(r="undefined"!=typeof globalThis?globalThis:r||self).dayjs_plugin_relativeTime=e()}(this,(function(){"use strict";return function(r,e,t){r=r||{};var n=e.prototype,o={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"};function i(r,e,t,o){return n.fromToBase(r,e,t,o)}t.en.relativeTime=o,n.fromToBase=function(e,n,i,d,u){for(var f,a,s,l=i.$locale().relativeTime||o,h=r.thresholds||[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],m=h.length,c=0;c<m;c+=1){var y=h[c];y.d&&(f=d?t(e).diff(i,y.d,!0):i.diff(e,y.d,!0));var p=(r.rounding||Math.round)(Math.abs(f));if(s=f>0,p<=y.r||!y.r){p<=1&&c>0&&(y=h[c-1]);var v=l[y.l];u&&(p=u(""+p)),a="string"==typeof v?v.replace("%d",p):v(p,n,y.l,s);break}}if(n)return a;var M=s?l.future:l.past;return"function"==typeof M?M(a):M.replace("%s",a)},n.to=function(r,e){return i(r,e,this,!0)},n.from=function(r,e){return i(r,e,this)};var d=function(r){return r.$u?t.utc():t()};n.toNow=function(r){return this.to(d(this),r)},n.fromNow=function(r){return this.from(d(this),r)}}}));
+5
View File
@@ -0,0 +1,5 @@
/**
* Skipped minification because the original files appears to be already minified.
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_toObject=e()}(this,(function(){"use strict";return function(t,e){e.prototype.toObject=function(){return{years:this.$y,months:this.$M,date:this.$D,hours:this.$H,minutes:this.$m,seconds:this.$s,milliseconds:this.$ms}}}}));
+7
View File
@@ -0,0 +1,7 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/dayjs@1.10.7/locale/zh-cn.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,_){"object"==typeof exports&&"undefined"!=typeof module?module.exports=_(require("./dayjs.min")):"function"==typeof define&&define.amd?define(["dayjs"],_):(e="undefined"!=typeof globalThis?globalThis:e||self).dayjs_locale_zh_cn=_(e.dayjs)}(this,(function(e){"use strict";function _(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var t=_(e),d={name:"zh-cn",weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),ordinal:function(e,_){switch(_){case"W":return e+"周";default:return e+"日"}},weekStart:1,yearStart:4,formats:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日Ah点mm分",LLLL:"YYYY年M月D日ddddAh点mm分",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},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 年"},meridiem:function(e,_){var t=100*e+_;return t<600?"凌晨":t<900?"早上":t<1100?"上午":t<1300?"中午":t<1800?"下午":"晚上"}};return t.default.locale(d,null,!0),d}));
+24
View File
@@ -0,0 +1,24 @@
// 此库来自 https://www.uviewui.com/js/intro.html
// 判断arr是否为一个数组,返回一个bool值
function isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
// 深度克隆
function deepClone (obj) {
// 对常见的“非”值,直接返回原来值
if([null, undefined, NaN, false].includes(obj)) return obj;
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
}
return o;
}
export default deepClone;
+31
View File
@@ -0,0 +1,31 @@
// 此库来自 https://www.uviewui.com/js/intro.html
import deepClone from "./deepClone";
// JS对象深度合并
function deepMerge(target = {}, source = {}) {
target = deepClone(target);
if (typeof target !== 'object' || typeof source !== 'object') return false;
for (var prop in source) {
if (!source.hasOwnProperty(prop)) continue;
if (prop in target) {
if (typeof target[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (typeof source[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (target[prop].concat && source[prop].concat) {
target[prop] = target[prop].concat(source[prop]);
} else {
target[prop] = deepMerge(target[prop], source[prop]);
}
}
}
} else {
target[prop] = source[prop];
}
}
return target;
}
export default deepMerge;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+48
View File
@@ -0,0 +1,48 @@
// 此库来自 https://www.uviewui.com/js/intro.html
// 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
// this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
export default function getParent(name, keys) {
let parent = this.$parent;
// 通过while历遍,这里主要是为了H5需要多层解析的问题
while (parent) {
// 父组件
if (parent.$options?.name !== name) {
// 如果组件的name不相等,继续上一级寻找
parent = parent.$parent;
} else {
let data = {};
// 判断keys是否数组,如果传过来的是一个数组,那么直接使用数组元素值当做键值去父组件寻找
if(Array.isArray(keys)) {
keys.map(val => {
data[val] = parent[val] ? parent[val] : '';
})
} else {
// 历遍传过来的对象参数
for(let i in keys) {
// 如果子组件有此值则用,无此值则用父组件的值
// 判断是否空数组,如果是,则用父组件的值,否则用子组件的值
if(Array.isArray(keys[i])) {
if(keys[i].length) {
data[i] = keys[i];
} else {
data[i] = parent[i];
}
} else if(keys[i].constructor === Object) {
// 判断是否对象,如果是对象,且有属性,那么使用子组件的值,否则使用父组件的值
if(Object.keys(keys[i]).length) {
data[i] = keys[i];
} else {
data[i] = parent[i];
}
} else {
// 只要子组件有传值,即使是false值,也是“传值”了,也需要覆盖父组件的同名参数
data[i] = (keys[i] || keys[i] === false) ? keys[i] : parent[i];
}
}
}
return data;
}
}
return {};
}
+19
View File
@@ -0,0 +1,19 @@
// 使用时,node = this.$parent;
// 根据给定的父组件名,寻找它的上一级。
export default function getParentAls(name,node) {
let parent = node;
while (parent) {
// 父组件
if (parent.$options?.name !== name) {
// 如果组件的name不相等,继续上一级寻找
parent = parent.$parent;
} else {
return parent;
}
}
return undefined;
}
+18
View File
@@ -0,0 +1,18 @@
// 使用时,node = this.$parent;
export default function getParentAttr(name, keys,node) {
let parent = node;
while (parent) {
// 父组件
if (parent.$options?.name !== name) {
// 如果组件的name不相等,继续上一级寻找
parent = parent.$parent;
} else {
return parent[keys];
}
}
return undefined;
}
+41
View File
@@ -0,0 +1,41 @@
/**
* 本算法来源于简书开源代码,详见:https://www.jianshu.com/p/fdbf293d0a85
* 全局唯一标识符(uuidGlobally Unique Identifier,也称作 uuid(Universally Unique IDentifier)
* 一般用于多个组件之间,给它一个唯一的标识符,或者v-for循环的时候,如果使用数组的index可能会导致更新列表出现问题
* 最可能的情况是左滑删除item或者对某条信息流"不喜欢"并去掉它的时候,会导致组件内的数据可能出现错乱
* v-for的时候,推荐使用后端返回的id而不是循环的index
* @param {Number} len uuid的长度
* @param {Boolean} firstU 将返回的首字母置为"u"
* @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
*/
function guid(len = 32, firstU = true, radix = null) {
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [];
radix = radix || chars.length;
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
} else {
let r;
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift();
return 'u' + uuid.join('');
} else {
return uuid.join('');
}
}
export default guid;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+385
View File
@@ -0,0 +1,385 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s)
{
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data)
{
var bkey = rstr2binl(key);
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input)
{
try { hexcase } catch(e) { hexcase=0; }
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var output = "";
var x;
for(var i = 0; i < input.length; i++)
{
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F)
+ hex_tab.charAt( x & 0x0F);
}
return output;
}
/*
* Convert a raw string to a base-64 string
*/
function rstr2b64(input)
{
try { b64pad } catch(e) { b64pad=''; }
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var output = "";
var len = input.length;
for(var i = 0; i < len; i += 3)
{
var triplet = (input.charCodeAt(i) << 16)
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
}
}
return output;
}
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any(input, encoding)
{
var divisor = encoding.length;
var i, j, q, x, quotient;
/* Convert to an array of 16-bit big-endian values, forming the dividend */
var dividend = Array(Math.ceil(input.length / 2));
for(i = 0; i < dividend.length; i++)
{
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. All remainders are stored for later
* use.
*/
var full_length = Math.ceil(input.length * 8 /
(Math.log(encoding.length) / Math.log(2)));
var remainders = Array(full_length);
for(j = 0; j < full_length; j++)
{
quotient = Array();
x = 0;
for(i = 0; i < dividend.length; i++)
{
x = (x << 16) + dividend[i];
q = Math.floor(x / divisor);
x -= q * divisor;
if(quotient.length > 0 || q > 0)
quotient[quotient.length] = q;
}
remainders[j] = x;
dividend = quotient;
}
/* Convert the remainders to the output string */
var output = "";
for(i = remainders.length - 1; i >= 0; i--)
output += encoding.charAt(remainders[i]);
return output;
}
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
function str2rstr_utf8(input)
{
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
/* Decode utf-16 surrogate pairs */
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
/* Encode output as utf-8 */
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}
/*
* Encode a string as utf-16
*/
function str2rstr_utf16le(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF);
return output;
}
function str2rstr_utf16be(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
input.charCodeAt(i) & 0xFF);
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input)
{
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
return output;
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input)
{
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
return output;
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
module.exports = {
md5 : function(str){
return hex_md5(str);
}
}
+53
View File
@@ -0,0 +1,53 @@
export function setVueTiflyThemeBlack() {
let vueTifly_black = this.$tm.vx.state().tmVuetify.black
if (vueTifly_black === true) {
uni.setTabBarStyle({
backgroundColor: "#212121"
})
} else {
uni.setTabBarStyle({
backgroundColor: "#FFFFFF"
})
}
}
// ...mapMutations(['setTmVuetifyColor', 'setTmVuetifyBlack']),
// 检测提供的字符串是否是颜色值还是颜色主题。true,表示颜色主题名称。否则为false.
export function $TestColor(color) {
if (typeof color !== 'string') return false;
if (color.indexOf('rgb') > -1 || color.indexOf('rgba') > -1 || color.indexOf('#') > -1) {
return {
theme: false,
color: color
};
} else {
return {
theme: true,
color: color
};
}
}
// 检查给定的值。如果是带有vw,vh,rem,em,upx,rpx,%则返回.如果是px,或者45数字,则转换为upx单位的数值。
export function $TestUnit(n) {
if (typeof n !== 'string' && typeof n !== 'number') return 0;
if (typeof n === 'number') return {
type: 'number',
value: uni.upx2px(n)
};
let reg = /(vw|vh|rem|em|\%|upx|rpx|auto|px)/g;
if (reg.test(n)) {
return {
type: 'string',
value: n
};
}
let num = parseFloat(n);
if (isNaN(n)) return 0;
return {
type: 'number',
value: uni.upx2px(n)
};
}
+11
View File
@@ -0,0 +1,11 @@
// 把对象转换为string,提供对象 和 分割符。
function objToString(obj, split=';') {
if(typeof obj !=="object") return '';
let a='';
for(let i in obj){
a+=i+':'+obj[i]+split;
}
return a;
}
module.exports.objToString = objToString;
File diff suppressed because one or more lines are too long
+53
View File
@@ -0,0 +1,53 @@
/**
* 上传文件。
* 作者:tmzdy
* 时间:‎2021‎年‎7‎月‎28‎日,‏9:14:53
* 联系:zhongjihan@sina.com
* 预览图片。
* @param {Object} url 必填 当前预览的图片链接。
* @param {Object} list 可以是url数组,也可以是对象,数据比如:["http:url"] or [{url:"https:url",...}]
* @param {Object} rangKey 如果list是对象数组,需要提供url字段。
*/
function previewImg(url,list,rangKey){
if(!url){
uni.$tm.toast("参数有误");
return;
}
if(arguments.length==1){
uni.previewImage({
current:url,
urls:list?list:[url]
})
}else if(arguments.length===3){
if(typeof list[0] === 'object' && typeof list[0] !== 'undefined'){
let urls = [];
list.forEach(item=>{
urls.push(item[rangKey]);
})
uni.previewImage({
current:url,
urls:urls,
fail: (er) => {
console.warn(er)
}
})
}else if(typeof list[0] === 'string'){
uni.previewImage({
current:url,
urls:list
})
}
}else{
uni.$tm.toast("参数有误");
}
}
export default previewImg
+10
View File
@@ -0,0 +1,10 @@
function random(min, max) {
if (min >= 0 && max > 0 && max >= min) {
let gab = max - min + 1;
return Math.floor(Math.random() * gab + min);
} else {
return 0;
}
}
export default random;
+7
View File
@@ -0,0 +1,7 @@
// 打乱数组
function randomArray(array = []) {
// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
return array.sort(() => Math.random() - 0.5);
}
export default randomArray
+388
View File
@@ -0,0 +1,388 @@
/**
* 签名版,钢笔效果
* 源参考:https://www.cnblogs.com/fangsmile/p/14324460.html
*/
class Point {
constructor(x, y, time) {
this.x = x;
this.y = y;
this.isControl = false;
this.time = Date.now();
this.lineWidth = 0;
this.isAdd = false;
}
}
class Line {
constructor() {
this.points = new Array();
this.changeWidthCount = 0;
this.lineWidth = 10;
}
}
class HandwritingSelf {
constructor(canvas,w,h,line_w=8,line_color='#ff0000') {
this.canvas = {width:w,height:h};
this.ctx = canvas
var context = this.ctx;
this.ctx.ellipse = function( x, y, a, b){
// ----
}
// this.points = new Array();
this.line = new Line();
this.pointLines = new Array();//Line数组
this.k = 0.5;
this.begin = null;
this.middle = null;
this.end = null;
this.preTime = null;
this.lineWidth = line_w;
this.lineColor = line_color;
this.isDown = false;
}
down(x, y) {
this.isDown = true;
this.line = new Line();
this.line.lineWidth = this.lineWidth;
let currentPoint = new Point(x, y, Date.now());
this.addPoint(currentPoint);
this.preTime = Date.now();
}
move(x, y) {
// console.log("move:",x,y)
if (this.isDown) {
let currentPoint = new Point(x, y, Date.now())
this.addPoint(currentPoint);
this.draw();
}
}
up(x, y) {
// if (e.touches.length > 0) {
let currentPoint = new Point(x, y, Date.now())
this.addPoint(currentPoint);
// }
this.draw(true);
this.pointLines.push(this.line);
this.begin = null;
this.middle = null;
this.end = null;
this.isDown = false;
}
draw(isUp = false) {
// this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.setStrokeStyle(this.lineColor)
//绘制不包含this.line的线条
this.pointLines.forEach((line, index) => {
let points = line.points;
this.ctx.beginPath();
this.ctx.ellipse(points[0].x - 1.5, points[0].y, 6, 3, Math.PI / 4, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.beginPath();
this.ctx.moveTo(points[0].x, points[0].y);
let lastW = line.lineWidth;
this.ctx.setLineWidth(line.lineWidth);
this.ctx.setLineJoin("round");
this.ctx.setLineCap( "round");
let minLineW = line.lineWidth / 4;
let isChangeW = false;
let changeWidthCount = line.changeWidthCount;
for (let i = 1; i <= points.length; i++) {
if (i == points.length) {
this.ctx.stroke();
break;
}
if (i > points.length - changeWidthCount) {
if (!isChangeW) {
this.ctx.stroke();//将之前的线条不变的path绘制完
isChangeW = true;
if (i > 1 && points[i - 1].isControl)
continue;
}
let w = (lastW - minLineW) / changeWidthCount * (points.length - i) + minLineW;
points[i - 1].lineWidth = w;
this.ctx.beginPath();//为了开启新的路径 否则每次stroke 都会把之前的路径在描一遍
// this.ctx.strokeStyle = "rgba("+Math.random()*255+","+Math.random()*255+","+Math.random()*255+",1)";
this.ctx.setLineWidth(w);
this.ctx.moveTo(points[i - 1].x, points[i - 1].y);//移动到之前的点
this.ctx.lineTo(points[i].x, points[i].y);
this.ctx.stroke();//将之前的线条不变的path绘制完
} else {
if (points[i].isControl && points[i + 1]) {
this.ctx.quadraticCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
} else if (i >= 1 && points[i - 1].isControl) {//上一个是控制点 当前点已经被绘制
} else
this.ctx.lineTo(points[i].x, points[i].y);
}
}
})
//绘制this.line线条
let points;
if (isUp)
points = this.line.points;
else
points = [...this.line.points];
//当前绘制的线条最后几个补点 贝塞尔方式增加点
let count = 0;
let insertCount = 0;
let i = points.length - 1;
let endPoint = points[i];
let controlPoint;
let startPoint;
while (i >= 0) {
if (points[i].isControl == true) {
controlPoint = points[i];
count++;
} else {
startPoint = points[i];
}
if (startPoint && controlPoint && endPoint) {//使用贝塞尔计算补点
let dis = this.z_distance(startPoint, controlPoint) + this.z_distance(controlPoint, endPoint);
let insertPoints = this.BezierCalculate([startPoint, controlPoint, endPoint], Math.floor(dis / 6) + 1);
insertCount += insertPoints.length;
var index = i;//插入位置
// 把insertPoints 变成一个适合splice的数组(包含splice前2个参数的数组)
insertPoints.unshift(index, 1);
Array.prototype.splice.apply(points, insertPoints);
//补完点后
endPoint = startPoint;
startPoint = null;
}
if (count >= 6)
break;
i--;
}
//确定最后线宽变化的点数
let changeWidthCount = count + insertCount;
if (isUp)
this.line.changeWidthCount = changeWidthCount;
//制造椭圆头
this.ctx.fillStyle = "rgba(255,20,87,1)"
this.ctx.beginPath();
this.ctx.ellipse(points[0].x - 1.5, points[0].y, 6, 3, Math.PI / 4, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.draw(true);
this.ctx.beginPath();
this.ctx.moveTo(points[0].x, points[0].y);
let lastW = this.line.lineWidth;
this.ctx.setLineWidth(this.line.lineWidth);
this.ctx.setLineJoin("round");
this.ctx.setLineCap( "round");
let minLineW = this.line.lineWidth / 4;
let isChangeW = false;
for (let i = 1; i <= points.length; i++) {
if (i == points.length) {
this.ctx.stroke();
break;
}
//最后的一些点线宽变细
if (i > points.length - changeWidthCount) {
if (!isChangeW) {
this.ctx.stroke();//将之前的线条不变的path绘制完
isChangeW = true;
if (i > 1 && points[i - 1].isControl)
continue;
}
//计算线宽
let w = (lastW - minLineW) / changeWidthCount * (points.length - i) + minLineW;
points[i - 1].lineWidth = w;
this.ctx.beginPath();//为了开启新的路径 否则每次stroke 都会把之前的路径在描一遍
// this.ctx.strokeStyle = "rgba(" + Math.random() * 255 + "," + Math.random() * 255 + "," + Math.random() * 255 + ",0.5)";
this.ctx.setLineWidth(w);
this.ctx.moveTo(points[i - 1].x, points[i - 1].y);//移动到之前的点
this.ctx.lineTo(points[i].x, points[i].y);
this.ctx.stroke();//将之前的线条不变的path绘制完
} else {
if (points[i].isControl && points[i + 1]) {
this.ctx.quadraticCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
} else if (i >= 1 && points[i - 1].isControl) {//上一个是控制点 当前点已经被绘制
} else
this.ctx.lineTo(points[i].x, points[i].y);
}
}
this.ctx.draw(true);
}
addPoint(p) {
if (this.line.points.length >= 1) {
let last_point = this.line.points[this.line.points.length - 1]
let distance = this.z_distance(p, last_point);
if (distance < 10) {
return;
}
}
if (this.line.points.length == 0) {
this.begin = p;
p.isControl = true;
this.pushPoint(p);
} else {
this.middle = p;
let controlPs = this.computeControlPoints(this.k, this.begin, this.middle, null);
this.pushPoint(controlPs.first);
this.pushPoint(p);
p.isControl = true;
this.begin = this.middle;
}
}
addOtherPoint(p1, p2, w1, w2) {
let otherPoints = new Array();
let dis = this.z_distance(p1, p2);
if (dis >= 25) {
otherPoints.push(p1);
let insertPCount = Math.floor(dis / 20);
for (let j = 0; j < insertPCount; j++) {
let insertP = new Point(p1.x + (j + 1) / (insertPCount + 1) * (p2.x - p1.x), p1.y + (j + 1) / (insertPCount + 1) * (p2.y - p1.y))
insertP.isAdd = true;
otherPoints.push(insertP);
}
otherPoints.push(p2);
}
let count = otherPoints.length;
if (count > 0) {
console.log("addOtherPoint")
debugger
let diffW = (w2 - w1) / (count - 1);
for (let i = 1; i < count; i++) {
let w = w1 + diffW * i;
this.ctx.beginPath();
this.ctx.setLineWidth(w);
this.ctx.moveTo(otherPoints[i - 1].x, otherPoints[i - 1].y);
this.ctx.lineTo(otherPoints[i].x, otherPoints[i].y)
this.ctx.stroke();
}
}
return otherPoints
}
pushPoint(p) {
//排除重复点
if (this.line.points.length >= 1 && this.line.points[this.line.points.length - 1].x == p.x && this.line.points[this.line.points.length - 1].y == p.y)
return;
this.line.points.push(p);
}
computeControlPoints(k, begin, middle, end) {
if (k > 0.5 || k <= 0)
return;
let diff1 = new Point(middle.x - begin.x, middle.y - begin.y)
let diff2 = null;
if (end)
diff2 = new Point(end.x - middle.x, end.y - middle.y)
// let l1 = (diff1.x ** 2 + diff1.y ** 2) ** (1 / 2)
// let l2 = (diff2.x ** 2 + diff2.y ** 2) ** (1 / 2)
let first = new Point(middle.x - (k * diff1.x), middle.y - (k * diff1.y))
let second = null;
if (diff2)
second = new Point(middle.x + (k * diff2.x), middle.y + (k * diff2.y))
return { first: first, second: second }
}
// W_current =
//   W_previous + min( abs(k*s - W_previous), distance * K_width_unit_change) (k * s-W_previous) >= 0
//   W_previous - min( abs(k*s - W_previous), distance * K_width_unit_change) (k * s-W_previous) < 0
//   W_current      当前线段的宽度
//   W_previous    与当前线条相邻的前一条线段的宽度
//   distance       当前线条的长度
//   w_k         设定的一个固定阈值,表示:单位距离内, 笔迹的线条宽度可以变化的最大量.
//   distance * w_k    即为当前线段的长度内, 笔宽可以相对于前一条线段笔宽的基础上, 最多能够变宽或者可以变窄多少.
z_linewidth(b, e, bwidth, step) {
if (e.time == b.time)
return bwidth;
let max_speed = 2.0;
let d = this.z_distance(b, e);
let s = d / (e.time - b.time);//计算速度
console.log("s", e.time - b.time, s)
s = s > max_speed ? max_speed : s;
// let w = (max_speed - s) / max_speed;
let w = 0.5 / s;
let max_dif = d * step;
console.log(w, bwidth, max_dif)
if (w < 0.05) w = 0.05;
if (Math.abs(w - bwidth) > max_dif) {
if (w > bwidth)
w = bwidth + max_dif;
else
w = bwidth - max_dif;
}
// printf("d:%.4f, time_diff:%lld, speed:%.4f, width:%.4f\n", d, e.t-b.t, s, w);
return w;
}
z_distance(b, e) {
return Math.sqrt(Math.pow(e.x - b.x, 2) + Math.pow(e.y - b.y, 2));
}
BezierCalculate(poss, precision) {
//维度,坐标轴数(二维坐标,三维坐标...)
let dimersion = 2;
//贝塞尔曲线控制点数(阶数)
let number = poss.length;
//控制点数不小于 2 ,至少为二维坐标系
if (number < 2 || dimersion < 2)
return null;
let result = new Array();
//计算杨辉三角
let mi = new Array();
mi[0] = mi[1] = 1;
for (let i = 3; i <= number; i++) {
let t = new Array();
for (let j = 0; j < i - 1; j++) {
t[j] = mi[j];
}
mi[0] = mi[i - 1] = 1;
for (let j = 0; j < i - 2; j++) {
mi[j + 1] = t[j] + t[j + 1];
}
}
//计算坐标点
for (let i = 0; i < precision; i++) {
let t = i / precision;
let p = new Point(0, 0);
p.isAdd = true;
result.push(p);
for (let j = 0; j < dimersion; j++) {
let temp = 0.0;
for (let k = 0; k < number; k++) {
temp += Math.pow(1 - t, number - k - 1) * (j == 0 ? poss[k].x : poss[k].y) * Math.pow(t, k) * mi[k];
}
j == 0 ? p.x = temp : p.y = temp;
}
}
return result;
}
}
export default HandwritingSelf;
+16
View File
@@ -0,0 +1,16 @@
/**
* 作者:tmzdy
* 延时操作
* @param {Number} wait = [500] 延时
*/
function sleep(wait=500){
let timid = null;
clearTimeout(timid);
return new Promise((res,rej)=>{
timid = setTimeout(function() {
res();
}, wait);
})
}
export default sleep;
+200
View File
@@ -0,0 +1,200 @@
/**
* 验证电子邮箱格式
*/
function email(value) {
return /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value);
}
/**
* 验证手机格式
*/
function mobile(value) {
return /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/.test(value)
}
/**
* 验证URL格式
*/
function url(value) {
return /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i.test(value)
}
/**
* 验证日期格式
*/
function date(value) {
return /^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$/.test(value)
}
/**
* 验证身份证号码
*/
function idCard(value) {
return /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/.test(
value)
}
/**
* 是否车牌号
*/
function carNo(value) {
// 新能源车牌
const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/;
// 旧车牌
const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/;
if (value.length === 7) {
return creg.test(value);
} else if (value.length === 8) {
return xreg.test(value);
} else {
return false;
}
}
/**
* 中文
*/
function chinese(value) {
let reg = /^[\u4e00-\u9fa5]+$/gi;
return reg.test(value);
}
/**
* 只能输入字母
*/
function letter(value) {
return /^[a-zA-Z]*$/.test(value);
}
/**
* 只能是字母或者数字
*/
function enOrNum(value) {
//英文或者数字
let reg = /^[0-9a-zA-Z]*$/g;
return reg.test(value);
}
/**
* 是否json字符串
*/
function jsonString(value) {
if (typeof value == 'string') {
try {
var obj = JSON.parse(value);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
return false;
}
/**
* 是否对象
*/
function object(value) {
return Object.prototype.toString.call(value) === '[object Object]';
}
/**
* 检查对象或者数组的值, true表示通过,没有空;false表示有空值。
* @param {Object} obj 对象
* @param {Array<String>} filter 需要排除的字段以数组提供。 对象
*/
function checkObject(obj,filter=[]){
let istrue = true;
function testObjec(obj){
if(typeof obj !=='object' || Array.isArray(obj) || obj==null ){
istrue = false;
return false;
}
for(let item in obj){
if(filter.indexOf(item)===-1){
if(typeof obj[item] ==='undefined' || obj[item] === null ){
istrue = false;
break;
return false;
}
if(typeof obj[item] === 'object'){
if(Array.isArray(obj[item])){
if(obj[item].length==0){
istrue = false;
break;
return false;
}
}else{
testObjec(obj[item]);
}
}else if(typeof obj[item] === 'string'){
if(!uni.$tm.trim(obj[item])){
istrue = false;
break;
return false;
}
}
}
}
}
testObjec(obj);
return istrue;
}
//中国邮政编码
function chinaPost(value) {
return /[1-9]\d{5}(?!\d)/.test(value)
}
//中国银行卡号
function bankCard(value) {
let p = /^([1-9]{1})(\d{15}|\d{16}|\d{18})$/;
let str = value.replace(/\s+/g, "");
return p.test(str);
}
//密码验证,只能字母字符和数字
//默认6位
function password(value,len=6) {
let p = new RegExp(`^[\w+|\-|+\*\.\`!@#\$%\^\&\(\)\_\+\,\///]{`+len+`,}$`)
return p.test(value);
}
//密码验证,只能字母字符和数字
//默认6位
//并且,大小写字母和数字必须至少要有1位。
function password2(value,len=6) {
let p = new RegExp(`^[\w+|\-|+\*\.\`!@#\$%\^\&\(\)\_\+\,\///]{`+len+`,}$`)
if(!p.test(value)) return false;
if(!/[a-z]{1,}/.test(value)) return false;
if(!/[A-Z]{1,}/.test(value)) return false;
if(!/[0-9]{1,}/.test(value)) return false;
return true;
}
export default {
email,
mobile,
url,
date,
idCard,
carNo,
chinese,
letter,
enOrNum,
jsonString,
object,
checkObject,
chinaPost,
password,
password2,
}
+32
View File
@@ -0,0 +1,32 @@
const theme = {
getTheme(){
return {
color:uni.$tm.vx.state().tmVuetify.color,
black:uni.$tm.vx.state().tmVuetify.black
}
},
setBlack(arg){
let p = arg;
if(typeof arg !=='undefined'){
uni.$tm.vx.commit("setTmVuetifyBlack",arg)
}else{
p = !uni.$tm.vx.state().tmVuetify.black;
uni.$tm.vx.commit("setTmVuetifyBlack",!uni.$tm.vx.state().tmVuetify.black)
}
uni.setStorageSync('setTmVuetifyBlack',p)
},
setTheme(arg){
let p = arg||"primary";
uni.$tm.vx.commit("setTmVuetifyColor",p)
uni.setStorageSync('setTmVuetifyColor',p)
},
clear(){
uni.$tm.vx.commit("setTmVuetifyColor",'')
uni.removeStorageSync('setTmVuetifyColor')
}
}
export default theme;
+9
View File
@@ -0,0 +1,9 @@
function toast(title, duration = 1500) {
uni.showToast({
title: title,
icon: 'none',
duration: duration
})
}
export default toast
+15
View File
@@ -0,0 +1,15 @@
function trim(str, pos = 'both') {
if (pos == 'both') {
return str.replace(/^\s+|\s+$/g, "");
} else if (pos == "left") {
return str.replace(/^\s*/, '');
} else if (pos == 'right') {
return str.replace(/(\s*$)/g, "");
} else if (pos == 'all') {
return str.replace(/\s+/g, "");
} else {
return str;
}
}
export default trim
@@ -0,0 +1,105 @@
export default class WxCanvas {
constructor(ctx, canvasId, isNew, canvasNode) {
this.ctx = ctx;
this.canvasId = canvasId;
this.chart = null;
this.isNew = isNew
if (isNew) {
this.canvasNode = canvasNode;
}
else {
this._initStyle(ctx);
}
// this._initCanvas(zrender, ctx);
this._initEvent();
}
getContext(contextType) {
if (contextType === '2d') {
return this.ctx;
}
}
// canvasToTempFilePath(opt) {
// if (!opt.canvasId) {
// opt.canvasId = this.canvasId;
// }
// return wx.canvasToTempFilePath(opt, this);
// }
setChart(chart) {
this.chart = chart;
}
attachEvent() {
// noop
}
detachEvent() {
// noop
}
_initCanvas(zrender, ctx) {
zrender.util.getContext = function () {
return ctx;
};
zrender.util.$override('measureText', function (text, font) {
ctx.font = font || '12px sans-serif';
return ctx.measureText(text);
});
}
_initStyle(ctx) {
ctx.createRadialGradient = () => {
return ctx.createCircularGradient(arguments);
};
}
_initEvent() {
this.event = {};
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown'
}, {
wxName: 'touchMove',
ecName: 'mousemove'
}, {
wxName: 'touchEnd',
ecName: 'mouseup'
}, {
wxName: 'touchEnd',
ecName: 'click'
}];
eventNames.forEach(name => {
this.event[name.wxName] = e => {
const touch = e.touches[0];
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y
});
};
});
}
set width(w) {
if (this.canvasNode) this.canvasNode.width = w
}
set height(h) {
if (this.canvasNode) this.canvasNode.height = h
}
get width() {
if (this.canvasNode)
return this.canvasNode.width
return 0
}
get height() {
if (this.canvasNode)
return this.canvasNode.height
return 0
}
}
+447
View File
@@ -0,0 +1,447 @@
import guid from './guid';
/**
* 上传文件。
* 作者:tmzdy
* 时间:‎2021‎年‎7‎月‎28‎日,‏9:14:53
* 联系:zhongjihan@sina.com
* @param {Function} chooesefile -- 选择图片上传
* @param {Function} selected -- 选择图片成功后触发。返回选择后的图片。
* @param {Function} addfile -- 动态加入预上传的文件。
* @param {Function} progress -- 进度。
* @param {Function} fail -- 失败。
* @param {Function} success -- 成功。
* @param {Function} complete -- 完成。
* @param {Function} start -- 开始上传。
* @param {Function} stop -- 停止上传。
*/
class uploadfile {
filelist = [];
isStop = false;
index = 0;
constructor({maxfile,uploadUrl,opts,responseStu,file_list,isAuto}) {
let arg = {
maxfile:9,
uploadUrl:'',
file_list:[],
isAuto:true,
opts:{},
maxsize:10*1024*1024,
code:0,//定义成功的标志码
type:'image',//文件选择的类型
extension:['*'],//后缀过滤。
responseStu:{
code:'code',//服务器返回的码的字段名称
data:'data',//服务上传成功后返回 的数据字段名称
msg:'msg'//服务器响应信息的字段名称。
},
...(arguments[0]??{})};
let ots = {
name:'file',header:{}
}//配置{name: 'file', // 上传时的文件key名。默认file,header: {}, // 上传的头部参数。}
this.config={
maxfile:arg.maxfile,
uploadUrl:arg.uploadUrl,
opts:{...ots,...arg.opts},
file_list:arg.file_list,//默认提供的图片.
maxsize:arg.maxsize,
code:arg.code,
isAuto:arg.isAuto,//自动上传
type:arg.type,//文件选择的类型
extension:arg.extension,//后缀过滤。
responseStu:{...arg.responseStu,...(responseStu||{})}
}
}
/**
* 成功后返回选择后的图片列表。
*/
async chooesefile(){
let t = this;
return new Promise((rs,rj)=>{
uni.chooseImage({
count:t.config.maxfile,
type:t.config.type,
extension:t.config.extension,
fail: (e) => {
console.error(e);
uni.$tm.toast("已取消选择");
rj(e);
},
success: (res) => {
console.log(res);
if(res.tempFilePaths.length==0){
uni.$tm.toast("未选择");
return;
}
console.log(res);
let imgarray = res.tempFilePaths;
let fielist = res.tempFiles;
let jgsk = [];
//0待上传,1上传中,2上传失败,3上传成功。4超过大小限制
imgarray.forEach((item,index)=>{
let isMaxsize = fielist[index].size>t.config.maxsize?true:false;
jgsk.push({
url:item,
status:isMaxsize?'超过大小':"待上传",
progress:isMaxsize?100:0,
fileId:guid(),
statusCode:isMaxsize?4:0,
data:null,//上传成功后的回调数据。
})
})
t.filelist.push(...jgsk)
t.selected(t.filelist);
if(t.config.isAuto){
t.start();
}
rs(t.filelist)
}
})
})
}
async chooseMPH5weixinFile(){
let t = this;
return new Promise((rs,rj)=>{
var fs = uni.chooseFile;
// #ifdef MP-WEIXIN || MP-QQ
fs = uni.chooseMessageFile;
// #endif
var config = {
count:t.config.maxfile,
type:t.config.type,
extension:t.config.extension,
}
if(!t.config.extension||!Array.isArray(t.config.extension)||t.config.extension?.length==0){
delete config.extension
}
fs({
...config,
fail: (e) => {
console.error(e);
uni.$tm.toast("已取消选择");
rj(e);
},
success: (res) => {
if(res.tempFiles.length==0){
uni.$tm.toast("未选择");
return;
}
let fielist = res.tempFiles;
let jgsk = [];
//0待上传,1上传中,2上传失败,3上传成功。4超过大小限制
fielist.forEach((item,index)=>{
let isMaxsize = fielist[index].size>t.config.maxsize?true:false;
let ftype = item.name||""
if(ftype){
ftype = ftype.substr(ftype.lastIndexOf(".")+1).toLocaleLowerCase();
}
jgsk.push({
url:item.path,
name:item.name||'默认文件名称',
type:ftype,
status:isMaxsize?'超过大小':"待上传",
progress:isMaxsize?100:0,
fileId:guid(),
statusCode:isMaxsize?4:0,
data:null,//上传成功后的回调数据。
})
})
t.filelist.push(...jgsk)
t.selected(t.filelist);
if(t.config.isAuto){
t.start();
}
rs(t.filelist)
}
})
})
}
setConfig({maxfile,uploadUrl,opts,file_list,isAuto,responseStu}){
let arg = arguments.length==0?{}:arguments[0];
this.config={...this.config,...arg}
}
// 动态加入预上传的文件。
/**
* 动态加入文件
* @param {Object} filelist
*/
addfile(filelist){
if(typeof filelist !=='object'&&!Array.isArray(filelist)) return;
this.filelist.push(...filelist)
}
// 选择图片成功后触发。返回选择后的图片。
selected(filelist){}
// 进度。
progress(item){}
// 失败
fail(item){}
// 成功
success(item){}
// 完成。
complete (filelist){}
// 开始上传。
start(){
if(this.filelist.length<=0){
uni.$tm.toast("未选择图片");
return;
}
let t = this;
// t重新开始上传从头开始。
this.index = 0;
this.isStop = false;
function startupload(){
if(t.isStop) return;
let item = t.filelist[t.index];
if(!item || typeof item === 'undefined'){
// 文件不存在。直接结束。
t.complete(t.filelist);
return;
}
if(item.statusCode==3||item.statusCode==1||item.statusCode==4){
// 直接跳过。至下一个文件。
t.index++;
startupload();
return;
}
const upObj = uni.uploadFile({
url:t.config.uploadUrl,
name:t.config.opts?.name??'file',
header:t.config.opts?.header??{},
filePath:item.url,
formData:{file_name:item.name},
success:(res)=>{
if(res.statusCode !=200){
item.statusCode = 2;
item.status = "上传失败";
uni.$tm.toast(String(res.statusCode))
t.fail(item)
t.index++;
return;
}
let jsd={};
let isOk = true;
// 是否从服务器返回的是json。如果不是则表示fasle为string.
let isJsonCallbackData = true;
try{
jsd = JSON.parse(res.data);
}catch(e){
isJsonCallbackData=false;
jsd = res.data;
item.data = res.data;
}
if(isJsonCallbackData){
try{
item.data = jsd[t.config.responseStu.data];
if(typeof item.data == 'object'){
item.data['name'] = item.name;
item.data['id'] = item['id']||"";
}
let itecode = jsd[t.config.responseStu.code]
if(itecode!==t.config.code){
isOk = false;
}
}catch(e){
isOk = false;
}
}
if(!isOk){
uni.$tm.toast(jsd[t.config.responseStu.msg]||"失败")
item.statusCode = 2;
item.status = "上传失败";
t.fail(item)
t.index++;
return;
}
// 上传成功。
item.statusCode = 3;
item.status = "上传成功";
uni.$tm.toast("上传成功")
// t.filelist[t.index] = item;
// t.filelist.splice(t.index,1,item)
t.success(item)
},
fail:(res)=>{
uni.$tm.toast(res.errMsg)
item.statusCode = 2;
item.status = "上传失败";
// t.filelist[t.index] = item;
t.fail(item)
t.index++;
},
complete:(res)=>{
// 直接下一个文件。
startupload();
}
})
if(upObj){
upObj.onProgressUpdate((res)=>{
t.filelist[t.index].statusCode = 1;
t.filelist[t.index].status = "上传中";
t.filelist[t.index].progress = res.progress;
// t.filelist[t.index] = item;
t.progress(item)
})
}
}
startupload();
}
// 停止上传
stop(){
this.isStop = true;
}
}
/**
* 上传文件。
* 作者:tmzdy
* 时间:‎2021‎年‎7‎月‎28‎日,‏9:14:53
* 联系:zhongjihan@sina.com
* 选择图片上传,相册或者拍照。
* @param {Number} maxfile 最大上传的文件数量,默认为 9 ;
* @param {String} uploadUrl -- ""
* @param {Object} opts -- {}
* @param {Function} progress {} --上传中调用
* @param {Function} success {} --上传成功才会调用。
* @param {Function} selected {} --选完图片待上传调用。
* @param {Function} fail {} --上传失败时调用,返回文件相关
* @param {Function} complete {} -- 完成上传时触发,失败与成功都触发。
*/
function chooseImgUpload(maxfile=9,uploadUrl="",opts={},progress,success,selected,fail,complete){
uni.chooseImage({
count:maxfile,
fail: (e) => {
uni.$tm.toast("用户取消选择图片");
},
success: (res) => {
if(res.tempFilePaths.length==0){
uni.$tm.toast("未选择图片");
return;
}
let imgarray = res.tempFilePaths;
let jgsk = [];
//0待上传,1上传中,2上传失败,3上传成功。
imgarray.forEach((item,index)=>{
jgsk.push({
url:item,
status:"待上传",
progress:0,
fileId:guid(),
statusCode:0,
data:null,//上传成功后的回调数据。
})
})
if(selected){
selected(jgsk);
}
let index = 0;
function startupload(){
let item = jgsk[index];
if(!item){
// 文件不存在。直接结束。
if(complete){
complete(jgsk);
}
return;
}
if(item.statusCode==2||item.statusCode==1){
// 直接跳过。至下一个文件。
index++;
startupload();
}
const upObj = uni.uploadFile({
url:uploadUrl,
name:opts?.name??'file',
header:opts?.header??{},
filePath:item.url,
success:(res)=>{
if(res.statusCode !=200){
item.statusCode = 2;
item.status = "上传失败";
uni.$tm.toast(res.errMsg)
if(fail){
fail(item)
}
return;
}
try{
item.data = JSON.parse(res.data).data;
}catch(e){
item.statusCode = 2;
item.status = "上传失败";
uni.$tm.toast(res.errMsg)
if(fail){
fail(item)
}
return;
}
// 上传成功。
item.statusCode = 3;
item.status = "上传成功";
item.data = JSON.parse(res.data).data;
uni.$tm.toast("上传成功")
if(success){
success(item)
}
},
fail:(res)=>{
uni.$tm.toast(res.errMsg)
item.statusCode = 2;
item.status = "上传失败";
if(fail){
fail(item)
}
},
complete:(res)=>{
// 直接下一个文件。
index++;
startupload();
}
})
if(upObj){
upObj.onProgressUpdate((res)=>{
item.statusCode = 1;
item.status = "上传中";
item.progress = res.progress
if(progress){
progress(item)
}
})
}
}
startupload();
}
})
}
export default {
chooseImgUpload,uploadfile
}
+234
View File
@@ -0,0 +1,234 @@
// #ifdef H5
var clipboardJS = require('./clipboardJS');
// #endif
/**
* 预览图片。
@param {Object} url 必填 当前预览的图片链接。
@param {Object} list 可以是url数组,也可以是对象,数据比如:["http:url"] or [{url:"https:url",...}]
@param {Object} rangKey 如果list是对象数组,需要提供url字段。
*/
import { previewImg } from "./preview.js"
/**
* 数据分组
* @param {Array} oArr - 原数组列表
* @param {Number} length - 单个数组长度
* @return {Array} arr - 分组后的新数组
*/
function splitData(oArr = [], length = 1) {
let arr = [];
let minArr = [];
oArr.forEach(c => {
if (minArr.length === length) {
minArr = [];
}
if (minArr.length === 0) {
arr.push(minArr);
}
minArr.push(c);
});
return arr;
}
/**
* 剩余时间格式化
* @param {Number} t - 剩余多少秒
* @return {Object} format - 格式后的天时分秒对象
*/
function timeMuch(t) {
let format = {
d: '00',
h: '00',
m: '00',
s: '00'
};
if (t > 0) {
let d = Math.floor(t / 86400);
let h = Math.floor((t / 3600) % 24);
let m = Math.floor((t / 60) % 60);
let s = Math.floor(t % 60);
format.d = d < 10 ? '0' + d : d;
format.h = h < 10 ? '0' + h : h;
format.m = m < 10 ? '0' + m : m;
format.s = s < 10 ? '0' + s : s;
}
return format;
}
/**
* 打电话
* @param {String<Number>} phoneNumber - 数字字符串
* @return {Promise}
*/
function callPhone(phoneNumber = '') {
let num = phoneNumber.toString()
return new Promise((rs,rj)=>{
uni.makePhoneCall({
phoneNumber: num,
success:()=> rs(),
fail:(err)=> rj(err)
});
})
}
/**
* 调起客户端相机扫码。
* @param {Boolean} onlyFromCamera true 是否只允许相机扫码识别
* @param {Array<string>} scanType ['barCode', 'qrCode', 'datamatrix','datamatrix']
* @returns Promise 成功返回相关数据结构
*/
function scanCode(onlyFromCamera = true, scanType = ['barCode', 'qrCode', 'datamatrix','datamatrix']){
// #ifdef H5
return Promise.reject('不支持H5');
// #endif
return new Promise((rs,rj)=>{
uni.scanCode({
onlyFromCamera: onlyFromCamera,
scanType: scanType,
success: (res) => rs(res),
fail:(error)=>rj(error)
});
})
}
/**
* 设置剪切板内容。
* @param {String} data
* @returns Promise true/false
*/
function setClipboardData(data){
// #ifndef H5
return new Promise((rs,rj)=>{
uni.setClipboardData({
data: data,
success:()=>rs(true),
fail:(error)=>rj(error)
});
})
// #endif
// #ifdef H5
return new Promise((rs,rj)=>{
let btn = document.createElement('button');
btn.style.display = 'none';
btn.className='hi-test-hi';
document.body.appendChild(btn);
clipboardJS = clipboardJS.bind(window);
let cb = new clipboardJS('.hi-test-hi', {
text: () => data
})
cb.on('success', function (res) {
rs(true);
})
cb.on('error', function (err) {
rj(err)
})
btn.click = btn.click.bind(window.document.body.querySelector('.hi-test-hi'))
btn.click()
})
// #endif
}
/**
* 获取剪切板内容
* @returns Promise 剪切板内容
*/
function getClipboardData(){
// #ifndef H5
return new Promise((rs, rj) => {
uni.getClipboardData({
success: (res) => rs(res.data),
fail: (error) => rj(error)
});
})
// #endif
// #ifdef H5
return Promise.reject('H5无法获取剪切板内容')
// #endif
}
/**
* 设置cookie数据
* @param {String} key 键值
* @param {String} data 值
* @returns Boolean
*/
function setCookie(key, data) {
try {
uni.setStorageSync(key, data);
return true;
} catch (e) {
return false;
}
}
/**
* 删除一个本地cookie
* @param {String} key 键值
* @returns Boolean
*/
function delCookie(key) {
try {
uni.removeStorageSync(key);
return true;
} catch (e) {
return false;
}
}
/**
* 获取一个cookie数据
* 如果存入的是对象,返回的也是对象。如果是string返回的也是字符串。
* @param {String} key 键
* @returns json/string
*/
function getCookie(key) {
try {
const value = uni.getStorageSync(key);
try {
let val = JSON.parse(value)
return val;
} catch (e) {
return value;
}
} catch (e) {
return undefined;
}
}
/**
* 向地址连接追加参数。
* @param {string} uri 网址
* @param {string} key 字段
* @param {string} value 字段值
* @returns
*/
function httpUrlAddKey(uri, key, value) {
if (!value) {
return uri;
}
var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
var separator = uri.indexOf("?") !== -1 ? "&" : "?";
if (uri.match(re)) {
return uri.replace(re, "$1" + key + "=" + value + "$2");
} else {
return uri + separator + key + "=" + value;
}
}
export default {
previewImg,//预览图片。
splitData,//数据分组
timeMuch,//剩余时间格式化
callPhone,//打电话
scanCode,//调起客户端相机扫码。
setClipboardData, //设置剪切板内容。
getClipboardData,//获取剪切板内容
setCookie,//设置cookie数据
delCookie,//删除一个本地cookie
getCookie,//获取一个cookie数据
httpUrlAddKey,//向地址连接追加参数
}
+63
View File
@@ -0,0 +1,63 @@
/*
* 操作全局Vuex。
* 作者:tmzdy
* 时间:‎2021‎年‎10‎月‎14‎日
* 联系:zhongjihan@sina.com
*
*/
class vuex {
constructor(store) {
this.store = store;
}
//链式调用
state(){
return this.store.state;
}
//链式调用
getters(){
let t = this;
const g = this.store.getters
let keys = Object.keys(g);
let k = keys.map((el,index)=>{
let f = el.split('/');
let tst = {}
if(f.length==1){
tst[el]=g[el]
}else{
tst[f[0]]={}
tst[f[0]][f[1]] = g[el]
}
return tst
})
let rulst = {};
k.forEach(el=>{
rulst = {...rulst,...el}
})
return rulst;
}
commit(funName,arg){
try{
this.store.commit(funName,arg);
}catch(e){
console.error("未发现函数方法:"+funName)
}
}
actions(funName,arg){
try{
return this.store.dispatch(funName,arg);
}catch(e){
console.error("未发现函数方法:"+funName)
}
}
//获得原始vuex对象。
getVuex(){
return this.store;
}
}
export default vuex;
+50
View File
@@ -0,0 +1,50 @@
import util from './util';
/**
* 微信小程序分享功能。
*/
var cfg = function(){
let config = {
...(this.$tm.vx.store.state.tmVuetify.wxshareConfig_miniMp || {})
};
if (typeof config.query !== 'object') config.query = {};
//获取当前路径。
if(!config.path){
let cur = getCurrentPages();
config.path = cur[cur.length-1].route;
config.path = (config.path[0]=='/'?'' : '/') + config.path;
config.copyLink = config.path;
}
// util.httpUrlAddKey
let query = ''
for (const key in config.query) {
if (config.hasOwnProperty.call(config.query, key)) {
const element = config.query[key];
query = util.httpUrlAddKey(query,key,element)
}
}
config.copyLink = config.path = config.path+query;
config.query = query;
return config;
}
var sharebywx = {
onShareAppMessage() {
let cg = cfg.call(this)||{};
return { ...cg}
},
onShareTimeline() {
let cg = cfg.call(this) || {};
return { ...cg }
}
}
export default { sharebywx }