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) { let data = baseData.map(i=>({x:i.x,y:i.y})) // y 軸斜率計算請參考 ./public/timeFrameSlope 的圖 // 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) // 最大值: (f - b)(e - c) = (f - d)(e - a),求 f = (be - bc -de + da) / (a - c) // y 軸最小值 let a = 0; let b; let c = 1; let d = baseData[0].y; let e = 2; let f = baseData[1].y; b = (e*d - a*d - f*a - f*c) / (e - c - a) if(isPercent) { b = b >= 1 ? 1 : b <= 0 ? 0 : b; } else { b = b >= yMax ? yMax : b <= yMin ? yMin : b; } // y 軸最大值 let ma = 9; let mb = baseData[8].y; let mc = 10; let md = baseData[9].y; let me = 11; let mf; mf = (mb*me - mb*mc -md*me + md*ma) / (ma - mc); if(isPercent) { mf = mf >= 1 ? 1 : mf <= 0 ? 0 : mf; } else { mf = mf >= yMax ? yMax : mf <= yMin ? yMin : mf; } // 添加最小值 // The unshift() method of Array instances adds the specified elements to // the beginning of an array and returns the new length of the array. data.unshift({ x: xMin, y: b, }) // 添加最大值 data.push({ x: xMax, y: mf, }) return data; }; /** * 將後端的 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 closestElement = data[0]; // 假定第一个元素是最接近的 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) { closestElement = data[i]; 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; }