如何使用HTML5或Angular8实现持续时间选择器,小时数超过24小时?



我正在尝试实现一个控件,使用

<input type="time"/>

或只是

<input type="text"/>

并实现一个持续时间选取器控件,该控件的小时格式可以超过 24,类似于 000:00:00 或 hhh:mm:ss,并且没有 am/pm 选项(时间的默认输入类型具有 am/pm 格式的格式,这在我的情况下没有用)。 要求是能够使用向上和向下键增加减少持续时间,就像 HTML 的默认输入类型时间一样。

是否有任何原生HTMLangular或材料成分? 或者有没有办法使用正则表达式/模式或其他东西来实现这一点?

我能想到的一种方法是编写自定义控件(如@Allabakash所述)。对于本机 HTML,控件可以是这样的:

window.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('[my-duration-picker]').forEach(picker => {
//prevent unsupported keys
const acceptedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp'];
const selectFocus = event => {
//get cursor position and select nearest block;
const cursorPosition = event.target.selectionStart;
"000:00:00" //this is the format used to determine cursor location
const hourMarker = event.target.value.indexOf(":");
const minuteMarker = event.target.value.lastIndexOf(":");
if (hourMarker < 0 || minuteMarker < 0) {
//something wrong with the format. just return;
return;
}
if (cursorPosition < hourMarker) {
event.target.selectionStart = 0; //hours mode
event.target.selectionEnd = hourMarker;
}
if (cursorPosition > hourMarker && cursorPosition < minuteMarker) {
event.target.selectionStart = hourMarker + 1; //minutes mode
event.target.selectionEnd = minuteMarker;
}
if (cursorPosition > minuteMarker) {
event.target.selectionStart = minuteMarker + 1; //seconds mode
event.target.selectionEnd = minuteMarker + 3;
}
}
const insertFormatted = (inputBox, secondsValue) => {
let hours = Math.floor(secondsValue / 3600);
secondsValue %= 3600;
let minutes = Math.floor(secondsValue / 60);
let seconds = secondsValue % 60;
minutes = String(minutes).padStart(2, "0");
hours = String(hours).padStart(3, "0");
seconds = String(seconds).padStart(2, "0");
inputBox.value = hours + ":" + minutes + ":" + seconds;
}
const increaseValue = inputBox => {
const rawValue = inputBox.value;
sectioned = rawValue.split(':');
let secondsValue = 0
if (sectioned.length === 3) {
secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
}
secondsValue += 1;
insertFormatted(inputBox, secondsValue);
}
const decreaseValue = inputBox => {
const rawValue = inputBox.value;
sectioned = rawValue.split(':');
let secondsValue = 0
if (sectioned.length === 3) {
secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
}
secondsValue -= 1;
if (secondsValue < 0) {
secondsValue = 0;
}
insertFormatted(inputBox, secondsValue);
}
const validateInput = event => {
sectioned = event.target.value.split(':');
if (sectioned.length !== 3) {
event.target.value = "000:00:00"; //fallback to default
return;
}
if (isNaN(sectioned[0])) {
sectioned[0] = "000";
}
if (isNaN(sectioned[1]) || sectioned[1] < 0) {
sectioned[1] = "00";
}
if (sectioned[1] > 59 || sectioned[1].length > 2) {
sectioned[1] = "59";
}
if (isNaN(sectioned[2]) || sectioned[2] < 0) {
sectioned[2] = "00";
}
if (sectioned[2] > 59 || sectioned[2].length > 2) {
sectioned[2] = "59";
}
event.target.value = sectioned.join(":");
}
const controlsDiv = document.createElement("div");
const scrollUpBtn = document.createElement("button");
const scrollDownBtn = document.createElement("button");
scrollDownBtn.textContent = " - ";
scrollUpBtn.textContent = " + ";
scrollUpBtn.addEventListener('click', (e) => {
increaseValue(picker);
});
scrollDownBtn.addEventListener('click', (e) => {
decreaseValue(picker);
});
picker.parentNode.insertBefore(scrollDownBtn, picker.nextSibling);
picker.parentNode.insertBefore(scrollUpBtn, picker.nextSibling);
picker.value = "000:00:00";
picker.style.textAlign = "right"; //align the values to the right (optional)
picker.addEventListener('keydown', event => {
//use arrow keys to increase value;
if (event.key == 'ArrowDown' || event.key == 'ArrowUp') {
if(event.key == 'ArrowDown'){
decreaseValue(event.target);
}
if(event.key == 'ArrowUp'){
increaseValue(event.target);
}
event.preventDefault(); //prevent default
}
if (isNaN(event.key) && !acceptedKeys.includes(event.key)) {
event.preventDefault(); //prevent default
return false;
}
});
picker.addEventListener('focus', selectFocus); //selects a block of hours, minutes etc
picker.addEventListener('click', selectFocus); //selects a block of hours, minutes etc
picker.addEventListener('change', validateInput);
picker.addEventListener('blur', validateInput);
picker.addEventListener('keyup', validateInput);
});
});
<input type="text" my-duration-picker></input>

在谷歌浏览器 78 上测试并工作。我稍后会做一个 Angular 版本。

对于 Angular 版本,您可以编写自己的自定义指令并将其导入到您的app-module-ts声明中。请参阅堆栈闪电战上的此示例:

应用演示:https://angular-xbkeoc.stackblitz.io

代码:https://stackblitz.com/edit/angular-xbkeoc

更新:随着时间的推移,我开发并改进了这个概念。您可以在此处👉查看选择器 https://nadchif.github.io/html-duration-picker.js/

查看此解决方案,https://github.com/FrancescoBorzi/ngx-duration-picker,它提供了您正在寻找的选项。

这是演示 - https://embed.plnkr.co/1dAIGrGqbcfrNVqs4WwW/。

演示显示 Y:M:W:D:H:M:S 格式。 您可以使用文档中定义的标志隐藏参数。


由于您正在寻找具有单个输入的持续时间选择器,因此创建自己的组件将很方便。 您可以考虑概念格式化程序和分析程序。

查看此主题,它可以帮助您实现这一目标。

https://netbasal.com/angular-formatters-and-parsers-8388e2599a0e
https://stackoverflow.com/questions/39457941/parsers-and-formatters-in-angular2

下面是更新的示例演示 - https://stackblitz.com/edit/hello-angular-6-yuvffz

您可以使用键向上/键向下事件函数实现增加/减少功能。

handle(event) {
let value = event.target.value; //hhh:mm:ss
if(event.key === 'ArrowUp') {
console.log('increase');
} else if (event.key === 'ArrowDown') {
console.log('decrease');
} else {
//dont allow user from entering more than two digits in seconds
}
}

您需要考虑的验证 ::

- If user enters wrong input, show error message / block from entering anything other than numbers
- allowing only unit specific digits - (Ex :: for hr - 3 digits, mm - 2 digits etc as per your requirement)

要做一些更有趣的事情或使其看起来像交互式,您可以使用flipclock.js看起来非常酷,使用它也是可行的。

这是链接:-

http://flipclockjs.com/

你可以尝试使用数字作为类型:

<input type="min" min="0" max="60">

演示: https://stackblitz.com/edit/angular-nz9hrn

最新更新