1.作品展示
这里的沙漏是真实随时间变化而变化的,当时间为零,下方的沙漏将会被填满,这里其实是想找一个现实中的沙漏,当网上好多素材都是收费的,所以就做了个简易版。
2.功能介绍
1.标题
大家应该能看到我上面两张图的颜色是不同,其实我是用了随机颜色来展示这个标题的,下面是实现这个功能的JavaScript代码,这里主要用十六进制颜色表示,通过随机数获得随机的颜色
//设置随机颜色标题 function getRandomColor() { const letters = '0123456789ABCDEF' let color = '#' for (let i = 0; i < 6; i++) { color += letters[Math.floor(Math.random() * 16)]; } return color } function updateRandomColor() { const title = document.querySelector('.title') const color1 = getRandomColor(); const color2 = getRandomColor(); const color3 = getRandomColor(); title.style.color = getRandomColor() title.style.backgroundImage = `linear-gradient(to right, ${color1}, ${color2}, ${color3})` }
复制
这里是css代码
@keyframes colorChange { 0% { background-position: 0% 50%; } 70% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .title { font-size: 60px; margin-top: 50px; text-align: center; background-image: linear-gradient(to right, #ff0000, #b9e3f0, #0000ff); background-size: 200% 200%; animation: colorChange 8s ease infinite; background-clip: text; -webkit-text-fill-color: transparent; }
复制
2.时间显示
可以看到我这个清单的坐上角有一个时间显示,这个是实时获取当时的时间,不断更新的,代码也是挺简单的
//表单日期 function getdate() { const currentDate = new Date() //const year = currentDate.getFullYear() const month = String(currentDate.getMonth() + 1).padStart(2, '0') const day = String(currentDate.getDate()).padStart(2, '0') const hours = String(currentDate.getHours()).padStart(2, '0') const minutes = String(currentDate.getMinutes()).padStart(2, '0') //const seconds = String(currentDate.getSeconds()).padStart(2, '0') return `${month}/${day} ${hours}:${minutes}` } setInterval(function () { document.querySelector('.date').innerHTML = new Date().toLocaleString() }, 1000)
复制
3.任务添加逻辑
这里的任务添加逻辑主要通过获取你输入在文本框的数据(这里有非空判断和去除前后空格),然后localStorage上传但本地浏览器,保存下来,当你刷新页面是仍然存在
//添加数据到本地 console.log(new Date().toLocaleString()) const info = document.querySelector('.info') const task = document.querySelector('.task') const error_message = document.querySelector('.error_message') const data = JSON.parse(localStorage.getItem('data')) || [] //数据提交 const taskList = document.querySelector('.task_list') renderTask() info.addEventListener('submit', function (e) { e.preventDefault() //去除前后空格 let taskValue = task.value.trim() if (taskValue) { error_message.textContent = '' data.push( { value: taskValue, time: getdate(), actionTime: '0', status: 'fasle' } ) while (taskList.firstChild) { taskList.removeChild(taskList.firstChild) } renderTask() } else { error_message.textContent = '请输入任务内容' } saveTasksStorage() this.reset() }) //添加数据 function addTask(allTask) { const taskLine = document.createElement('li') taskLine.classList.add('taskList') const taskRadio = document.createElement('input') taskRadio.type = 'checkbox'; taskRadio.classList.add('checkbox') //复选框操作 taskRadio.addEventListener('change', () => { const taskIndex = Array.from(taskLine.parentNode.children).indexOf(taskLine); if (taskRadio.checked) { taskLine.classList.add('completed') taskLine.classList.add('aaaa') taskRadio.style.pointerEvents = 'auto' data[taskIndex].status = 'true' checkPause() resetTimer() } else { taskLine.classList.remove('completed') // 取消之前设置的定时器 taskLine.classList.remove('aaaa') data[taskIndex].status = 'false' checkStart() //clearTimeout(taskLine.dataset.timeoutId) } saveTasksStorage() }) //文本显示和编辑 const taskValue = document.createElement('span') taskValue.textContent = allTask.value taskValue.classList.add('task_vlaue') taskValue.setAttribute('contenteditable', 'true') taskValue.setAttribute('spellcheck', 'false') taskValue.addEventListener('input', () => { taskValue.addEventListener('keydown', (event) => { // 检查是否按下了 Enter 键 if (event.key === 'Enter') { // 阻止默认的换行行为 event.preventDefault() // 获取更改后的内容 const updatedContent = taskValue.textContent.trim() const index = Array.from(taskLine.parentNode.children).indexOf(taskLine) // 将更改后的内容保存到本地存储 data[index].value = updatedContent if (updatedContent === '') { taskValue.textContent = '无标题' } else { // 将更改后的内容保存到本地存储 localStorage.setItem('data', JSON.stringify(data)) } // 触发 blur 事件,使编辑状态结束 taskValue.blur(); } }) }) const taskTime = document.createElement('span') taskTime.textContent = allTask.time taskTime.classList.add('task_time') const taskFunction = document.createElement('div') taskFunction.classList.add('taskFunction') //推迟 const template1 = document.querySelector('.pattern1') const taskRemind = template1.content.cloneNode(true); //关闭按钮 const taskDelete = document.createElement('div') taskDelete.classList.add('taskDelete') taskDelete.textContent = '关闭' taskDelete.addEventListener('click', () => { if (data.length <= +localStorage.getItem('indexNow')) { localStorage.setItem('indexNow', '0') saveTasksStorage() } deletetask(taskLine); }); taskFunction.appendChild(taskRemind) taskFunction.appendChild(taskDelete) taskLine.appendChild(taskRadio) taskLine.appendChild(taskValue) taskLine.appendChild(taskTime) taskLine.appendChild(taskFunction) selecttime(taskLine) return taskLine }
复制
4.渲染数据
这个主要的实现逻辑是通过获取你储存在浏览器的数据,然后遍历渲染的你的页面上,添加新的数据时并实时更新
//检测输入框上是否有数据 task.addEventListener('input', () => { if (error_message.textContent !== '') { error_message.textContent = '' } }) //保存数据到本地 function saveTasksStorage() { localStorage.setItem('data', JSON.stringify(data)) } //渲染数据 function renderTask() { while (taskList.firstChild) { taskList.removeChild(taskList.firstChild) } for (let i = 0; i < data.length; i++) { const task = data[i] const taskLine = addTask(task) const checked = taskLine.querySelector('.checkbox') if (task.status === 'true') { taskLine.classList.add('completed') taskLine.classList.add('aaaa') checked.checked = 'true' checked.style.pointerEvents = 'auto' taskList.appendChild(taskLine) } else { taskList.appendChild(taskLine) } } }
复制
5.删除数据
删除数据主要逻辑是当你点击你想要删除的一个任务的关闭按钮时,获取你点击这行的索引,在你储存data对象数组删除这一行数据,然后再上传到本地,再去重新渲染页面。
//删出数据 function deletetask(taskLine) { const taskIndex = Array.from(taskList.childNodes).indexOf(taskLine) console.log(taskIndex) data[taskIndex].actionTime = '0' data.splice(taskIndex, 1); saveTasksStorage() //taskLine.remove(); while (taskList.firstChild) { taskList.removeChild(taskList.firstChild); } resetTimer() renderTask(); }
复制
6.推迟时间和沙漏时间显示
这个功能是整个项目最难的地方,因为他要获取你点击的时间,并且还要考虑你当时在哪一行数据操作,以及当你在去点击其他任务时时间沙漏的变化,还有就是当你刷新页面是这个沙漏倒计时是不停止的,只有当你点击展厅或者重置按钮或者你点击复选框完成这项任务,时间才会暂停,这里的沙漏变化也是一个难点,需要根据倒计时还有多少时间就更新你沙漏状态。
//获取你选择的执行时间 function selecttime(taskLine) { //const func = taskLine.querySelector('.taskFunction') // const thirdChild = taskLine.children[2]; taskLine.children[3].addEventListener('change', function (event) { // taskLine.style.backgroundColor = '' const delaySelectValue = +(event.target.value * 60) const delayIndex = Array.from(taskList.childNodes).indexOf(taskLine) console.log(delayIndex); data[delayIndex].actionTime = +data[delayIndex].actionTime + delaySelectValue // let indexNow = delayIndex //当前你的时间记录行的索引 localStorage.setItem('indexNow', JSON.stringify(delayIndex)) //更新数据到本地 saveTasksStorage() //执行你所选的倒计时时间 startTimer(data[delayIndex].actionTime, taskLine) const childrenLine = document.querySelectorAll('.taskList') childrenLine.forEach(child => { childrenLine.forEach(c => c.classList.remove('hintColor')); taskLine.classList.add('hintColor') }); // 为当前点击的子元素添加 'active' 类 //taskLine.classList.add('hintColor'); //taskLine.classList.add('hintColor') }) } //刷新页面时显示倒计时正在进行哪一行 (function () { const childrenLine = document.querySelectorAll('.taskList') if (childrenLine.length > 0) { childrenLine[+localStorage.getItem('indexNow')].classList.add('hintColor') } })() //时间沙漏 let timer let totalSeconds = 0 let elapsedSeconds = 0 let isRunning = false const topSand = document.getElementById('topSand') const bottomSand = document.getElementById('bottomSand') const timerDisplay = document.getElementById('timer') const pauseBtn = document.getElementById('pauseBtn') //当页面刷新时继续倒计时上次的 if (data.length > 0) { startTimer2(data[+localStorage.getItem('indexNow')].actionTime, data[+localStorage.getItem('indexNow')]) } function startTimer2(minutes, taskLine) { resetTimer() if (minutes === null) { minutes = 0 } totalSeconds = minutes elapsedSeconds = 0 if (data[+localStorage.getItem('indexNow')].status === 'false') { isRunning = true pauseBtn.disabled = false updateTimer() timer = setInterval(() => { if (isRunning) { updateTimer() elapsedSeconds++ if (elapsedSeconds > totalSeconds) { pauseBtn.disabled = true //时间到去除这项任务 setTimeout(function () { //deletetask(taskLine) }, 2000) clearInterval(timer) } } }, 1000) } } function startTimer(minutes, taskLine) { resetTimer() if (minutes === null) { minutes = 0 } totalSeconds = minutes elapsedSeconds = 0 isRunning = true pauseBtn.disabled = false updateTimer() timer = setInterval(() => { if (isRunning) { updateTimer() elapsedSeconds++ if (elapsedSeconds > totalSeconds) { pauseBtn.disabled = true //时间到去除这项任务 setTimeout(function () { //deletetask(taskLine) }, 2000) clearInterval(timer) } } }, 1000) } //更新时间 function updateTimer() { const progress = elapsedSeconds / totalSeconds topSand.style.width = `${200 * (1 - progress)}px` topSand.style.height = `${150 * (1 - progress)}px` bottomSand.style.width = `${200 * progress}px` bottomSand.style.height = `${150 * progress}px` const remainingSeconds = totalSeconds - elapsedSeconds const minutes = Math.floor(remainingSeconds / 60) const seconds = remainingSeconds % 60 timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` //跟新本地数据时间 data[+localStorage.getItem('indexNow')].actionTime = remainingSeconds saveTasksStorage() } //时间的暂停 function pauseResumeTimer() { isRunning = !isRunning; pauseBtn.textContent = isRunning ? '暂停' : '继续' } //当结束任务是的时间状态 function checkPause() { isRunning = false; pauseBtn.disabled = true } function checkStart() { isRunning = false pauseBtn.disabled = false pauseBtn.textContent = isRunning ? '暂停' : '继续' } //重置沙漏状态 function resetTimer() { clearInterval(timer) elapsedSeconds = 0 isRunning = false topSand.style.width = '200px' topSand.style.height = '150px' bottomSand.style.width = '0px' bottomSand.style.height = '0px' timerDisplay.textContent = '00:00' pauseBtn.textContent = '暂停' pauseBtn.disabled = true } //重置并清空时间 const resetButton = document.querySelector('.test111') resetButton.addEventListener('click', function () { clearInterval(timer) data[localStorage.getItem('indexNow')].actionTime = '0' localStorage.setItem('data', JSON.stringify(data)) })
复制
7.数据更改
这里的数据文字更改是可以直接选择在你渲染好的数据上修改的,主要逻辑也是文本哪里添加一个键盘监听,当你修改好数据按下enter键就会完成修改,也是通过先更改定义的对象数组data里的数据然后再上传本地数据,最后重新渲染。
//文本显示和编辑 const taskValue = document.createElement('span') taskValue.textContent = allTask.value taskValue.classList.add('task_vlaue') taskValue.setAttribute('contenteditable', 'true') taskValue.setAttribute('spellcheck', 'false') taskValue.addEventListener('input', () => { taskValue.addEventListener('keydown', (event) => { // 检查是否按下了 Enter 键 if (event.key === 'Enter') { // 阻止默认的换行行为 event.preventDefault() // 获取更改后的内容 const updatedContent = taskValue.textContent.trim() const index = Array.from(taskLine.parentNode.children).indexOf(taskLine) // 将更改后的内容保存到本地存储 data[index].value = updatedContent if (updatedContent === '') { taskValue.textContent = '无标题' } else { // 将更改后的内容保存到本地存储 localStorage.setItem('data', JSON.stringify(data)) } // 触发 blur 事件,使编辑状态结束 taskValue.blur(); } }) })
复制
3.完整代码展示
1.HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./index.css"> <link rel="stylesheet" href="./base.css"> <script async src="./JS.js"></script> </head> <body> <video class="bg_video" src="./images/wallpaper-dynamic_wallpaper-dynamic-357.mp4" autoplay loop muted></video> <h1 class="title">todoList</h1> <div class="subject"> <div class="subject_left"> <!-- 头部标题 --> <div class="sb_title"> <div class="sb_date"> <p>我的一天</p> <span class="date"></span> </div> <div class="sb_caption">To-Do List</div> </div> <!-- //输入框 --> <form class="info" autocomplete="off"> <input type="text" class="task" placeholder="+ Add Your Task"> <button class="add">Add Task</button> </form> <!-- 错误提示 --> <div class="error_message"></div> <!-- 任务内容 --> <div class="assignment"> <div class="to_doTask">To-Do</div> <ul class="task_list"> </ul> <template class="pattern1"> <div class="custom-select"> <span class="placeholder">推迟</span> <select name="time" class="hidden-select"> <option value="0">现在</option> <option value="0.1">5分钟后</option> <option value="15">15分钟后</option> <option value="30">30分钟后</option> <option value="60">1小时后</option> <option value="180">2小时后</option> <option value="1440">明天</option> <option value="0">选中</option> </select> </div> </template> </div> </div> <!-- 时间沙漏 --> <div class="subject_right"> <div class="container"> <div class="timer" id="timer">00:00</div> <div class="hourglass"> <div class="hourglass-shape"> <div class="top-triangle"></div> <div class="bottom-triangle"></div> </div> <div class="sand top-sand" id="topSand"></div> <div class="middle"></div> <div class="sand bottom-sand" id="bottomSand"></div> </div> <div class="controls"> <button id="pauseBtn" onclick="pauseResumeTimer()" disabled>暂停</button> <button onclick="resetTimer()" class="resetButton test111">重置</button> </div> </div> </div> </div> </body> </html>
复制
2.CSS代码
这里我定义的less文件,它是会自动转化为CSS文件代码的,是一个比较方便的CSS写法,其实就可以写嵌套代码
body { margin: 0; padding: 0; // width: 100vw; // height: 100vh; overflow-y: scroll; } .bg_video { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; object-fit: cover; } @keyframes colorChange { 0% { background-position: 0% 50%; } 70% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .title { font-size: 60px; margin-top: 50px; text-align: center; background-image: linear-gradient(to right, #ff0000, #b9e3f0, #0000ff); background-size: 200% 200%; animation: colorChange 8s ease infinite; //背景图像限制在文本区域内 background-clip: text; //文字设为透明 -webkit-text-fill-color: transparent; } .subject { position: relative; display: flex; width: 980px; height: 490px; margin: 50px auto; border-radius: 5%; border: 2px #d9b949 solid; overflow: hidden; .subject_left { width: 600px; background: url(./images/mission-candlestick-7617552_1280.webp) no-repeat; background-size: cover; opacity: .8; .sb_title { display: flex; margin-top: 0px; height: 80px; .sb_date { margin-top: 35px; width: 200px; //background-color: blue; p { text-align: center; font-size: 20px; color: #07aa93; } .date { margin-top: 5px; display: block; text-align: center; font-size: 15px; color: #adb1b5; } } .sb_caption { width: 200px; margin: 30px 25px 0; color: #8fb4d1; font-size: 25px; text-align: center; border-bottom: 1px solid #0681be; } } .info { margin-top: 30px; .task { width: 400px; height: 40px; background-color: transparent; border: 1px solid transparent; margin-left: 30px; color: #999; border-bottom: 2px solid #0681be; } .add { width: 80px; height: 30px; margin-left: 20px; background-color: #a96f34; border-radius: 15px; } } .assignment { width: 430px; height: 290px; margin-top: 10px; margin-left: 30px; border: 2px solid #90b4d1; border-radius: 10%; overflow: auto; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Internet Explorer 和 Edge */ .to_doTask { width: 80px; height: 30px; margin-top: 10px; margin-left: 10px; border-bottom: 2px solid #f2b11c; text-align: center; color: #f2b11c; } //任务栏 .task_list { overflow: auto; } //时间栏 .timeDelay { width: 80px; height: 200px; } /* 选择框容器样式 */ .custom-select { position: relative; display: inline-block; width: 60px; height: 23px; line-height: 23px; } /* 占位元素样式 */ .custom-select .placeholder { display: block; text-align: center; //font-size: inherit; color: #895d31; background-color: #cccdcf; border-radius: 4px; cursor: pointer; } /* 隐藏的选择框样式 */ .custom-select .hidden-select { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; } /* 选择时间显示区域 */ .selected-time { margin-top: 10px; font-weight: bold; } } .assignment::-webkit-scrollbar { display: none; } .error_message { color: red; height: 12px; margin-left: 30px; margin-top: 5px; font-size: 12px; font-weight: 700; } } .subject_right { width: 380px; background: url(./images/385dd3ba7d76551.jpg) no-repeat; background-size: cover; opacity: .8; border-left: 2px solid black; // body { // font-family: Arial, sans-serif; // display: flex; // justify-content: center; // align-items: center; // height: 100vh; // margin: 0; // background-color: #f0f0f0; // } .container { text-align: center; } .hourglass { width: 200px; height: 300px; position: relative; margin: 30px auto; } .hourglass-shape { width: 100%; height: 100%; position: absolute; border: 4px solid #b3b7bb; } .top-triangle, .bottom-triangle { width: 0; height: 0; position: absolute; left: 50%; transform: translateX(-50%); } .top-triangle { top: 0; border-left: 100px solid transparent; border-right: 100px solid transparent; border-top: 150px solid #b3b7bb; } .bottom-triangle { bottom: 0; border-left: 100px solid transparent; border-right: 100px solid transparent; border-bottom: 150px solid #b3b7bb; } .sand { position: absolute; left: 50%; transform: translateX(-50%); background-color: #f1c40f; transition: height 0.1s linear; } .top-sand { top: 4px; clip-path: polygon(0% 0%, 100% 0%, 50% 100%); } .bottom-sand { bottom: 4px; clip-path: polygon(50% 0%, 0% 100%, 100% 100%); } .middle { width: 20px; height: 20px; background-color: #b3b7bb; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) rotate(45deg); } .timer { font-size: 24px; margin-top: 15px; color: #f8fbf1; } .controls button { padding: 10px 20px; font-size: 16px; margin: 0 5px; cursor: pointer; background-color: #3498db; color: white; border: none; border-radius: 5px; transition: background-color 0.3s; } .controls button:hover { background-color: #2980b9; } .controls button:disabled { background-color: #95a5a6; cursor: not-allowed; } } } /* 创建的组件属性 */ .taskList { display: flex; align-items: center; justify-content: space-between; width: 380px; height: 50px; border: 1px solid #f2b11c; border-radius: 20px; margin: 10px 0 0 20px; } //显示文本 .task_vlaue { font-size: 18px; width: 200px; color: #c7cfd5; margin: 10px 20px; margin-right: 15px; cursor: text; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } //显示时间 .task_time { //text-align: right; flex-grow: 1; font-size: 12px; color: #0681be; } .checkbox { //appearance: none; width: 20px; height: 20px; border: 1px solid #ccc; border-radius: 3px; outline: none; cursor: pointer; margin-left: 10px; //background-image: url(./images/玩球的猫.png); // background-size: contain; // background-repeat: no-repeat; } //功能按钮 .taskFunction { display: flex; flex-direction: column; justify-content: space-between; width: 60px; height: 48px; background-color: #07203f; margin-right: 20px; cursor: pointer; } //推迟按钮 .taskRemind { text-align: center; width: 60px; height: 23px; line-height: 23px; color: #895d31; background-color: #cccdcf; border-radius: 5px; } //关闭按钮 .taskList .taskDelete { text-align: center; width: 60px; height: 23px; line-height: 23px; color: #895d31; background-color: #cccdcf; border-radius: 5px; text-decoration: none !important; } .hintColor { background-color: #fff; } //点击复选框出现复选框 .completed { text-decoration: line-through; } //点击复选框禁用其他按钮 .aaaa { pointer-events: none; } .bbbb { pointer-events: none; }
复制
3.JavaScript代码
//设置随机颜色标题 function getRandomColor() { const letters = '0123456789ABCDEF' let color = '#' for (let i = 0; i < 6; i++) { color += letters[Math.floor(Math.random() * 16)]; } return color } function updateRandomColor() { const title = document.querySelector('.title') const color1 = getRandomColor(); const color2 = getRandomColor(); const color3 = getRandomColor(); title.style.color = getRandomColor() title.style.backgroundImage = `linear-gradient(to right, ${color1}, ${color2}, ${color3})` } updateRandomColor() setInterval(updateRandomColor, 1000) //表单左侧内容--------------------------------------------------------------------------- //表单日期 function getdate() { const currentDate = new Date() //const year = currentDate.getFullYear() const month = String(currentDate.getMonth() + 1).padStart(2, '0') const day = String(currentDate.getDate()).padStart(2, '0') const hours = String(currentDate.getHours()).padStart(2, '0') const minutes = String(currentDate.getMinutes()).padStart(2, '0') //const seconds = String(currentDate.getSeconds()).padStart(2, '0') return `${month}/${day} ${hours}:${minutes}` } setInterval(function () { document.querySelector('.date').innerHTML = new Date().toLocaleString() }, 1000) //任务添加---------------------------------------------------- //添加数据到本地 console.log(new Date().toLocaleString()) const info = document.querySelector('.info') const task = document.querySelector('.task') const error_message = document.querySelector('.error_message') const data = JSON.parse(localStorage.getItem('data')) || [] //数据提交 const taskList = document.querySelector('.task_list') renderTask() info.addEventListener('submit', function (e) { e.preventDefault() //去除前后空格 let taskValue = task.value.trim() if (taskValue) { error_message.textContent = '' data.push( { value: taskValue, time: getdate(), actionTime: '0', status: 'fasle' } ) while (taskList.firstChild) { taskList.removeChild(taskList.firstChild) } renderTask() } else { error_message.textContent = '请输入任务内容' } saveTasksStorage() this.reset() }) //检测输入框上是否有数据 task.addEventListener('input', () => { if (error_message.textContent !== '') { error_message.textContent = '' } }) //保存数据到本地 function saveTasksStorage() { localStorage.setItem('data', JSON.stringify(data)) } //渲染数据 function renderTask() { while (taskList.firstChild) { taskList.removeChild(taskList.firstChild) } for (let i = 0; i < data.length; i++) { const task = data[i] const taskLine = addTask(task) const checked = taskLine.querySelector('.checkbox') if (task.status === 'true') { taskLine.classList.add('completed') taskLine.classList.add('aaaa') checked.checked = 'true' checked.style.pointerEvents = 'auto' taskList.appendChild(taskLine) } else { taskList.appendChild(taskLine) } } } //添加数据 function addTask(allTask) { const taskLine = document.createElement('li') taskLine.classList.add('taskList') const taskRadio = document.createElement('input') taskRadio.type = 'checkbox'; taskRadio.classList.add('checkbox') //复选框操作 taskRadio.addEventListener('change', () => { const taskIndex = Array.from(taskLine.parentNode.children).indexOf(taskLine); if (taskRadio.checked) { taskLine.classList.add('completed') taskLine.classList.add('aaaa') taskRadio.style.pointerEvents = 'auto' data[taskIndex].status = 'true' checkPause() resetTimer() } else { taskLine.classList.remove('completed') // 取消之前设置的定时器 taskLine.classList.remove('aaaa') data[taskIndex].status = 'false' checkStart() //clearTimeout(taskLine.dataset.timeoutId) } saveTasksStorage() }) //文本显示和编辑 const taskValue = document.createElement('span') taskValue.textContent = allTask.value taskValue.classList.add('task_vlaue') taskValue.setAttribute('contenteditable', 'true') taskValue.setAttribute('spellcheck', 'false') taskValue.addEventListener('input', () => { taskValue.addEventListener('keydown', (event) => { // 检查是否按下了 Enter 键 if (event.key === 'Enter') { // 阻止默认的换行行为 event.preventDefault() // 获取更改后的内容 const updatedContent = taskValue.textContent.trim() const index = Array.from(taskLine.parentNode.children).indexOf(taskLine) // 将更改后的内容保存到本地存储 data[index].value = updatedContent if (updatedContent === '') { taskValue.textContent = '无标题' } else { // 将更改后的内容保存到本地存储 localStorage.setItem('data', JSON.stringify(data)) } // 触发 blur 事件,使编辑状态结束 taskValue.blur(); } }) }) const taskTime = document.createElement('span') taskTime.textContent = allTask.time taskTime.classList.add('task_time') const taskFunction = document.createElement('div') taskFunction.classList.add('taskFunction') //推迟 const template1 = document.querySelector('.pattern1') const taskRemind = template1.content.cloneNode(true); //关闭按钮 const taskDelete = document.createElement('div') taskDelete.classList.add('taskDelete') taskDelete.textContent = '关闭' taskDelete.addEventListener('click', () => { if (data.length <= +localStorage.getItem('indexNow')) { localStorage.setItem('indexNow', '0') saveTasksStorage() } deletetask(taskLine); }); taskFunction.appendChild(taskRemind) taskFunction.appendChild(taskDelete) taskLine.appendChild(taskRadio) taskLine.appendChild(taskValue) taskLine.appendChild(taskTime) taskLine.appendChild(taskFunction) selecttime(taskLine) return taskLine } //删出数据 function deletetask(taskLine) { const taskIndex = Array.from(taskList.childNodes).indexOf(taskLine) console.log(taskIndex) data[taskIndex].actionTime = '0' data.splice(taskIndex, 1); saveTasksStorage() //taskLine.remove(); while (taskList.firstChild) { taskList.removeChild(taskList.firstChild); } resetTimer() renderTask(); } //获取你选择的执行时间 function selecttime(taskLine) { //const func = taskLine.querySelector('.taskFunction') // const thirdChild = taskLine.children[2]; taskLine.children[3].addEventListener('change', function (event) { // taskLine.style.backgroundColor = '' const delaySelectValue = +(event.target.value * 60) const delayIndex = Array.from(taskList.childNodes).indexOf(taskLine) console.log(delayIndex); data[delayIndex].actionTime = +data[delayIndex].actionTime + delaySelectValue // let indexNow = delayIndex //当前你的时间记录行的索引 localStorage.setItem('indexNow', JSON.stringify(delayIndex)) //更新数据到本地 saveTasksStorage() //执行你所选的倒计时时间 startTimer(data[delayIndex].actionTime, taskLine) const childrenLine = document.querySelectorAll('.taskList') childrenLine.forEach(child => { childrenLine.forEach(c => c.classList.remove('hintColor')); taskLine.classList.add('hintColor') }); // 为当前点击的子元素添加 'active' 类 //taskLine.classList.add('hintColor'); //taskLine.classList.add('hintColor') }) } //刷新页面时显示倒计时正在进行哪一行 (function () { const childrenLine = document.querySelectorAll('.taskList') if (childrenLine.length > 0) { childrenLine[+localStorage.getItem('indexNow')].classList.add('hintColor') } })() //时间沙漏 let timer let totalSeconds = 0 let elapsedSeconds = 0 let isRunning = false const topSand = document.getElementById('topSand') const bottomSand = document.getElementById('bottomSand') const timerDisplay = document.getElementById('timer') const pauseBtn = document.getElementById('pauseBtn') //当页面刷新时继续倒计时上次的 if (data.length > 0) { startTimer2(data[+localStorage.getItem('indexNow')].actionTime, data[+localStorage.getItem('indexNow')]) } function startTimer2(minutes, taskLine) { resetTimer() if (minutes === null) { minutes = 0 } totalSeconds = minutes elapsedSeconds = 0 if (data[+localStorage.getItem('indexNow')].status === 'false') { isRunning = true pauseBtn.disabled = false updateTimer() timer = setInterval(() => { if (isRunning) { updateTimer() elapsedSeconds++ if (elapsedSeconds > totalSeconds) { pauseBtn.disabled = true //时间到去除这项任务 setTimeout(function () { //deletetask(taskLine) }, 2000) clearInterval(timer) } } }, 1000) } } function startTimer(minutes, taskLine) { resetTimer() if (minutes === null) { minutes = 0 } totalSeconds = minutes elapsedSeconds = 0 isRunning = true pauseBtn.disabled = false updateTimer() timer = setInterval(() => { if (isRunning) { updateTimer() elapsedSeconds++ if (elapsedSeconds > totalSeconds) { pauseBtn.disabled = true //时间到去除这项任务 setTimeout(function () { //deletetask(taskLine) }, 2000) clearInterval(timer) } } }, 1000) } //更新时间 function updateTimer() { const progress = elapsedSeconds / totalSeconds topSand.style.width = `${200 * (1 - progress)}px` topSand.style.height = `${150 * (1 - progress)}px` bottomSand.style.width = `${200 * progress}px` bottomSand.style.height = `${150 * progress}px` const remainingSeconds = totalSeconds - elapsedSeconds const minutes = Math.floor(remainingSeconds / 60) const seconds = remainingSeconds % 60 timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` //跟新本地数据时间 data[+localStorage.getItem('indexNow')].actionTime = remainingSeconds saveTasksStorage() } //时间的暂停 function pauseResumeTimer() { isRunning = !isRunning; pauseBtn.textContent = isRunning ? '暂停' : '继续' } //当结束任务是的时间状态 function checkPause() { isRunning = false; pauseBtn.disabled = true } function checkStart() { isRunning = false pauseBtn.disabled = false pauseBtn.textContent = isRunning ? '暂停' : '继续' } //重置沙漏状态 function resetTimer() { clearInterval(timer) elapsedSeconds = 0 isRunning = false topSand.style.width = '200px' topSand.style.height = '150px' bottomSand.style.width = '0px' bottomSand.style.height = '0px' timerDisplay.textContent = '00:00' pauseBtn.textContent = '暂停' pauseBtn.disabled = true } //重置并清空时间 const resetButton = document.querySelector('.test111') resetButton.addEventListener('click', function () { clearInterval(timer) data[localStorage.getItem('indexNow')].actionTime = '0' localStorage.setItem('data', JSON.stringify(data)) })
复制