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))
})