前言
主打:快速获取完善开发思想。
您是否在众多文章中看到过「50projects50days」项目的详细描述?垂涎三尺了?没有时间?如果您时间有限,或者只想快速领略其中的亮点,那么您来对地方了。
50projects50days项目地址:?????
GitHub – bradtraversy/50projects50days: 50+ mini web projects using HTML, CSS & JS
简介:
想要快速领略「50projects50days」的精华,却又没有足够的时间?本文为您呈现这个项目系列的精华概览,每个项目都展示了不同的技术和创意,我们将深入剖析每个项目的关键代码和实现步骤,了解其背后的设计思想和技术原理。无论您是初学者还是有一定经验的开发者,本文都将为您提供灵感和知识,帮助您更好地理解和应用 HTML、CSS 和 JavaScript。无需大量时间投入,让我们一起探索这些项目,汲取前端技术的精华。前方的创意和知识等待着您的发现!
目录
由于篇幅问题,本文将分成数个部分来介绍项目系列。以下✅是已发布部分的导航
上期解析 11day-15day,项目展示。
- ✅ Event Keycodes(监听事件按键的代码)
- ✅ Faq Collapse(问题解答折叠)
- ✅ Random Choice Picker(随机选取器)
- ✅ Animated Navigation(动画导航)
- ✅ Incrementing Counter(递增计数器)
本期解析 16day-20day,项目展示。
- ✅ Drink Water(喝杯水)
- ✅ movie App(视频APP)
- ✅ Background Slider(背景滑块)
- ✅ Theme Clock(主题时钟)
- ✅ Button Ripple Effect(按钮波纹效果)
下期解析 21day-25day,项目展示。
传送门?
- ✅ 前端创意探索:速览「50projects50days」项目精华 – 第一部分(1-5 天)
- ✅ 前端创意探索:速览「50projects50days」项目精华 – 第二部分(6-10 天)
- ✅ 前端创意探索:速览「50projects50days」项目精华 – 第三部分(11-15 天)
- ✅ 前端创意探索:速览「50projects50days」项目精华 – 第四部分(16-20 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第五部分(21-25 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第六部分(26-30 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第七部分(31-35 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第八部分(36-40 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第九部分(41-45 天)
- 前端创意探索:速览「50projects50days」项目精华 – 第十部分(46-50 天)
16、Drink Water(喝杯水)
主要关注点:大杯水量变化与小杯的连续选中与取消问题。
实现效果:
用户打开页面后,能够看到标题“Drink Water”和目标饮水量为2升的提示。页面中有一组250毫升的小水杯,用户可以点击这些小水杯来记录已饮水量。每次点击小水杯,大水杯的显示状态会更新,显示已饮水量的百分比,以及剩余的升数。页面使用了一定的CSS样式来美化界面。
实现关键代码
HTML 结构:页面的结构包括标题、目标饮水量的提示、大水杯、小水杯列表以及用于显示剩余水量的元素。
<div class="cup">
<div class="remained" id="remained">
<span id="liters"></span>
<small>Remained</small>
</div>
<div class="percentage" id="percentage"></div>
</div>
<!--省略-->
<div class="cups">
<div class="cup cup-small">250 ml</div>
<div class="cup cup-small">250 ml</div>
<!--省略-->
</div>
JavaScript 逻辑:获取元素、初始化状态、为小水杯添加点击事件监听器、处理按键事件、更新页面显示等。
看一下注释
// 获取所有小水杯元素
const smallCups = document.querySelectorAll('.cup-small')
// 获取剩余水量、百分比和剩余部分的元素
const liters = document.getElementById('liters')
const percentage = document.getElementById('percentage')
const remained = document.getElementById('remained')
// 为每个小水杯添加点击事件监听器
// 注意:两个循环最好不要嵌套,拉出一个新函数
smallCups.forEach((cup, idx) => {
cup.addEventListener('click', () => highlightCups(idx))
}
// 处理小水杯点击事件的函数
function highlightCups(idx) {
// 如果当前点击的是第 8 杯且已经满了,将索引减 1,确保不会超出范围
if (idx===7 && smallCups[idx].classList.contains("full")) idx--;
// 如果当前小水杯已满且下一个小水杯未满,将索引减 1,确保不会跳过未满的小水杯
else if(
smallCups[idx].classList.contains('full') && !smallCups[idx].nextElementSibling.classList.contains('full')) {
idx--
}
// 遍历所有小水杯,更新它们的状态
smallCups.forEach((cup, idx2) => {
if(idx2 <= idx) {
cup.classList.add('full')
} else {
cup.classList.remove('full')
}
})
// 更新大水杯的状态和显示
updateBigCup()
}
// 更新大水杯状态和显示的函数
function updateBigCup () {
// 获取已满的小水杯数量和总小水杯数量
const fullCups = document.querySelectorAll('.cup-small.full').length
const totalCups = smallCups.length
// 根据已饮水杯数量更新百分比条形图的显示
if (fullCups === 0) {
percentage.style.visibility = 'hidden'
percentage.style.height = 0
} else {
percentage.style.visibility = 'visible'
percentage.style.height = `${fullCups / totalCups * 330}px`
percentage.innerText = `${fullCups / totalCups * 100}%`
}
// 如果已饮水杯数量等于总水杯数量,隐藏剩余水量部分
if (fullCups === totalCups) {
remained.style.visibility = 'hidden'
remained.style.height = 0
} else {
// 否则显示剩余水量部分,并计算剩余水量并显示
remained.style.visibility = 'visible'
liters.innerText = `${2 - (250 * fullCups / 1000)}L`
}
}
实现步骤:
- 获取元素: 在开始执行逻辑之前,首先获取需要操作的元素,包括小水杯、剩余水量、百分比和剩余部分的元素。
- 初始化大水杯状态: 在页面加载后,调用
updateBigCup
函数来初始化大水杯的状态和显示,以确保初始状态正确。 - 为小水杯添加点击事件监听器: 使用
forEach
循环为每个小水杯元素添加点击事件监听器,这样当用户点击小水杯时,将会调用highlightCups
函数来处理点击事件。 - 处理点击事件: 在
highlightCups
函数中,首先处理点击事件的逻辑。根据用户点击的小水杯的位置,更新其他小水杯的状态,以确保状态正确。 - 更新大水杯状态: 在
highlightCups
函数内调用updateBigCup
函数,以更新大水杯的状态和显示,以反映最新的饮水量状态。 - 更新大水杯百分比和剩余水量显示: 在
updateBigCup
函数内,通过计算已饮水杯数量和总杯数,更新百分比条形图的高度和显示。如果已饮水满,隐藏剩余水量部分,否则计算并显示剩余水量。
CSS样式
?水杯水量的变化:定义了页面的样式,包括水杯、文本等外观。
.percentage {
background-color: var(--fill-color);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 30px;
height: 0; /* 高度为0,主要靠js修改高度,动画来源于transition*/
transition: 0.3s ease;
}
总结:
上面的代码实现了一个“Drink Water”(喝水)的页面应用,允许用户通过点击小水杯来记录已饮水量,同时大水杯的状态会根据饮水量的百分比进行更新显示,剩余水量也会显示出来。
点击后面的杯子,前面的杯子也会被添加full
,点击前面的满杯,后面的杯子也会被移除full
,每一次的操作都要对大杯进行更新。
17、movie App(视频APP)
主要关注点:关于html的api的运用、电影搜索应用的实现
实现效果:
该应用通过调用 The Movie Database 的 API,从中获取电影信息并显示在页面上。用户可以搜索电影,查看电影海报、评分和概述。
实现关键代码
HTML 结构:页面主要由一个搜索框和一个用于显示电影信息的主区域构成。
<header>
<form id="form">
<input type="text" id="search" class="search" placeholder="Search">
</form>
</header>
<main id="main"></main>
JavaScript 逻辑:实现获取电影信息、显示电影、处理评分等功能。
const API_URL = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=3fd2be6f0c70a2a598f084ddfb75487c&page=1'
const IMG_PATH = 'https://image.tmdb.org/t/p/w1280'
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie?api_key=3fd2be6f0c70a2a598f084ddfb75487c&query="'
const main = document.getElementById('main')
const form = document.getElementById('form')
const search = document.getElementById('search')
// 获取初始电影
getMovies(API_URL)
async function getMovies(url) {
const res = await fetch(url)
const data = await res.json()
showMovies(data.results)
}
function showMovies(movies) {
main.innerHTML = ''
movies.forEach((movie) => {
// 获取电影信息,创建电影元素并添加到页面
const { title, poster_path, vote_average, overview } = movie
const movieEl = document.createElement('div')
// ...
main.appendChild(movieEl)
})
}
function getClassByRate(vote) {
// 根据评分获取不同的 CSS 类名
if(vote >= 8) {
return 'green'
} else if(vote >= 5) {
return 'orange'
} else {
return 'red'
}
}
// 搜索表单提交事件监听
form.addEventListener('submit', (e) => {
e.preventDefault()
const searchTerm = search.value
if(searchTerm && searchTerm !== '') {
// 根据搜索词获取电影信息并显示
getMovies(SEARCH_API + searchTerm)
search.value = ''
} else {
// 重新加载页面
window.location.reload()
}
})
实现步骤:
- 获取电影数据: 使用 themoviedb API 获取电影数据,主要关注热门电影的排序。
- 展示电影: 使用
showMovies
函数将电影数据渲染到页面上,包括电影海报、评分和概述。 - 评分样式: 使用
getClassByRate
函数根据评分判断并返回不同的 CSS 类名,用于显示不同的评分颜色。 - 搜索功能: 通过
form
元素的提交事件监听,在搜索框中输入关键词后,通过 TMDb API 搜索电影,并在页面上显示匹配的电影。 - 重新加载页面: 如果搜索框为空或只包含空格,则通过重新加载页面来重置搜索状态。
CSS样式
?页面:提供了页面的样式,包括搜索框、电影海报、评分等的样式。
主要关心:鼠标移动在movie上面,出现的简介形式
.overview {
background-color: #fff;
padding: 2rem;
position: absolute;
left: 0;
bottom: 0;
right: 0;
max-height: 100%;
transform: translateY(101%);
overflow-y: auto;
transition: transform 0.3s ease-in;
}
.movie:hover .overview {
transform: translateY(0);
}
总结:
电影搜索应用充分利用了 API 数据获取和展示、事件监听和处理以及动态样式变化等前端开发技术。用户可以轻松地搜索电影信息,查看评分和概述,为电影爱好者提供了便捷的交互式体验。
利用overflow
隐藏初始化的overview
标签,采用transform: translateY(101%)
隐藏起来,在触发movie
的hover
时候overview
移动到transform: translateY(0)
。
18、Background Slider(背景滑块)
主要关注点:背景的展示、滑动逻辑。
实现效果:
创建一个背景幻灯片,用户可以点击左右箭头按钮切换不同的背景图片,实现视觉上的幻灯片切换效果。
实现关键代码
HTML 结构:创建了幻灯片容器和箭头按钮。
<div class="slider-container">
<div class="slide active" style="background-image: url('...');"></div>
<div class="slide" style="background-image: url('...');"></div>
<!-- 其他幻灯片项 -->
<button class="arrow left-arrow" id="left">
<i class="fas fa-arrow-left"></i>
</button>
<button class="arrow right-arrow" id="right">
<i class="fas fa-arrow-right"></i>
</button>
</div>
JavaScript 逻辑:通过按钮点击事件切换幻灯片和更新背景图片。
// 获取页面元素
const body = document.body; // 页面 body 元素
const slides = document.querySelectorAll('.slide'); // 获取所有幻灯片元素
const leftBtn = document.getElementById('left'); // 左箭头按钮
const rightBtn = document.getElementById('right'); // 右箭头按钮
let activeSlide = 0; // 当前激活的幻灯片索引
// 右箭头按钮点击事件
rightBtn.addEventListener('click', () => {
activeSlide++;
// 如果当前索引超过幻灯片数量,回到第一张
if (activeSlide > slides.length - 1) {
activeSlide = 0;
}
setBgToBody(); // 更新页面背景图片
setActiveSlide(); // 设置当前激活的幻灯片
});
// 左箭头按钮点击事件
leftBtn.addEventListener('click', () => {
activeSlide--;
// 如果当前索引小于 0,回到最后一张
if (activeSlide < 0) {
activeSlide = slides.length - 1;
}
setBgToBody(); // 更新页面背景图片
setActiveSlide(); // 设置当前激活的幻灯片
});
// 初始化页面背景图片
setBgToBody();
// 更新页面背景图片
function setBgToBody() {
body.style.backgroundImage = slides[activeSlide].style.backgroundImage;
}
// 设置当前激活的幻灯片
function setActiveSlide() {
slides.forEach((slide) => slide.classList.remove('active')); // 移除所有幻灯片的激活状态
slides[activeSlide].classList.add('active'); // 添加当前幻灯片的激活状态
}
实现步骤:
- 创建HTML结构,包括幻灯片容器和箭头按钮。
- 使用JavaScript监听按钮点击事件,切换幻灯片和更新背景图片。
- 实现动态切换背景图片,为用户提供视觉上的幻灯片切换效果。
完成的样式效果
<body style="
background-image: url('...a');
">
<div class="slider-container">
<div
class="slide active"
style="
background-image: url('...a');
"
></div>
</div>
</body>
CSS样式
?底部图片:底部加上遮罩层、图片铺开、动画效果。
body {
font-family: 'Roboto', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
background-position: center center;
background-size: cover;
transition: 0.4s;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.7);
z-index: -1;
}
?高亮图片:定义了幻灯片容器的样式,包括盒阴影、尺寸、位置等。
.slider-container {
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
// 给出height与width,hidden后内部的元素再折腾也就显示出来那么多
height: 70vh;
width: 70vw;
position: relative;
overflow: hidden;
}
.slide {
opacity: 0;
/* 图片100%的张开了,大小与body里的图片一样 */
height: 100vh;
width: 100vw;
background-position: center center;
background-size: cover;
position: absolute;
/* 再高亮的区域内部上移15vh很合理,因为上方就有15vh */
/* (100vh - 70vh) / 2 = 15vh*/
top: -15vh;
left: -15vw;
transition: 0.4s ease;
z-index: 1;
}
.slide.active {
opacity: 1;
}
总结:
这段代码实现了一个背景幻灯片效果,用户可以通过点击左右箭头按钮切换不同的背景图片,从而实现视觉上的幻灯片切换效果。JavaScript监听按钮点击事件,根据用户操作切换幻灯片,并实时更新页面背景图片,为用户提供了一种美观的页面交互体验。
对于上下图片重合显示问题:
在body中加入的图片height: 100vh;
的展示,而在.slider-container
中是height: 70vh;width: 70vw;
的展示,加上隐藏超出的数据,所有在.slide
中的需要填满body使用(100vh - 70vh) / 2 = 15vh
,上下都差15到边沿
为什么?
看图
所以slide
采用了
position: absolute;
top: -15vh;
left: -15vw;
19、Theme Clock(主题时钟)
主要关注点:全局的颜色变化
实现效果:
创建一个时钟,包括时、分、秒的指针,以及日期和主题切换按钮。可以切换亮色和暗色两种主题。
实现关键代码
HTML 结构:创建时钟的样式
<button class="toggle">Dark mode</button>
<div class="clock-container">
<div class="clock">
<div class="needle hour"></div>
<div class="needle minute"></div>
<div class="needle second"></div>
<div class="center-point"></div>
</div>
<div class="time"></div>
<div class="date"></div>
</div>
JavaScript 逻辑:切换黑白、定时处理时间函数
切换黑白颜色
toggle.addEventListener('click', (e) => {
const html = document.querySelector('html')
if (html.classList.contains('dark')) {
html.classList.remove('dark')
e.target.innerHTML = 'Dark mode'
} else {
html.classList.add('dark')
e.target.innerHTML = 'Light mode'
}
})
时间显示
function setTime() {
const time = new Date();
const month = time.getMonth()
const day = time.getDay()
const date = time.getDate()
const hours = time.getHours()
const hoursForClock = hours >= 13 ? hours % 12 : hours;
const minutes = time.getMinutes()
const seconds = time.getSeconds()
const ampm = hours >= 12 ? 'PM' : 'AM'
hourEl.style.transform = `translate(-50%, -100%) rotate(${scale(hoursForClock, 0, 12, 0, 360)}deg)`
minuteEl.style.transform = `translate(-50%, -100%) rotate(${scale(minutes, 0, 60, 0, 360)}deg)`
secondEl.style.transform = `translate(-50%, -100%) rotate(${scale(seconds, 0, 60, 0, 360)}deg)`
timeEl.innerHTML = `${hoursForClock}:${minutes < 10 ? `0${minutes}` : minutes} ${ampm}`
dateEl.innerHTML = `${days[day]}, ${months[month]} <span class="circle">${date}</span>`
}
// 这个是角度
const scale = (num, in_min, in_max, out_min, out_max) => {
return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
setTime()
setInterval(setTime, 1000)
实现步骤:
- 创建一个按钮元素,用于切换主题。
- 创建一个时钟容器,包含时钟指针、时间和日期元素。
- 使用 JavaScript 获取页面元素,设置切换主题功能,以及更新时钟的时间和日期。
- 定义时间和日期的格式,并设置时、分、秒指针的位置。
- 创建一个函数用于进行数值映射。
- 初始化时钟并使用
setInterval
每秒钟更新一次时间。
CSS样式
?黑白颜色:切换黑白颜色需要的样式
// 规划颜色
:root {
--primary-color: #000;
--secondary-color: #fff;
}
html {
transition: all 0.5s ease-in;
}
// 给html写了两个全新的变量
html.dark {
--primary-color: #fff;
--secondary-color: #333;
}
// 设计在toggle中dark下的颜色
html.dark {
background-color: #111;
color: var(--primary-color);
}
// 下面的颜色都使用变量来设计
总结:
本代码实现了一个主题切换功能的时钟。通过点击按钮可以切换亮色和暗色两种主题,时钟包含了时、分、秒指针,以及日期显示。JavaScript 控制时钟的更新和主题切换,样式为时钟提供了美观的外观。整体效果为一个实用且富有设计感的时钟应用。
利用scale
的方法,获取到当前时间 时秒分 的角度,setInterval(setTime, 1000)
,每一秒执行一次执行函数。
toggle
给html
添加class类,来控制变量颜色。
20、Button Ripple Effect(按钮波纹效果)
主要关注点:按钮点击产生水波纹效果展示
实现效果:
创建一个按钮,当点击按钮时,按钮上产生水波纹扩散效果。
实现关键代码
HTML 结构:构建方块
<button class="ripple">Click Me</button>
JavaScript 逻辑:获取点击的位置
const buttons = document.querySelectorAll('.ripple')
buttons.forEach(button => {
button.addEventListener('click', function (e) {
const x = e.pageX
const y = e.pageY
const buttonTop = e.target.offsetTop
const buttonLeft = e.target.offsetLeft
const xInside = x - buttonLeft
const yInside = y - buttonTop
const circle = document.createElement('span')
circle.classList.add('circle')
circle.style.top = yInside + 'px'
circle.style.left = xInside + 'px'
this.appendChild(circle)
setTimeout(() => circle.remove(), 500)
})
})
实现步骤:
- 创建一个按钮元素,标记为具有水波纹效果的类名
ripple
。 - 使用 JavaScript 获取所有具有
.ripple
类名的按钮元素。 - 对每个按钮添加点击事件监听器,在点击时创建一个
<span>
元素,表示水波纹效果。 - 根据点击位置计算水波纹的位置,并将其添加到按钮内部。
- 通过定时器,在一段时间后移除水波纹元素。
CSS样式
?关键样式:定义了按钮的样式以及水波纹效果的动画。
button .circle {
position: absolute;
background-color: #fff;
width: 100px;
height: 100px;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
animation: scale 0.5s ease-out;
}
@keyframes scale {
to {
transform: translate(-50%, -50%) scale(3);
opacity: 0;
}
}
总结:
本代码实现了一个按钮点击产生水波纹效果的功能。通过 JavaScript 监听按钮点击事件,利用<span/>
的画圆形图,在按钮上产生一个水波纹扩散的动画效果,通过添加和移除 <span>
元素来实现。按钮的样式以及水波纹的动画效果使得按钮点击更加有趣且生动。
?预知后事如何,待下回分解