247 lines
6.9 KiB
JavaScript
247 lines
6.9 KiB
JavaScript
import getMoment from 'moment';
|
||
|
||
/**
|
||
* 將後端的 chart data 加入最大、最小值,折線圖
|
||
* @param {array} baseData 後端給的 10 個時間點
|
||
* @param {number} xMax 2022-05-23T18:00:00
|
||
* @param {number} xMin 2022-05-23T08:00:00
|
||
* @param {boolean} isPercent 是否以百分比顯示
|
||
* @param {number} yMax case
|
||
* @param {number} yMin case
|
||
* @returns {array} 可直接套入 chart.js 的 data
|
||
*/
|
||
export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
|
||
// 將 baseData 轉換為包含 x 和 y 屬性的物件陣列
|
||
let data = baseData.map(i => ({ x: i.x, y: i.y }));
|
||
|
||
// 計算 y 軸最小值
|
||
let b = calculateYMin(baseData, isPercent, yMin, yMax);
|
||
|
||
// 計算 y 軸最大值
|
||
let mf = calculateYMax(baseData, isPercent, yMin, yMax);
|
||
|
||
// 添加最小值
|
||
data.unshift({
|
||
x: xMin,
|
||
y: b,
|
||
});
|
||
|
||
// 添加最大值
|
||
data.push({
|
||
x: xMax,
|
||
y: mf,
|
||
});
|
||
|
||
return data;
|
||
};
|
||
/**
|
||
* 計算 y 軸最小值
|
||
*
|
||
* x 值為 0 ~ 11,
|
||
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
|
||
* 最小值: (f - b)(c - a) = (e - a)(d - b),求 b = (ed - ad - fa - fc) / (e - c - a)
|
||
*/
|
||
function calculateYMin(baseData, isPercent, yMin, yMax) {
|
||
let a = 0;
|
||
let c = 1;
|
||
let d = baseData[0].y;
|
||
let e = 2;
|
||
let f = baseData[1].y;
|
||
let b = (e * d - a * d - f * a - f * c) / (e - c - a);
|
||
return clampValue(b, isPercent, yMin, yMax);
|
||
};
|
||
/**
|
||
* 計算 y 軸最大值
|
||
*
|
||
* x 值為 9 ~ 11,
|
||
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
|
||
* 最大值: (f - b)(e - c) = (f - d)(e - a),求 f = (be - bc -de + da) / (a - c)
|
||
*/
|
||
function calculateYMax(baseData, isPercent, yMin, yMax) {
|
||
let ma = 9;
|
||
let mb = baseData[8].y;
|
||
let mc = 10;
|
||
let md = baseData[9].y;
|
||
let me = 11;
|
||
let mf = (mb * me - mb * mc - md * me + md * ma) / (ma - mc);
|
||
return clampValue(mf, isPercent, yMin, yMax);
|
||
};
|
||
/**
|
||
* 將值限制在指定範圍內
|
||
* 如果 isPercent 為 true,則將值限制在 0 到 1 之間
|
||
* 否則,將值限制在 yMin 和 yMax 之間
|
||
*
|
||
* @param {number} value 需要被限制的值
|
||
* @param {boolean} isPercent 如果為 true,表示值應該被限制在百分比範圍內(0 到 1)
|
||
* @param {number} min 非百分比情況下的最小允許值(yMin)
|
||
* @param {number} max 非百分比情況下的最大允許值(yMax)
|
||
* @returns {number} 被限制在指定範圍內的值
|
||
*/
|
||
function clampValue(value, isPercent, min, max) {
|
||
if (isPercent) {
|
||
if (value >= 1) {
|
||
return 1;
|
||
} else if (value <= 0) {
|
||
return 0;
|
||
}
|
||
} else {
|
||
if (value >= max) {
|
||
return max;
|
||
}
|
||
if (value <= min) {
|
||
return min;
|
||
}
|
||
}
|
||
return value;
|
||
};
|
||
/**
|
||
* 將後端的 chart data x 值轉換時間格式,長條圖
|
||
* @param {array} baseData 後端給的 10 個時間點
|
||
* @returns {array} 可直接套入 chart.js 的 data
|
||
*/
|
||
export function setBarChartData(baseData) {
|
||
let data = baseData.map(i =>{
|
||
return {
|
||
x: getMoment(i.x).format('YYYY/M/D hh:mm:ss'),
|
||
y: i.y
|
||
}
|
||
})
|
||
return data
|
||
};
|
||
/**
|
||
* 將一段時間均分成多段的時間點
|
||
* @param {string} startTime 開始時間(ex: 434) 總秒數
|
||
* @param {string} endTime 結束時間(ex: 259065) 總秒數
|
||
* @param {number} amount 切分成多少段
|
||
* @returns {array} 均分成多段的時間 array
|
||
*/
|
||
export function timeRange(minTime, maxTime, amount) {
|
||
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
|
||
const startTime = minTime;
|
||
const endTime = maxTime;
|
||
let timeRange = []; // return數據初始化
|
||
const timeGap = (endTime - startTime) / (amount - 1); // 切分成多少段
|
||
|
||
for (let i = 0; i < amount; i++) {
|
||
timeRange.push(startTime + timeGap * i);
|
||
}
|
||
timeRange = timeRange.map(value => Math.round(value));
|
||
return timeRange;
|
||
};
|
||
/**
|
||
* 將 y 軸的值分割成跟 x 時間軸一樣的等份,使用統計學 R 語言算出貝茲曲線攻勢
|
||
* @param {array} data 切分成多少段
|
||
* @param {number} yAmount 切分成多少段
|
||
* @param {number} yMax y 最大值
|
||
* @returns {array} 均分成多段的時間 array
|
||
*/
|
||
export function yTimeRange(data, yAmount, yMax) {
|
||
const yRange = [];
|
||
const yGap = (1/ (yAmount-1));
|
||
|
||
// 貝茲曲線公式
|
||
const threebsr = function (t, a1, a2, a3, a4) {
|
||
return (
|
||
(1 - t) * (1 - t) * (1 - t) * a1 +
|
||
3 * t * (1 - t)* (1 - t) * a2 +
|
||
3 * t * t * (1 - t) * a3 +
|
||
t * t * t * a4
|
||
)
|
||
};
|
||
|
||
for (let j = 0; j < data.length - 1; j++) {
|
||
for (let i = 0; i <= 1; i += yGap*11) {
|
||
yRange.push(threebsr(i, data[j].y, data[j].y, data[j + 1].y, data[j + 1].y));
|
||
}
|
||
}
|
||
|
||
if(yRange.length < yAmount) {
|
||
let len = yAmount - yRange.length;
|
||
for (let i = 0; i < len; i++) {
|
||
yRange.push(yRange[yRange.length - 1]);
|
||
}
|
||
}
|
||
else if(yRange.length > yAmount) {
|
||
let len = yRange.length - yAmount;
|
||
for(let i = 0; i < len; i++) {
|
||
yRange.splice(1, 1);
|
||
}
|
||
}
|
||
|
||
return yRange;
|
||
};
|
||
/**
|
||
* 找出選擇的時間點的 index
|
||
* @param {array} data xVal
|
||
* @param {number} xValue x 值
|
||
* @returns {numver} x index
|
||
*/
|
||
export function getXIndex(data, xValue) {
|
||
let closestIndex = xValue; // 假定第一个元素的索引是 0
|
||
let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值
|
||
|
||
for (let i = 0; i < data.length; i++) {
|
||
let difference = Math.abs(xValue - data[i]);
|
||
|
||
if (difference <= smallestDifference) {
|
||
closestIndex = i;
|
||
smallestDifference = difference;
|
||
}
|
||
}
|
||
|
||
return closestIndex;
|
||
};
|
||
/**
|
||
* 有 dhms 表達的時間單位
|
||
* @param {number} seconds 總秒數
|
||
* @returns {string}
|
||
*/
|
||
export function formatTime(seconds) {
|
||
if(!isNaN(seconds)) {
|
||
const remainingSeconds = seconds % 60;
|
||
const minutes = (Math.floor(seconds - remainingSeconds) / 60) % 60;
|
||
const hours = (Math.floor(seconds / 3600)) % 24;
|
||
const days = Math.floor(seconds / (3600 * 24));
|
||
|
||
let result = '';
|
||
if (days > 0) {
|
||
result += `${days}d`;
|
||
}
|
||
if (hours > 0) {
|
||
result += `${hours}h`;
|
||
}
|
||
if (minutes > 0) {
|
||
result += `${minutes}m`;
|
||
}
|
||
result += `${remainingSeconds}s`;
|
||
|
||
return result.trim(); // 去除最后一个空格
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
/**
|
||
* 只顯示最大的兩個單位
|
||
* @param {array} times 時間
|
||
* @returns {array}
|
||
*/
|
||
export function formatMaxTwo(times) {
|
||
const formattedTimes = [];
|
||
for (let time of times) {
|
||
let units = time.match(/\d+[dhms]/g); // 匹配數字和單位(天、小時、分鐘、秒)
|
||
let formattedTime = '';
|
||
let count = 0;
|
||
|
||
// 只保留最大的兩個單位
|
||
for (let unit of units) {
|
||
if (count >= 2) {
|
||
break;
|
||
}
|
||
formattedTime += unit + ' ';
|
||
count++;
|
||
}
|
||
formattedTimes.push(formattedTime.trim()); // 去除末尾的空格
|
||
}
|
||
return formattedTimes;
|
||
}
|