开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
项目出处: github.com/bradtravers…
1. Expanding Cards(扩展卡)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .panel.active { flex: 5; filter: brightness(1); } // js const ctnEl = document.querySelector('.container') ctnEl.addEventListener('click', function (event) { Array.prototype.forEach.call(this.children, (el) => { el.classList.remove('active') }) event.target.classList.add('active') }) 复制代码
-
项目总结
(1)CSS:
flex:1
是一种简写写法,表示在容器中占的份数;filter:brightness(1)
用于控制明亮效果,也可用百分比形式,0%
则为全黑
效果(2)JS: 由于
document.querySelector
返回的是一个nodeList,故需要使用Array.prototype.foreach.call()
来进行循环,通过监听点击事件动态添加|移除
active类实现展示效果
2. Progress Steps(进度步骤)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css :root{ --line-border-fill: #168483; --line-border-empty: #e0e0e0; } .btn:disabled{ background-color: var(--line-border-empty); cursor: not-allowed; } // js circles.forEach((item, index) => { if (index < currentActive) { item.classList.add('active') } else { item.classList.remove('active') } }) const actives = document.querySelectorAll('.active') progress.style.width = (actives.length - 1) / (circles.length - 1) * 100 + '%' 复制代码
-
项目总结
(1)CSS: 使用
:root
定义全局变量,搭配::before、:active、:focus、:disabled等伪元素和伪类选择器
实现页面效果(2)JS: 通过当前激活节点
currentActive
来动态添加|移除
active类,以及控制进度条的长度
3. Rotating Navigation Animation(旋转导航动画)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .container.show-nav .circle { transform: rotate(-70deg); } .container.show-nav + nav li { transform: translateX(0); transition-delay: 0.3s; } // js openBar.addEventListener('click',()=>{ container.classList.add('show-nav') }) closeBar.addEventListener('click',()=>{ container.classList.remove('show-nav') }) 复制代码
-
项目总结
(1)CSS: 多个类名连着写表示要
同时满足
才能生效,+
为相邻兄弟选择器(相邻且同级),transform
用于平移、旋转、缩放、倾斜给定元素(2)JS: 通过点击事件来动态
添加|移除
show-nav类,实现导航栏的展示或隐藏
4. Hidden Search Widget(隐藏的搜索小工具)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .search .input{ background-color: #fff; border: 0; font-size: 18px; padding: 15px; width: 50px; height: 50px; transition: width 0.3s ease; border-radius: 2px; } // js btn.addEventListener('click',()=>{ search.classList.toggle('active') input.focus() }) 复制代码
-
项目总结
(1)CSS:
transition
控制过渡效果,可接收四个参数(需要过渡的属性名,过渡时间,过渡曲线,过渡延迟
)(2)JS: 通过监听click点击事件,使用
toggle()
方法切换active类的状态,没有则加上,有则移除
5. Blurry Loading(模糊加载)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .bg { background: url('xx.jpg') no-repeat center/cover; position: absolute; top: -30px; left: -30px; width: calc(100vw + 60px); height: calc(100vh + 60px); z-index: -1; filter: blur(0px); } // js loadText.style.opacity = scale(load, 0, 100, 1, 0) // 透明度 bg.style.filter = `blur(${scale(load, 0, 100, 30, 0)}px)` // 高斯模糊 const scale = (num, in_min, in_max, out_min, out_max) => { // 100 * (-1) / 100 + 1 = 0 // 100 * (-30) / 100 + 30 = 0 return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min } 复制代码
-
项目总结
(1)CSS: background中的
center/cover
代表background-position:center;background-size:cover,且size只能跟在position后
出现,用/
分隔(2)JS: 主要通过
scale()
方法来控制文字的透明度
和背景的blur模
糊参数
6. Scroll Animation(滚动动画)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .box:nth-of-type(even) { transform: translateX(-400%); } .box.show { transform: translateX(0); } // js function checkBoxes() { const triggerBottom = (window.innerHeight / 5) * 4 boxes.forEach((box)=>{ const boxTop =box.getBoundingClientRect().top if(boxTop<triggerBottom){ box.classList.add('show') }else{ box.classList.remove('show') } }) } 复制代码
-
项目总结
(1)CSS:
nth-of-type(even)
伪类选择器,表示选择所有偶数
元素,translateX(-400%)
表示向左平移400%(看不见了)(2)JS:
(window.innerHeight / 5) * 4
获取当前页面高度的4/5,getBoundingClientRect().top
获取元素的顶部距离网页最上方的高度
7. Split Landing Page(拆分着陆页)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .split.left::before { content: ''; position: absolute; width: 100%; height: 100%; background-color: var(--left-bg-color); } // js left.addEventListener('mouseenter', () => container.classList.add('hover-left')) left.addEventListener('mouseleave', () => container.classList.remove('hover-left') ) 复制代码
-
项目总结
(1)CSS: 通过
before
伪元素选择器配合background-color
给图片设置一层遮罩效果(2)JS: 通过监听鼠标
mouseenter和mouseleave
事件动态添加|移除
hover-left类,实现动画效果
8. Form Wave(波形表单)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .form-control label span { ... transition: 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .form-control input:valid + label span { color: lightblue; transform: translateY(-30px); } // js labels.forEach(label => { label.innerHTML = label.innerText .split('') .map( (letter, index) => `<span style="transition-delay:${index * 50}ms">${letter}</span>` ) .join('') }) 复制代码
-
项目总结
(1)CSS: 贝塞尔曲线
cubic-bezier()
是一种过渡线条形式,input:valid
表示文本框输入的值是符合校验
的(2)JS: 将字符串通过
split
分割为单个字符并使用transition-delay
延迟每个字符出现的时间,用map函数
重新渲染,配合贝塞尔曲线的参数,实现波浪效果
9. Sound Board(音板)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// html <audio id="applause" src="sounds/applause.mp3"></audio> <audio id="boo" src="sounds/boo.mp3"></audio> <audio id="gasp" src="sounds/gasp.mp3"></audio> // js btn.addEventListener('click', () => { stopSong() document.getElementById(sound).play() }) 复制代码
-
项目总结
(1)HTML: audio标签中的
src
用于引入音频源(2)JS: 通过监听click事件及
play()
和pause()
方法用来控制音频的播放与暂停
10. Dad Jokes(老爹式笑话)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .btn:active{ transform:scale(0.98); } // js async function generateJoke(){ const config={ headers:{ Accept:'application/json', } } const res=await fetch('https://icanhazdadjoke.com',config) const data =await res.json() jokeEl.innerHTML=data.joke } 复制代码
-
项目总结
(1)CSS:
scale
用来控制元素的缩放、放大
效果。1为正常大小,>1 放大,<1 缩小(2)JS:
async和await
发送异步请求,fetch(url)
可以直接发送get
请求,res.json()
是一个异步操作,取出所有内容,并将其转为JSON对象
11. Event KeyCodes(键盘键码)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css body{ justify-content:center; align-items:center; text-align:center; ... } // js window.addEventListener('keydown', event => { insert.innerHTML = ` <div class='key'> ${event.key === ' ' ? 'Space' : event.key} <small>event.key</small> </div> .... }) 复制代码
-
项目总结
(1)CSS:
flex
弹性布局实现居中(2)JS: 通过监听
keydown
键盘事件,使用innerHTML
渲染事件中的key、keyCode、code
属性
12. Faq Collapse(常见问题解答)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .faq.active:before, .faq.active:after { content: '\f075'; font-family: 'Font Awesome 5 Free'; ... } // js toggles.forEach(toggle=>{ toggle.addEventListener('click',()=>{ toggle.parentNode.classList.toggle('active') }) }) 复制代码
-
项目总结
(1)CSS: 通过
before
和after
伪元素选择器搭配font-awesome
字体库实现基本样式效果(2)JS: 监听click点击事件,配合
toggle()
函数,动态控制active类的添加或移除
13. Random Choice Picker(随机选择器)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css textarea:focus{ outline:none; } // js function createTags(input) { const tags = input .split(',') .filter(tag => tag.trim() !== '') .map(tag => tag.trim()) tagsEl.innerHTML = '' tags.forEach(tag => { const tagEl = document.createElement('span') tagEl.classList.add('tag') tagEl.innerText = tag tagsEl.appendChild(tagEl) }) } function randomSelect() { const times = 30 const interval = setInterval(() => { const randomTag = pickerRandomTag() if (randomTag !== undefined) { highlightTag(randomTag) setTimeout(() => { unHighlightTag(randomTag) }, 100) } }, 100) setTimeout(() => { clearInterval(interval) setTimeout(() => { const randomTag = pickerRandomTag() highlightTag(randomTag) },100) }, times * 100) } 复制代码
-
项目总结
(1)CSS:
focus
伪类选择器,当聚焦于文本框时,通过设置outline:none
,控制轮廓属性
的展示方式(2)JS: 通过
split、filter、map
对文本框输入值进行处理,document.createElement
动态创建元素,setInterval和setTimeout
定时器控制随机选择的动画效果
14. Animated Navigation(动画导航)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css nav.active .icon .line1 { transform: rotate(-765deg) translateY(5.5px); } nav.active .icon .line2 { transform: rotate(765deg) translateY(-5.5px); } // js toggle.addEventListener('click', () => nav.classList.toggle('active')) 复制代码
-
项目总结
(1)CSS:
rotate
控制旋转角度,translateY
控制在Y轴的平移像素值(2)JS: 监听click点击事件,通过
toggle()
函数动态控制active类的添加或移除
15. Incrementing Counter(递增计数器)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// html <div class="counter-container"> <i class="fab fa-twitter fa-3x"></i> <div class="counter" data-target="12000"></div> <span>Twitter Followers</span> </div> // js const updateCounter = () => { const target = +counter.getAttribute('data-target') const c = +counter.innerText const increment = target / 200 if (c < target) { counter.innerText = `${Math.ceil(c + increment)}` setTimeout(updateCounter, 1) } else { counter.innerText = target } } 复制代码
-
项目总结
(1)HTML:
data-target
自定义目标值属性(2)JS: 使用
+
号把值转为隐式转为数字类型【关于加号的隐式转换】,通过setTimeout
定时器实现数字的增量相加
16. Drink Water(喝水)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .cups{ display:flex; flex-wrap:wrap; align-items:center; justify-content:center; width:280px; } // js function highlightCups(idx) { // 点击两次最后一个则索引减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-- } // 小于等于当前索引值的增加full类,否则移除 smallCups.forEach((cup, idx2) => { if (idx2 <= idx) { cup.classList.add('full') } else { cup.classList.remove('full') } }) updateBigCup() } 复制代码
-
项目总结
(1)CSS: 常规flex布局,
flex-wrap:wrap
控制换行(2)JS: 通过监听每个杯子的
click
事件,传递给highlightCups索引值
,用来控制full类
的添加或移除,updateBigCup()方法则通过含有full类的元素控制大水杯的高度和百分比
17. Movie App(电影应用)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .overview{ position:absolute; left:0; bottom:0; right:0; max-height:100%; transform:translateY(101%); ... } .movie:hover .overview { transform:translateY(0) } // js form.addEventListener('submit', e => { e.preventDefault() // 组阻止表单提交时重新加载页面 console.log(e.preventDefault()) const searchTerm = search.value if (searchTerm && searchTerm !== '') { getMovies(SEARCH_API + searchTerm) search.value = '' } else { window.location.reload() } }) 复制代码
-
项目总结
(1)CSS:
translateY(101%)
表示向下平移,>100% 则隐藏
,当触发hover伪类时,变成transformY(0)
显示(2)JS: 监听
submit
提交事件,当搜索框值不为空时,触发getMovies()
方法
18. Background Slider(背景滑块)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css body::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100vh; background-color: rgba(0, 0, 0, 0.7); z-index: -1; } // js rightBtn.addEventListener('click', () => { activeSlide++ if (activeSlide > slides.length - 1) { activeSlide = 0 } setBgToBody() setActiveSlide() }) 复制代码
-
项目总结
(1)CSS: 使用
before
伪元素设置body的堆叠层级z-index:-1
(2)JS: 监听右箭头的
click
点击事件,如果当前激活滑块超过总滑块个数,则重新从0开始
19. Theme Clock(主题时钟)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .center-point::after { content: ''; background-color: var(--primary-color); width: 5px; height: 5px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 50%; } // js function setTime() { const time = new Date(); const month = time.getMonth() // 月份:0-11 const day = time.getDay() // 星期:0-6 const date = time.getDate() // 日:1-31 const hours = time.getHours() // 小时:0-23 const hoursForClock = hours >= 13 ? hours % 12 : hours; const minutes = time.getMinutes() // 分钟:0-59 const seconds = time.getSeconds() // 秒:0-59 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>` } 复制代码
-
项目总结
(1)CSS: 通过绝对定位
absolute
和transform:translate(-50%,-50%)
实现水平垂直居中
的效果(2)JS: 通过
new Date()
日期对象获取带有格式的时间,通过调用和例5
相同的scale()
工具函数实现指针转动效果
20. Button Ripple Effect(按钮涟漪效应)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css button .circle { ... animation: myScale 0.5s ease-out; } @keyframes myScale { to { transform: translate(-50%, -50%) scale(3); opacity: 0; } } // js button.addEventListener('click', function (e) { // 鼠标坐标 const x = e.pageX const y = e.pageY // 按钮最左侧的边相对于有定位的父元素的位置,没有的话则相对于body const buttonLeft = e.target.offsetLeft const buttonTop = e.target.offsetTop 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) }) 复制代码
-
项目总结
(1)CSS: 使用
@keyframes
定义动画名称,在animation
属性中使用,实现放大及透明度变为0隐藏的效果(2)JS: 通过
pageX/pageY
、offsetLeft/offsetTop
计算坐标,以此计算出circle类的top和left偏移值
21. Drag N Drop(拖拽)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// html <div class="empty"> <div class="fill" draggable="true"></div> </div> // js // 当用户开始拖动一个元素或者一个选择文本的时候触发 function dragStart() { this.className += ' hold' setTimeout(() => (this.className = 'invisible'), 0) } // 在可拖动的元素或者被选择的文本进入一个有效的放置目标时触发。 function dragEnter(e) { e.preventDefault() this.className += ' hovered' } // 当元素或者选择的文本被拖拽到一个有效的放置目标上时触发 function dragOver(e) { // 组织默认动作 e.preventDefault() } // 在拖动的元素或选中的文本离开一个有效的放置目标时被触发 function dragLeave() { this.className = 'empty' } // 在元素或选中的文本被放置在有效的放置目标上时被触发。 function dragDrop() { this.className = 'empty' this.append(fill) } // 在拖放操作结束时触发 function dragEnd() { this.className = 'fill' } 复制代码
-
项目总结
(1)HTML:
draggable
用于标识元素是否允许使用拖放操作API(2)JS: 通过监听
dragstart、dragEnter、dragOver、dragLeave、dragDrop、dragEnd
事件实现从一个可拖拽元素-->拖拽-->放置该元素
的过程
22. Drawing App(绘图应用)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .toolbox > *:last-child{ margin-left:auto; } // js function drawLine(x1, y1, x2, y2) { ctx.beginPath() // 创建一个开始路径 ctx.moveTo(x1, y1) // 开始移动位置 ctx.lineTo(x2, y2) // 行动路线 ctx.strokeStyle = color // 线条颜色 ctx.lineWidth = size * 2 ctx.stroke() // 线条绘制 } 复制代码
-
项目总结
(1)CSS:
>
为子选择器,*
表示全部子元素,:last-child
表示最后一个子元素(2)JS: 通过监听
鼠标
事件,计算x与y
的坐标,配合canvas API
实现绘图功能
23. Kinrtic Loader(永动机)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .kinetic::after { ... // 下三角形 width: 0; height: 0; border: 50px solid transparent; border-bottom-color: #fff; animation: rotateA 2s linear infinite 0.5s; } @keyframes rotateA { 0%, 25% { transform: rotate(0deg); } 50%, 75% { transform: rotate(180deg); } 100% { transform: rotate(360deg); } } 复制代码
-
项目总结
(1)CSS: 首先通过width、height、border属性实现
下三角形
,然后通过animation
和transform
实现旋转动画效果
24. Content Placeholder(骨架屏)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .animated-bg { background-image: linear-gradient( to right, #f6f7f8 0%, #edeef1 10%, #f6f7f8 20%, #f6f7f8 100% ); background-size:200% 100%; animation:bgPos 1s linear infinite; } // js animated_bgs.forEach(bg => bg.classList.remove('animated-bg')) animated_bg_texts.forEach(bg => bg.classList.remove('animated-bg-text')) 复制代码
-
项目总结
(1)CSS: 通过
linear-gradient
和animation
实现骨架屏加载效果(2)JS: 通过
setTimeout
设置2500ms后调用getData()
方法,在该方法中移除
所有关于骨架屏显示的类
25. Sticky Navbar(粘性导航栏)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .nav{ position:sticky; background-color:#222; top:0; left:0; right:0; transition:all 0.3s ease-in-out; } // js function fixNav() { if (window.scrollY > nav.offsetHeight + 150) { nav.classList.add('active') } else { nav.classList.remove('active') } } 复制代码
-
项目总结
(1)CSS: 使用
position:sticky
实现导航栏的粘性定位(2)JS: 当
滚动条高度 > 设定值时
,动态给导航添加样式类active
26. Double Vertical Slider(双垂直滑块)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .slider-container .action-buttons .down-button { transform: translateX(-100%); border-top-left-radius: 5px; border-bottom-left-radius: 5px; } // js const changeSlide=(direction)=>{ // 元素内部高度 const sliderHeight=sliderContainer.clientHeight if(direction==='up'){ activeSlideIndex++ if(activeSlideIndex > slidesLength -1){ activeSlideIndex=0 } }else if(direction==='down'){ activeSlideIndex-- if(activeSlideIndex<0){ activeSlideIndex = slidesLength-1 } } slideRight.style.transform=`translateY(-${activeSlideIndex * sliderHeight}px)` slideLeft.style.transform=`translateY(${activeSlideIndex * sliderHeight}px)` } 复制代码
-
项目总结
(1)CSS:
transform
中的所有属性都是相对于自身
进行转换(2)JS: 通过监听按钮的
click
事件,控制activeSlideIndex
的变化,实现左右两侧translateY
属性的更改,实现错位
效果
27. Toast Notification(吐司/消息条 通知)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js const types = ['info', 'success', 'error'] function getRandomType() { return types[Math.floor(Math.random() * types.length)] } 复制代码
-
项目总结
(1)JS: 通过
getRandomType
方法使用Math.floor()
和Math.random()
函数随机从types数组中返回一个类型,然后增加到notif类中,实现文字颜色
的变化
28. Github Profiles(Github 简介)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js async function getUser(username) { try { const { data } = await axios(APIURL + username) createUserCard(data) getRepos(username) } catch (err) { if (err.response.status === 404) { createUserCard('No profile with this username') } } } 复制代码
-
项目总结
(1)JS: 使用
async/await
配合axios
处理异步请求,通过调用getUser()
方法,接收用户输入要搜索的用户名
,搭配githubAPI
获取数据进行页面渲染
29. Double Click Heart(双击爱心)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .loveMe .fa-heart { position: absolute; animation: grow 0.6s linear; transform: translate(-50%, -50%) scale(0); } @keyframes grow { to { transform: translate(-50%, -50%) scale(10); opacity: 0; } } // js loveMe.addEventListener('click', e => { if (clickTime === 0) { clickTime = new Date().getTime() } else { if (new Date().getTime() - clickTime < 800) { createdHeart(e) clickTime = 0 } else { clickTime = new Date().getTime() } } }) 复制代码
-
项目总结
(1)CSS: 使用
font-awesome
图标字体库中的类名.fa-heart
实现爱心图标,配合transform和animation实现动画效果(2)JS: 通过监听
click
点击事件,判断点击的间隔时间戳
是否小于800,如果满足条件表示双击
了,则调用createdHeart()
方法创建爱心
30. Auto Text Effect(打字机效果)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js function writeText() { textEl.innerText = text.slice(0, idx) idx++ if (idx > text.length) { idx = 1 } setTimeout(writeText, speed) } 复制代码
-
项目总结
(1)JS: 通过定时器
setTimeout
设置每speed
秒调用一次writeText()
方法,利用slice()
方法进行字符的截取
31. Password Generator(密码生成器)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css #result::-webkit-scrollbar { width: 6px; height:6px; background:#fff; } // js function generatePassword(lower, upper, number, symbol, length) { let generatePassword = '' const typesCount = lower + upper + number + symbol const typeArr=[{lower},{upper},{number},{symbol}].filter(item=>Object.values(item)[0]) if(typesCount===0) return '' for(let i=0;i<length;i+=typesCount){ typeArr.forEach(type=>{ const funcName=Object.keys(type)[0] generatePassword +=randomFunc[funcName]() }) } const finalPassword=generatePassword.slice(0,length) return finalPassword } 复制代码
-
项目总结
(1)CSS: 使用
::-webkit-scrollbar
伪元素选择器去修改基于webkit
的浏览器的滚动条
样式(2)JS: 通过
Object.values()
和Object.keys()
分别获取到值数组typeArr
和属性数组
,循环遍历取出数组的第一项funcName
并将其作为参数
传给randomFunc()
方法,得到随机值
32. Good Cheap Fast(好、便宜、快)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .toggle:checked + .label .ball{ animation:slideOn 0.3s linear forwards; } @keyframes slideOn{ 0%{ transform:translateX(0) scale(1); } 50%{ transform:translateX(20px) scale(1.2); } 100%{ transform:translateX(40px) scale(1); } } // js toggles.forEach(toggle => toggle.addEventListener('change', e => doTheTrick(e.target)) ) 复制代码
-
项目总结
(1)CSS: 通过
:checked
伪类选择器和+
兄弟选择器,配合animation动画
实现选中效果(2)JS: 通过
forEach
遍历每一个toggle开关,通过监听change
改变,传递触发事件的对象
给doTheTrick()
方法
33. Notes App(笔记应用)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js // getItem() 接收一个key返回value const notes = JSON.parse(localStorage.getItem('notes')) ... textArea.addEventListener('input',(e)=>{ const {value} =e.target main.innerHTML=marked(value) updateLS() }) ... function updateLS(){ const notesText=document.querySelectorAll('textarea') const notes=[] notesText.forEach(note=>notes.push(note.value)) // 接受一个键名和值作为参数,将会把键名添加到给定的 Storage 对象中 localStorage.setItem('notes',JSON.stringify(notes)) } 复制代码
-
项目总结
(1)JS: 使用
marked.js
在线编译Markdown代码为Html并直接显示,通过localStorage.setItem()
和localStorage.getItem()
进行值的存储和获取
34. Animated Countdown(动画倒计时)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .nums span.in { transform: translate(-50%, -50%) rotate(0deg); animation: goIn 0.5s ease-in-out; } @keyframes goIn { 0% { transform: translate(-50%, -50%) rotate(120deg); } 30% { transform: translate(-50%, -50%) rotate(-20deg); } 60% { transform: translate(-50%, -50%) rotate(10deg); } 100% { transform: translate(-50%, -50%) rotate(0deg); } } // JS function runAnimation() { nums.forEach((num, idx) => { const nextToLast = nums.length - 1 num.addEventListener('animationend', e => { // 当前使用goIn动画且不是最后一个元素 if (e.animationName === 'goIn' && idx !== nextToLast) { num.classList.remove('in') num.classList.add('out') } // 当前使用goOut动画且下一个不为空 else if (e.animationName === 'goOut' && num.nextElementSibling) { num.nextElementSibling.classList.add('in') } // 最后一个元素 else { counter.classList.add('hide') finalMessage.classList.add('show') } }) }) } 复制代码
-
项目总结
(1)CSS: 使用
translate(-50%,-50%)
实现元素的水平垂直居中
,使用goIn
动画实现元素倒计时时的抖动
效果(2)JS:
nextToLast
拿到所有计时数字的个数,通过监听animationend事件
判断当前元素使用的动画名,进行类的添加或元素的隐藏与显示
35. Image Carousel(轮播图)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js function changeImage() { // 如果为最后一张,则设置idx为0,从头开始; if (idx > img.length - 1) { idx = 0 } // 如果idx为0,则设置idx为img.length-1,从最后开始 else if (idx < 0) { idx = img.length - 1 } imgs.style.transform = `translateX(${-idx * 500}px)` } 复制代码
-
项目总结
(1)JS: 通过
idx
变量判断当前图片为第几张,根据idx计算出translateX()
的值,实现图片的切换
36. Hoverboard(悬浮板)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// JS function setColor(element) { const color = getRandomColor() element.style.background = color element.style.boxShadow = `0 0 2px ${color},0 0 10px ${color}` } function removeColor(element) { element.style.background = '#1d1d1d' element.style.boxShadow = '0 0 2px #000' } function getRandomColor() { return colors[Math.floor(Math.random() * colors.length)] } 复制代码
-
项目总结
(1)JS: 通过监听鼠标的
mouseover()
和mouseout()
事件执行setColor()
方法和removeColor()
方法,设置鼠标悬浮时方块颜色
的变化
37. Pokedex(图鉴)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js const createPokemonCard = pokemon => { // 创建 div 并添加 pokemon类 const pokemonEl = document.createElement('div') pokemonEl.classList.add('pokemon') // 获取 name 和 id const name = pokemon.name[0].toUpperCase() + pokemon.name.slice(1) const id = pokemon.id.toString().padStart(3, '0') // 背景颜色 const poke_types = pokemon.types.map(type => type.type.name) const type = main_types.find(type => poke_types.indexOf(type) > -1) const color = colors[type] pokemonEl.style.backgroundColor = color // 生成元素内容 const pokemonInnerHTML = ` <div class="img-container"> <img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png"" alt="${name}"> </div> <div class="info"> <span class="number">#${id}</span> <h3 class="name">${name}</h3> <small class="type">Type: <span>${type}</span> </small> </div> ` pokemonEl.innerHTML = pokemonInnerHTML // 添加子节点 poke_container.appendChild(pokemonEl) } 复制代码
-
项目总结
(1)JS: 通过
fetch()
请求获取值后传递给createPokemonCard()
方法,通过slice、padStart、map、find
等一系列API渲染出图鉴列表,具体使用方法可查阅MDN
38. Mobile Tab Navigation(手机导航)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js listItems.forEach((item, index) => { item.addEventListener('click', () => { hideAllContents() hideAllItems() item.classList.add('active') contents[index].classList.add('show') }) }) 复制代码
-
项目总结
(1)JS: 循环遍历
listItems
,监听click
事件,首先调用hideAllContents()
和hideAllItems()
方法清空所有元素的active和show类,然后分别给被点击的元素添加active类和show类
39. Password Strength Background(密码长度背景变化)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// html <div class="my-4 text-left"> <label for="email" class="text-gray-900">Email:</label> <input class="border block w-full p-2 mt-2 rounded" type="email" name="email" id="email" placeholder="Enter Email"> </div> // js password.addEventListener('input', e => { const input = e.target.value const len = input.length const blurValue = 20 - len * 2 background.style.filter=`blur(${blurValue}px)` }) 复制代码
-
项目总结
(1)HTML: 使用
TailWindCss
简化样式编写,👉官网👈(2)JS: 监听密码输入框
input
事件,获取输入值的长度,
改变filter:blur()
属性值
40. 3d Background Boxes(3D背景框)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .box::before { content: ''; background-color: #f9ca24; position: absolute; bottom: -15px; left: 8px; height: 15px; width: 100%; transform: skewX(45deg); } // js function createBoxes() { for (let i = 0; i < 4; i++) { for (let j = 0; j < 4; j++) { const box = document.createElement('div') box.classList.add('box') box.style.backgroundPosition = `${-j * 125}px ${-i * 125}px` boxesContainer.appendChild(box) } } } 复制代码
-
项目总结
(1)CSS:
transform:skewX()
将元素在二维平面上进行水平转换
(2)JS: 通过
双层for循环
生成16
个box,并使用backgroundPosition
为每一个背景图片设置初始位置
41. Verify Account Ui(验证账户用户界面)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .code::-webkit-outer-spin-button, .code::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } // js codes.forEach((code, idx) => { code.addEventListener('keydown', e => { if (e.key >= 0 && e.key <= 9) { codes[idx].value = '' setTimeout(() => codes[idx + 1].focus(), 10) } else if (e.key === 'Backspace') { setTimeout(() => codes[idx - 1].focus(), 10) } }) }) 复制代码
-
项目总结
(1)CSS:
-webkit-appearance: none
设置输入框不应用
任何特殊样式,此处为隐藏
数字输入框的增加/减少
按钮(2)JS: 通过监听键盘的
keydown
事件,当用户输入一个数字
时,会自动清空当前输入框,然后跳到下一个
输入框,按下Backspace
(回退)键时,可跳到上一个
输入框
42. Live User Filter(实时用户过滤器)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .user-list li:not(:last-of-type) { border-bottom: 1px solid #eee; } // js filter.addEventListener('input', e => filterData(e.target.value)) function filterData(searchTerm){ listItems.forEach(item=>{ if(item.innerText.toLowerCase().includes(searchTerm.toLowerCase())){ item.classList.remove('hide') }else{ item.classList.add('hide') } }) } 复制代码
-
项目总结
(1)CSS:
li:not(:last-of-type)
设置不是最后一个li类型
的元素有下边框线
(2)JS: 监听输入框的
input
事件,接受文本框输入的值,通过forEach
循环每一个li元素中的文本,筛选出匹配项,如匹配成功则移除hide类
,反之添加hide类
43. Feedback Ui Design(用户反馈界面设计)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// JS ratingsContainer.addEventListener('click', e => { // 点击图片:检查是否有下一个兄弟元素 if ( e.target.parentNode.classList.contains('rating') && e.target.nextElementSibling ) { removeActive() e.target.parentNode.classList.add('active') selectedRating = e.target.nextElementSibling.innerHTML } // 点击文字:检查是否有上一个兄弟元素且兄弟元素为IMG else if ( e.target.parentNode.classList.contains('rating') && e.target.previousSibling && e.target.previousElementSibling.nodeName === 'IMG' ) { removeActive() e.target.parentNode.classList.add('active') selectedRating = e.target.innerHTML } }) 复制代码
-
项目总结
(1)JS: 监听
click
点击事件,判断用户点击的是图片
还是文字
,执行对应操作:给点击元素的父元素
添加active类
,获取文字值赋给selectedRating
变量
44. Custom Range Slider(自定义范围滑块)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css /* 设置滑块可滑动区域样式 */ input[type='range']::-webkit-slider-runnable-track{ background: purple; border-radius: 4px; width: 100%; height: 10px; cursor: pointer; } /* ::-webkit-slider-thumb 设置滑块样式 */ input[type='range']::-webkit-slider-thumb{ -webkit-appearance: none; height: 24px; width: 24px; background:#fff; border-radius: 50%; border: 1px solid purple; margin-top: -7px; cursor: pointer; } // js range.addEventListener('input', e => { const value = +e.target.value const label = e.target.nextElementSibling // 获取滑动区域宽度:300px(带单位) const range_width = getComputedStyle(e.target).getPropertyValue('width') console.log('range_width: ', range_width); // 获取label宽度:80px const label_width = getComputedStyle(label).getPropertyValue('width') // 去掉px,获取单纯数字 const num_width = +range_width.substring(0, range_width.length - 2) const num_label_width = +label_width.substring(0, label_width.length - 2) const max = +e.target.max const min = +e.target.min // label元素位置 const left = value * (num_width / max) - num_label_width / 2 + scale(value, min, max, 10, -10) label.style.left = `${left}px` label.innerHTML = value }) 复制代码
-
项目总结
(1)CSS: 通过伪类
::-webkit-slider-runnable-track
和::-webkit-slider-thumb自定义在Chrom 和 Safari 内核
中滑块区域和滑块的样式(2)JS: 监听
input
事件,分别获取到滑动区域宽度值、label宽度值、滑块最大值、滑块最小值
,通过scale()
方法计算出label元素的left值
,实现位置偏移
效果
45. Netfix Mobile Navigation(Netfix 移动导航)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .nav-black { background-color: rgb(34, 31, 31); width: 60%; max-width: 480px; min-width: 320px; transition-delay: 0.4s; } .nav-black.visible { transition-delay: 0s; } // js open_btn.addEventListener('click',()=>{ navs.forEach(nav=>nav.classList.add('visible')) }) close_btn.addEventListener('click',()=>{ navs.forEach(nav=>nav.classList.remove('visible')) }) 复制代码
-
项目总结
(1)CSS: 通过
transition-delay
设置导航显示的延迟时间
,实现黑、红、白三色的展示(2)JS: 监听鼠标点击
click
事件,给导航卡片动态添加/移除visible类
46. Quiz App(测验应用)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js submitBtn.addEventListener('click', () => { const answer = getSelected() if (answer) { if (answer === quizData[currentQuiz].correct) { score++ } currentQuiz++ if (currentQuiz < quizData.length) { loadQuiz() } else { quiz.innerHTML = ` <h2>You answered ${score}/${quizData.length} questions correctly</h2> <button onClick="location.reload()">Reload</button> ` } } }) 复制代码
-
项目总结
(1)JS: 监听
click
事件,通过getSelected()
方法拿到用户选择的答案,然后和正确答案比较,答对的话成绩+1
,然后通过loadQuiz()
渲染出下一题的数据
47. Testimonial Box Switcher(推荐盒切换)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .content { line-height: 28px; text-align: justify; } .progress { background-color: #fff; height: 4px; width: 100%; animation: grow 10s linear infinite; transform-origin: left; } // js let index = 1 function updateTestimonial() { const { name, position, photo, text } = testimonials[index] content.innerHTML = text userImage.src = photo username.innerHTML = name role.innerHTML = position index++ if (index > testimonials.length) { index = 0 } } setInterval(updateTestimonial,10000); 复制代码
-
项目总结
(1)CSS:
text-align:justify
设置文字两侧对齐,使用animation
和transform-origin
实现进度条
效果(2)JS: 通过ES6
{}
解构出对应数据,使用定时器setInterva
l实现每10s轮询一次内容
48. Random Image Feed(随机图)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js const urls = 'https://source.unsplash.com/random/' const rows = 5 for (let i = 0; i < rows * 3; i++) { const img = document.createElement('img') img.src = `${urls}${getRandomSize()}` container.appendChild(img) } 复制代码
-
项目总结
(1)JS: 使用image的
src
属性配合getRandomSize()
方法,通过for循环
实现每次重新加载便会随机
渲染出15张
图
49. Todo List(待办事项)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// css .input:focus{ outline-color: rgb(179,131,226); } // js function addTodo(todo) { let todoText = input.value if (todo) { todoText = todo.text } if (todoText) { const todoEl = document.createElement('li') // 对storage中的数据进行判断 if (todo && todo.completed) { todoEl.classList.add('completed') } todoEl.innerText = todoText // 监听鼠标左键点击事件 todoEl.addEventListener('click', () => { todoEl.classList.toggle('completed') updateLS() }) // 监听鼠标点击右键或者按下键盘上的菜单键 todoEl.addEventListener('contextmenu', e => { e.preventDefault() todoEl.remove() updateLS() }) // 元素追加 todosUL.appendChild(todoEl) input.value = '' updateLS() } } function updateLS() { todosEl = document.querySelectorAll('li') const todos = [] todosEl.forEach(item => { todos.push({ text: item.innerText, completed: item.classList.contains('completed'), }) }) localStorage.setItem('todos', JSON.stringify(todos)) } 复制代码
-
项目总结
(1)CSS: 通过
:focus
伪类和outline-color
设置文本框聚焦时外边框线的颜色(2)JS: 通过
localstorage
的setItem()
和getItem()
方法对文本框数据进行存储与获取
,通过监听左键click()
点击事件和右键contextmenu()
事件进行类的添加/移除
及元素的删除
50. Insect Catch Game(昆虫捕捉游戏)
-
👉项目源码👈
-
👉在线演示👈
-
代码片段
// js // 点击昆虫开始游戏 choose_insect_btns.forEach(btn => { btn.addEventListener('click', () => { const img = btn.querySelector('img') const src = img.getAttribute('src') const alt = img.getAttribute('alt') selected_insect = { src, alt } screen[1].classList.add('up') setTimeout(createInsect, 1000) startGame() }) }) // 创建昆虫 function createInsect() { const insect = document.createElement('div') insect.classList.add('insect') const { x, y } = getRandomLocation() insect.style.top = `${y}px` insect.style.left = `${x}px` insect.innerHTML = ` <img src="${selected_insect.src}" alt="${selected_insect.alt}" style="transform:rotate(${Math.random() * 360}deg)" >` insect.addEventListener('click', catchInsect) game_container.appendChild(insect) } // 捕捉昆虫 function catchInsect() { increaseScore() this.classList.add('caught') setTimeout(() => this.remove(), 2000) addInsects() } // 增加昆虫 function addInsects() { setTimeout(createInsect, 1000) setTimeout(createInsect, 1500) } 复制代码
-
项目总结
(1)JS: 通过监听
click
点击事件,存储点击"昆虫"的src
和alt
并在创建时使用,在通过getRandomLocation()
方法在屏幕中随机位置
出现昆虫,每次"捕捉
"到1个昆虫时,调用addInsects()
方法,使用setTimeout()定时器
创建两个"昆虫"
Tips:50Projects中有些项目所用API需要
科学上网🛝
才可以正常使用,文中注释可能有一些理解不到位的地方,欢迎批评指正☀️