diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6347583 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.html linguist-language=javascript diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 0000000..5834ef8 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,12 @@ +Title: 标号-题目-类型 问题简述 + +例子: + +``` +11 - Custom Video Player - readme: LocalStorage 補充 +11 - Custom Video Player - js: const 写法错误 +``` + +说明中最好提供问题所在的具体位置链接,链接获取方法: +1. Markdown 文档下,鼠标移动至你有问题的小标题,点击其左侧的链接图标,复制地址栏的地址即可。如:https://github.com/soyaine/JavaScript30/tree/master/11%20-%20Custom%20Video%20Player#图标切换 +2. 代码文档下,点击某一行左侧标号,复制地址栏地址即可,如 https://github.com/soyaine/JavaScript30/blob/master/11%20-%20Custom%20Video%20Player/index.html#L20 diff --git a/01 - JavaScript Drum Kit/README.md b/01 - JavaScript Drum Kit/README.md index fb3822a..1b02d30 100644 --- a/01 - JavaScript Drum Kit/README.md +++ b/01 - JavaScript Drum Kit/README.md @@ -1,6 +1,6 @@ # 01 JavaScript Drum Kit 中文指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 1 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 @@ -92,7 +92,7 @@ audio.play(); ### 如何使页面按钮恢复原状? -利用一个叫 [`transitionened`](https://developer.mozilla.org/zh-CN/docs/Web/Events/transitionend) 的事件,它在 CSS transition 结束后会被触发。我们就可以利用这个事件,在每次打鼓的效果(尺寸变大、颜色变化)完成之后,去除相应样式。 +利用一个叫 [`transitionend`](https://developer.mozilla.org/zh-CN/docs/Web/Events/transitionend) 的事件,它在 CSS transition 结束后会被触发。我们就可以利用这个事件,在每次打鼓的效果(尺寸变大、颜色变化)完成之后,去除相应样式。 在这个页面中,发生 `transition` 的样式属性不止一个(`box-shadow`, `transform`, `border-color`),所以需要添加一个判断语句,使每发生一次按键事件时,只去除一次样式。 diff --git a/02 - JS + CSS Clock/README.md b/02 - JS + CSS Clock/README.md index 31544fe..0ca0415 100644 --- a/02 - JS + CSS Clock/README.md +++ b/02 - JS + CSS Clock/README.md @@ -1,6 +1,6 @@ # 02 纯 JS、CSS 时钟 中文指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 2 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2016-12-21 @@ -21,7 +21,7 @@ 3. 每一秒改变一次指针状态 **涉及到的特性:** -- `transform-oragin` +- `transform-origin` - `transform: rotate()` - `transition` - `transition-timing-function: cubic-bezier(x, x, x, x)` @@ -33,10 +33,10 @@ ### CSS 部分 1. 调整指针的初始位置以及旋转的轴点 - [transform-oragin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin) + [transform-origin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin) ```css - transform-oragin: 100%; // 或者可以用 right + transform-origin: 100%; // 或者可以用 right ``` 2. 调整时钟指针跳动时的过渡效果 diff --git a/02 - JS + CSS Clock/index-SOYAINE.html b/02 - JS + CSS Clock/index-SOYAINE.html index b97c0db..af02706 100644 --- a/02 - JS + CSS Clock/index-SOYAINE.html +++ b/02 - JS + CSS Clock/index-SOYAINE.html @@ -53,7 +53,8 @@ position: relative; width: 100%; height: 100%; - transform: translateY(-3px); /* account for the height of the clock hands */ + /* transform: translateY(-3px); /* account for the height of the clock hands */ + /* canceled according to https://github.com/soyaine/JavaScript30/issues/55 */ } .hand { @@ -87,8 +88,6 @@ 0 0 0 2px rgba(0,0,0,0.1), 0 0 10px rgba(0,0,0,0.2); transform: translate(-50%, -50%); - /*transition: all .05s;*/ - } .hour-hand { @@ -104,7 +103,7 @@ width: 45%; height:5px; margin-top: -2.5px; - transition: all .1s; + transition: all .1s cubic-bezier(0.9, 0.54, 0.26, 1.68); } .second-hand { @@ -112,7 +111,7 @@ margin-top: -1px; border-bottom-left-radius: 100%; border-top-left-radius: 100%; - transition: all .05s; + transition: all .05s cubic-bezier(0.9, 0.54, 0.26, 1.68); background-color: red; } @@ -141,10 +140,10 @@ // 解决指针跳顿问题【第一种方法】 // 在发生跳顿的角度值处,将 CSS 的 `transition` 属性去掉 if (secondDeg === 90) secHand.style.transition = 'all 0s'; - else secHand.style.transition = 'all 0.05s'; + else secHand.style.transition = 'all 0.05s cubic-bezier(0.9, 0.54, 0.26, 1.68)'; if (minDeg === 90) minHand.style.transition = 'all 0s'; - else minHand.style.transition = 'all 0.1s'; + else minHand.style.transition = 'all 0.1s cubic-bezier(0.9, 0.54, 0.26, 1.68)'; // 时针间距过大不做处理 // if (hourDeg === 116.5) hourHand.style.transition = 'all 0s'; diff --git a/03 - CSS Variables/README.md b/03 - CSS Variables/README.md index fa175ef..e28b206 100644 --- a/03 - CSS Variables/README.md +++ b/03 - CSS Variables/README.md @@ -1,6 +1,6 @@ # 03 CSS Variable -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 3 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 diff --git a/04 - Array Cardio Day 1/README.md b/04 - Array Cardio Day 1/README.md index c6e08e1..ffff200 100644 --- a/04 - Array Cardio Day 1/README.md +++ b/04 - Array Cardio Day 1/README.md @@ -1,6 +1,6 @@ # 04 Array Cardio 💪 指南一 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[Soyaine](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 4 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 @@ -23,11 +23,11 @@ console.table(thing) 1. 筛选 16 世纪出生的发明家 2. 展示他们的姓和名 -3. 把他们按照年龄从大到小进行排序 +3. 把他们按照出生日期从大到小进行排序 4. 计算所有的发明家加起来一共活了多少岁 5. 按照他们活了多久来进行排序 6. 筛选出一个网页里含有某个词语的标题 -7. 按照姓氏来对发明家进行排序 +7. 按照姓氏来进行排序 8. 统计给出数组中各个物品的数量 ## 相关知识 diff --git a/04 - Array Cardio Day 1/index-SOYAINE.html b/04 - Array Cardio Day 1/index-SOYAINE.html index 6de312b..5408bdc 100644 --- a/04 - Array Cardio Day 1/index-SOYAINE.html +++ b/04 - Array Cardio Day 1/index-SOYAINE.html @@ -6,11 +6,9 @@

请按 F12 查看 Console 面板 💁

-

若无结果请刷新试试

+

若无结果请刷新试试

diff --git a/05 - Flex Panel Gallery/README.md b/05 - Flex Panel Gallery/README.md index af61333..90a1a04 100644 --- a/05 - Flex Panel Gallery/README.md +++ b/05 - Flex Panel Gallery/README.md @@ -1,6 +1,6 @@ # 05 Flex 实现可伸缩的图片墙 中文指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 5 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 diff --git a/06 - Type Ahead/README.md b/06 - Type Ahead/README.md index 38845b5..8a1f093 100644 --- a/06 - Type Ahead/README.md +++ b/06 - Type Ahead/README.md @@ -1,6 +1,6 @@ # 06 Fetch 结合 filter 实现快速匹配古诗 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 6 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 diff --git a/06 - Type Ahead/index-SOYAINE.html b/06 - Type Ahead/index-SOYAINE.html index 927d08d..890247a 100644 --- a/06 - Type Ahead/index-SOYAINE.html +++ b/06 - Type Ahead/index-SOYAINE.html @@ -14,61 +14,53 @@
  • - - - - - - - - + }).join(''); + // console.log(html); + suggestions.innerHTML = html; + } + const search = document.querySelector('.search'); + const suggestions = document.querySelector('.suggestions'); + search.addEventListener('change', displayMatches); + search.addEventListener('keyup', displayMatches); + // console.log(poetrys); + + + \ No newline at end of file diff --git a/07 - Array Cardio Day 2/README.md b/07 - Array Cardio Day 2/README.md index 767a1f7..2a2e85d 100644 --- a/07 - Array Cardio Day 2/README.md +++ b/07 - Array Cardio Day 2/README.md @@ -1,6 +1,6 @@ # 07 Array Cardio 💪 指南二 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 7 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 diff --git a/08 - Fun with HTML5 Canvas/README.md b/08 - Fun with HTML5 Canvas/README.md index f9397ef..3cf6bd9 100644 --- a/08 - Fun with HTML5 Canvas/README.md +++ b/08 - Fun with HTML5 Canvas/README.md @@ -1,6 +1,6 @@ # 08 HTML5 Canvas 实现彩虹画笔绘画板指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 8 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) ## 实现效果 diff --git a/09 - Dev Tools Domination/README.md b/09 - Dev Tools Domination/README.md index 34a8f42..8335d96 100644 --- a/09 - Dev Tools Domination/README.md +++ b/09 - Dev Tools Domination/README.md @@ -1,10 +1,10 @@ # 09 Console 调试技巧指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 9 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-01-06 -最后更新:2017-01-06 +最后更新:2017-09-24 ## 实现效果 @@ -31,7 +31,7 @@ ```js console.log("输出一个字符串 %s ", "log"); console.log("输出一个整数是 %d ", 1.23); //1 -console.log("输出一个小数是 %d ", 1.23); //1.23 +console.log("输出一个小数是 %f ", 1.23); //1.23 console.log("%c不同样式的输出效果", "color: #00fdff; font-size: 2em;"); ``` @@ -64,7 +64,7 @@ console.dir(p); 要清空已经打印输出的内容,有两种方式,一种是 JavaScript 语句: `console.clear()`。另一个是快捷键 Ctrl + L。 -### asset 方法进行测试 +### assert 方法进行测试 接受一个表达式作为参数,如果参数返回值是 false,则会输出第二个参数中的内容。 @@ -119,4 +119,7 @@ fetch("https://api.github.com/users/soyaine") 如果 timeEnd 中的名称如果和上面不一样,得到的数据是系统当前时间换算后的毫秒值。 -**~\(≧▽≦)/~调试技巧这部分就结束啦,拿起宝剑去开疆扩土吧~** \ No newline at end of file +**~\(≧▽≦)/~调试技巧这部分就结束啦,拿起宝剑去开疆扩土吧~** + +## ChangeLog +- 2017-09-24 fix typo [issue #29](https://github.com/soyaine/JavaScript30/issues/29) diff --git a/09 - Dev Tools Domination/index-SOYAINE.html b/09 - Dev Tools Domination/index-SOYAINE.html index f67fcb1..4dcbabd 100644 --- a/09 - Dev Tools Domination/index-SOYAINE.html +++ b/09 - Dev Tools Domination/index-SOYAINE.html @@ -9,7 +9,13 @@

    ×点×我×呀×

    - \ No newline at end of file + diff --git a/10 - Hold Shift and Check Checkboxes/README.md b/10 - Hold Shift and Check Checkboxes/README.md index 056f796..dbb6c5f 100644 --- a/10 - Hold Shift and Check Checkboxes/README.md +++ b/10 - Hold Shift and Check Checkboxes/README.md @@ -1,6 +1,6 @@ # 10 JS 实现 Checkbox 中按住 Shift 的多选功能 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 10 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-01-07 diff --git a/11 - Custom Video Player/README.md b/11 - Custom Video Player/README.md index ad96dd9..4dcd6f1 100644 --- a/11 - Custom Video Player/README.md +++ b/11 - Custom Video Player/README.md @@ -4,7 +4,7 @@ > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 10 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-02-06 -最后更新:2017-04-16 +最后更新:2017-09-24 ## 实现目标 * 为 `video` 元素添加自定义样式的播放控制面板 @@ -79,6 +79,7 @@ function togglePlay() { } video.addEventListener('click', togglePlay); +toggle.addEventListener('click', togglePlay); ``` 对于这种二选一的判断用三元操作符会更简洁,我们可以直接在一行里写完,像这样: @@ -240,4 +241,8 @@ progress.addEventListener('mousemove', (e) => mousedown && scrub(e)); ## **结语** 至此,我们已经实现了控制面板的绝大部分功能,最后一个留给大家自己尝试的功能是全屏播放: * 在控制面板中添加一个全屏按钮 -* 点击该按钮后可以进入/退出全屏模式 \ No newline at end of file +* 点击该按钮后可以进入/退出全屏模式 + +## ChangeLog +- 2017-02-06 完稿 +- 2017-09-24 add missing code by [issue#30](https://github.com/soyaine/JavaScript30/issues/30) diff --git a/12 - Key Sequence Detection/README.md b/12 - Key Sequence Detection/README.md index 0b84335..9956f18 100644 --- a/12 - Key Sequence Detection/README.md +++ b/12 - Key Sequence Detection/README.md @@ -1,6 +1,6 @@ # 12 键盘输入序列的验证指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 12 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-02-27 diff --git a/13 - Slide in on Scroll/README.md b/13 - Slide in on Scroll/README.md index 22bb0d5..182908c 100644 --- a/13 - Slide in on Scroll/README.md +++ b/13 - Slide in on Scroll/README.md @@ -1,6 +1,6 @@ # 13 图片随屏幕滚动而滑入滑出的效果指南 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 13 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-07-14 diff --git a/14 - JavaScript References VS Copying/README.md b/14 - JavaScript References VS Copying/README.md index 2af71f5..9fc48f1 100644 --- a/14 - JavaScript References VS Copying/README.md +++ b/14 - JavaScript References VS Copying/README.md @@ -1,10 +1,10 @@ # 14 JS中的引用与复制 -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 14 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-07-19 -最后更新:2017-07-21 +最后更新:2018-12-05 ## 实现效果 @@ -98,6 +98,8 @@ 使用 `Object.assign(target, ...sources)` 时,后来的源对象的属性值,将会覆盖它之前的对象的属性。所以可以先复制 `person` 之后,再赋给属性新的值。 + 需要注意的是:这个例子里面,我们用的数组和对象都只是一层嵌套,Lodash 有一个深度复制的方法,但使用之前需要多考虑一下。 + ```js const cap2 = Object.assign({}, person, { number: 99, age: 12 }); console.log(cap2); // Object {name: "Wes Bos", age: 12, number: 99} @@ -122,8 +124,7 @@ console.log(dev); console.log(dev2); ``` - - 最后需要注意的是:在前面的例子里面,我们用的数组和对象都只是一层嵌套,Lodash 有一个深度复制的方法,但你使用之前需要多考虑一下。 + OVER~\(^o^)/~ diff --git a/14 - JavaScript References VS Copying/index-SOYAINE.html b/14 - JavaScript References VS Copying/index-SOYAINE.html index acf3f62..4e11b80 100644 --- a/14 - JavaScript References VS Copying/index-SOYAINE.html +++ b/14 - JavaScript References VS Copying/index-SOYAINE.html @@ -101,8 +101,8 @@ } }; -// console.clear(); -// console.log(wes); + // console.clear(); + // console.log(wes); const dev = Object.assign({}, wes); diff --git a/15 - LocalStorage/README.md b/15 - LocalStorage/README.md index e18a7f7..06c2776 100644 --- a/15 - LocalStorage/README.md +++ b/15 - LocalStorage/README.md @@ -1,34 +1,80 @@ # 15 LocalStorage -> 作者:©[缉熙Soyaine](https://github.com/soyaine) +> 作者:©[未枝丫](https://github.com/soyaine) > 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 15 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) > 创建时间:2017-07-24 -最后更新:2017-07-27 +最后更新:2018-12-05 ## 实现效果 -## 知识点 -## 过程指南 +我们的目的是使网页模拟菜单的效果,在页面中添加新的菜品,而且在页面刷新之后也不清空。 -默认情况下,在表单空间拥有焦点时按下 Enter 键或者点击提交按钮,会提交表单,提交时浏览器会在将请求发送给服务器之前触发 submit 事件,可以先添加事件监听后看看效果: -```js -function addItem(e) { - console.log('hello'); -} +现在的初始页面中,点击提交按钮(Add Item)时页面默认触发 `submit` 事件,并重新加载页面,这导致重新加载之后的页面中,已丢失刚提交的内容。页面所用到的 CSS 文件已经完成了,我们需要做的是完成 JavaScript 部分的内容,以实现目标效果。 -addItems.addEventListener('submit', addItem); -``` -结果就是 Console 中闪现 hello 后刷新整个页面,这是 submit 的默认行为,在当前的场景中不适用,先去除。 -```js -function addItem(e) { - e.preventDefault(); -} -``` +这一个挑战所用时间可能比之前的稍稍长一些,但理解了 localStorage 的机制,想明白菜单的实现之后,就会一目了然了,带上耐心我们开始吧~ +## 知识点 +- Event + - [event.preventDefault](https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault) + - [eventTarget.addEventListener](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener) +- [localStorage](https://developer.mozilla.org/zh-CN/docs/Web/API/Storage/LocalStorage) + - `localStorage.setItem()` + - `localStorage.getItem()` +- [JSON](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON) + - `JSON.stringify()` + - `JSON.parse()` + +## 过程指南 -this 获取当前 form,是从 submit 事件获取到的。 +1. 默认情况下,在表单空间拥有焦点时按下 `Enter` 键或者点击提交按钮,会提交表单,提交时,浏览器会在发送请求给服务器之前触发 `submit` 事件,为了验证这个行为,我们可以添加事件监听后看看效果,此处先写一个处理函数: + ```js + function addItem(e) { + console.log('hello'); + } -this.querySelector('[name=item]'); 获取其中的输入框 + addItems.addEventListener('submit', addItem); + ``` + 点击按钮后发现 `submit` 事件触发后的结果是, Console 中闪现 hello 后刷新整个页面,这是 `submit` 的默认行为,在当前的场景中不适用,所以我们需要先去除此事件的默认行为。 + ```js + function addItem(e) { + e.preventDefault(); + } + ``` -this.reset(); -以清空 input 中正在输入的值 \ No newline at end of file +2. 下面我们开始改造 `addItem` 方法,以实现我们的功能。 + 首先在事件监听中,`this` 可以获取当前 form 即我们为其添加事件监听的 `addItem` 元素,所以可以借此方便的获取输入框中的值。在 `addItem()` 方法中获取输入,并构造一个对象 `item` 来存储这个信息,: + ```js + /* function addItem(){} 中 */ + const text = this.querySelector('[name=item]').value; + const item = { + text, // ES6中对 text: text, 的简写 + done: false // 为后续的勾选做准备 + } + ``` +3. 把对象放入此前声明好的数组 `items`,同时更新数据,包括页面中的 HTML 内容、LocalStorage。 + ```js + /* function addItem(){} 中 */ + items.push(item); + this.reset();// 以清空 input 中正在输入的值 + populateList(items, itemsList); + localStorage.setItem('items', JSON.stringify(items)); + ``` + - **HTML 内容更新** + 声明一个 `populateList` 方法来更新页面的内容。接收需要更新的数组作为参数,根据数组里的内容构造一组 `
  • ` 组成的列表,并且加上一些标识信息,以助于之后需要实现的选中功能。 + ```js + function populateList(plates = [], plateslist) { + plateslist.innerHTML = plates.map((plate, i) => { + return ` +
  • + + +
  • + `; + }).join(''); + } + ``` + - **LocalStorage 更新** + 我们利用 LocalStorage 把信息存到本地,从而可以保证刷新后内容不变。但使用 `localStorage` 的时候,直接把 `items` 传入得到的值是 [object Object],所以需要在把数据传进去之前就把内容转换成 String 类型的数据,之后读取时也使用 `JSON.parse()` 来将数据转换成 JSON 格式。 + + > 来自 [@diabloevagto](https://github.com/diabloevagto) 在 [issue](https://github.com/soyaine/JavaScript30/issues/32) 里的补充: + > 这样的原因是因为 localStorage 里面只会储存 String 类型数据,如果传入的是非 String 则会直接使用 toString 转换:`({}).toString() //"[object Object]"`,这时候就会发生异常,所以才需要先使用 `JSON.stringify()` 将 Object 转换成 json 格式,读取出来之后再利用 `JSON.parse()` 转换为 Object。 diff --git a/15 - LocalStorage/index-SOYAINE.html b/15 - LocalStorage/index-SOYAINE.html index dd78784..a450fa7 100644 --- a/15 - LocalStorage/index-SOYAINE.html +++ b/15 - LocalStorage/index-SOYAINE.html @@ -22,13 +22,16 @@

    LOCAL TAPAS

    + + +
    - - \ No newline at end of file diff --git a/16 - Mouse Move Shadow/README.md b/16 - Mouse Move Shadow/README.md new file mode 100644 index 0000000..984c795 --- /dev/null +++ b/16 - Mouse Move Shadow/README.md @@ -0,0 +1,55 @@ +# 16 文字阴影的鼠标随动效果 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 16 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-08-20 +最后更新:2017-08-21 + +## 挑战任务 + 初始文件`index-start.html`中提供了一个包含了`h1`元素的`div`元素,`h1`元素已经设置了`text-Shadow`的样式。本次编程挑战中需要完成的效果是根据用户当前的鼠标位置来操纵文字阴影的位置。 + +## 实现效果 +![结果展示](https://github.com/dashnowords/JavaScript30/blob/master/16%20-%20Mouse%20Move%20Shadow/effects.png) + +## 基本知识 +`text-shadow`样式为标准CSS3样式,用于添加**一个或多个**文字阴影,用于其的语法格式为: +```css +text-shadow: h-shadow v-shadow blur color + +``` + +## 过程指南 +1.在`script`标签中,我们使用3个变量,一个指向`div`元素,一个指向其子元素`h1`,最后一个变量`factor`用于标记阴影距离`h1`中心的距离和鼠标距离`h1`中心距离的比例,用于计算阴影的具体位置。 + +2.在`hero`元素上监听鼠标移动事件`mousemove`,并添加事件处理的回调函数`shadowMove`. +```js +hero.addEventListener('mousemove',shadowMove); +``` + +3.为获得第一个阴影的瞬时位置,需要通过鼠标位置距离`h1`中心的距离乘以`factor`系数来获得,`pos`表示鼠标当前位置的坐标,range指代`hero`元素的宽和高: +```js + var disX = parseInt((pos.x-range.x/2)*factor); + var disY = parseInt((pos.y-range.y/2)*factor); +``` +4.从事件发生的event对象中获取需要的值: +```js + var range = { + x:hero.offsetWidth, + y:hero.offsetHeight + } + var pos = { + x:e.target.offsetLeft+e.offsetX, + y:e.target.offsetTop+e.offsetY + } +``` +5.计算出`h1`元素第一个阴影位置后,可以以坐标镜像或旋转90°等不同的方式来生成其他阴影,本例中我们采用绕`h1`元素中心旋转90°的方式共生成4个阴影: +```js + text.style.textShadow = ` + ${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7), + ${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7), + ${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7), + ${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7) + `; +``` diff --git a/16 - Mouse Move Shadow/effects.png b/16 - Mouse Move Shadow/effects.png new file mode 100644 index 0000000..76a04c1 Binary files /dev/null and b/16 - Mouse Move Shadow/effects.png differ diff --git a/16 - Mouse Move Shadow/index-finished-es5.html b/16 - Mouse Move Shadow/index-finished-es5.html new file mode 100644 index 0000000..93f97b4 --- /dev/null +++ b/16 - Mouse Move Shadow/index-finished-es5.html @@ -0,0 +1,62 @@ + + + + + Mouse Shadow-ES5 + + + +
    +

    🔥WOAH!

    +
    + + + + + + diff --git a/16 - Mouse Move Shadow/index-finished-es6.html b/16 - Mouse Move Shadow/index-finished-es6.html new file mode 100644 index 0000000..8d015b9 --- /dev/null +++ b/16 - Mouse Move Shadow/index-finished-es6.html @@ -0,0 +1,72 @@ + + + + + Mouse Shadow + + + +
    +

    🔥WOAH!

    +
    + + + + + + diff --git a/16 - Mouse Move Shadow/index-start.html b/16 - Mouse Move Shadow/index-start.html new file mode 100644 index 0000000..543cb51 --- /dev/null +++ b/16 - Mouse Move Shadow/index-start.html @@ -0,0 +1,40 @@ + + + + + Mouse Shadow + + + +
    +

    🔥WOAH!

    +
    + + + + + + diff --git a/16 - Mouse Move Shadow/style.css b/16 - Mouse Move Shadow/style.css new file mode 100644 index 0000000..9d543f3 --- /dev/null +++ b/16 - Mouse Move Shadow/style.css @@ -0,0 +1,6 @@ +@charset "UTF-8"; +/** + * @dashrun (@389399428@qq.com) + * @date 2017-08-20 + * @version $1.0 + */ diff --git a/17 - Sort Without Articles/README.md b/17 - Sort Without Articles/README.md new file mode 100644 index 0000000..2bed663 --- /dev/null +++ b/17 - Sort Without Articles/README.md @@ -0,0 +1,42 @@ +# 17 数组的去前缀排序 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 17 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-08-23 +最后更新:2017-08-24 + +## 挑战任务 + 初始文件`index-start.html`中提供了一个无序列表元素,并在`script`标签中提供了一个字符串数组。请为这些字符串排序,要求去除字符串中的`The`,`A`以及`An`的前缀后再进行排序,并把排序后的结果作为列表项展示在无序列表中。 + +## 实现效果 +![结果展示](https://github.com/soyaine/JavaScript30/blob/master/17%20-%20Sort%20Without%20Articles/effects.png) + +## 基本思路 +1.基本的编程任务有两个要点,即**排序**和**展示**;
    +2.数组排序部分最外层即为`Array.sort(arr)`函数,内层实现具体排序规则;
    +3.展示部分即将排列好的新数组拼接成带有标签的HTML元素,然后一次性插入到列表中。 + +## 过程指南(以非ES6版本为例) +1.声明去前缀函数,使用`String.replace()`函数实现,第一参数使用字面量正则表达式。 +```js +function delPrefix(item){ + return item.replace(/^(The|A|An)\s{1}/,''); +} +``` +2.使用`Array.sort()`对数组进行排序,将数组中逐项使用`delPrefix()`去掉前缀后再进行对比。 +```js +var sortedbands = bands.sort(function(a,b){ + return delPrefix(a) > delPrefix(b) ? 1 : -1; +}); +``` +3.使用选择器选中无序列表`#bands`,将排序后的数组作为列表项插入其中。 +```js + document.getElementById('bands').innerHTML = '
  • '+arr.join('
  • ')+'
  • '; +``` + +## 细节知识点 +1.`Array.prototype.sort(*param*)`方法虽然有返回值,但排序结果也影响原数组,在非ES6版本的代码中,我们使用了指向原数组的变量`bands`,而在ES6版本的代码中将排序后的结果赋值给了新的变量sortedbands,从结果可以看出,两者达到了相同的目的。 + +2.在ES6版本的代码结尾处,我们修改原数组`bands`中的第一项,并在控制台打印出排序后的数组`sortedbands`,从结果可以看出`sortedbands`也受到了影响,由此可以看出`Array.prototype.sort()`函数只是返回了一个指向原数组的引用,而并没有生成新的数组。 \ No newline at end of file diff --git a/17 - Sort Without Articles/effects.png b/17 - Sort Without Articles/effects.png new file mode 100644 index 0000000..63fb6be Binary files /dev/null and b/17 - Sort Without Articles/effects.png differ diff --git a/17 - Sort Without Articles/index-finished-Dashrun-es5.html b/17 - Sort Without Articles/index-finished-Dashrun-es5.html new file mode 100644 index 0000000..0f1523d --- /dev/null +++ b/17 - Sort Without Articles/index-finished-Dashrun-es5.html @@ -0,0 +1,74 @@ + + + + + Sort Without Articles-es5 + + + + + + + + + + + diff --git a/17 - Sort Without Articles/index-finished-Dashrun-es6.html b/17 - Sort Without Articles/index-finished-Dashrun-es6.html new file mode 100644 index 0000000..5204b8f --- /dev/null +++ b/17 - Sort Without Articles/index-finished-Dashrun-es6.html @@ -0,0 +1,66 @@ + + + + + Sort Without Articles-es6 + + + + + + + + + + + diff --git a/17 - Sort Without Articles/index-start.html b/17 - Sort Without Articles/index-start.html new file mode 100644 index 0000000..9bbd250 --- /dev/null +++ b/17 - Sort Without Articles/index-start.html @@ -0,0 +1,53 @@ + + + + + Sort Without Articles + + + + + + + + + + + diff --git a/17 - Sort Without Articles/style.css b/17 - Sort Without Articles/style.css new file mode 100644 index 0000000..9d543f3 --- /dev/null +++ b/17 - Sort Without Articles/style.css @@ -0,0 +1,6 @@ +@charset "UTF-8"; +/** + * @dashrun (@389399428@qq.com) + * @date 2017-08-20 + * @version $1.0 + */ diff --git a/18 - AddingUpTimesWithReduce/README.md b/18 - AddingUpTimesWithReduce/README.md new file mode 100644 index 0000000..c493459 --- /dev/null +++ b/18 - AddingUpTimesWithReduce/README.md @@ -0,0 +1,51 @@ +# 18 使用reduce进行时间累加 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 18 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-08-25 +最后更新:2017-08-29 + +## 挑战任务 + 初始文件`index-start.html`中提供了一个包含多个列表项的无序列表元素,每一个列表项均添加了`data-time`属性,该属性用**分**和**秒**表示了时间。要求将所有的时间累加在一起,并用`时:分:秒`来表示计算的结果。 + +## 实现效果 +![结果展示](https://github.com/soyaine/JavaScript30/blob/master/18%20-%20AddingUpTimesWithReduce/effects.png) + +## 基本思路 +1.取得所有`li`中`data-time`属性的值,将时间换算为秒并累加求得总时间(单位:秒);
    +2.手动计算将总时间转化为新的格式“XX小时XX分XX秒”;
    +3.将结果显示在页面上。 + +## 过程指南(以非ES6版本为例) +1.取得所有`li`标签 +```js +var oLi = document.getElementsByTagName('li'); +``` +2.遍历`li`元素节点,取得每个`data-time`的值并以:为界将其分解为含有两个元素的数组,每个数组中含有两项,第一项为表示分钟的字符串,第二项为表示秒的字符串,将两者进行运算转化为表示秒的数字,并添加进新的数组。 +```js + for( var i = 0, len = oLi.length; i < len; i++){ + var timeItem = oLi[i].dataset['time'].split(':'); + //将时间转换为秒 + times.push(parseInt(timeItem[0],10)*60+parseInt(timeItem[1],10)); + } +``` +3.将新数组`times`中各项累加 +```js +//方法1.因为times为数组类型,故可以直接使用reduce函数进行累加 + return times.reduce(function(a,b){ + return a+b; + },0); +//方法2.不熟悉reduce函数的也可通过for循环遍历数组各项进行结果累加 +``` +4.总时间格式转换 +```js + //总时间对60取余即为不足1分钟的秒数 + var sec = seconds % 60; + //总时间除以3600并向下取整为小时数 + var hour = Math.floor(seconds/3600); + //总时间减去前两项即可获得分钟数 + var min = (seconds - 3600*hour - sec)/60; +``` +5.将结果打印在界面上即可 \ No newline at end of file diff --git a/18 - AddingUpTimesWithReduce/effects.png b/18 - AddingUpTimesWithReduce/effects.png new file mode 100644 index 0000000..82164bf Binary files /dev/null and b/18 - AddingUpTimesWithReduce/effects.png differ diff --git a/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es5.html b/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es5.html new file mode 100644 index 0000000..91abe24 --- /dev/null +++ b/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es5.html @@ -0,0 +1,216 @@ + + + + + Videos + + +

    总播放时间为:

    + + + + diff --git a/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es6.html b/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es6.html new file mode 100644 index 0000000..e7765e1 --- /dev/null +++ b/18 - AddingUpTimesWithReduce/index-finished-Dashrun-es6.html @@ -0,0 +1,205 @@ + + + + + Videos + + +

    总播放时间为:

    + + + + diff --git a/18 - AddingUpTimesWithReduce/index-start.html b/18 - AddingUpTimesWithReduce/index-start.html new file mode 100644 index 0000000..abdf4c9 --- /dev/null +++ b/18 - AddingUpTimesWithReduce/index-start.html @@ -0,0 +1,187 @@ + + + + + Videos + + + + + + diff --git a/19 - Webcam Fun/README.md b/19 - Webcam Fun/README.md new file mode 100644 index 0000000..389458d --- /dev/null +++ b/19 - Webcam Fun/README.md @@ -0,0 +1,111 @@ +# 19 Webcam Fun 中文指南 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 19 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-08-31 +最后更新:2017-09-02 + +## 挑战任务 +在`index-start.html`中提供了一个名为**Take Photo**的按钮,该按钮的点击事件会触发`takePhoto()`函数,并提供了一组标有RGBmin/max标记的`range`类型`input`元素,一个`canvas`元素,一个`video`元素,以及带有`strip`类名的空`div`元素。 +本次的编程任务: +1.通过编写javascript代码,请求调用用户的网络摄像头; +2.在页面上展示来自webcam的数据流信息; +3.并允许用户保存展示的照片; +4.及使用滑块来改变图像的色彩。 + +## 实现效果 +![结果展示](https://github.com/soyaine/JavaScript30/blob/master/19%20-%20Webcam%20Fun/effects.png) + +## 相关知识 +1.`window.navigator`对象 +`window.navigator`对象上有很多有趣的属性和方法,通过调用相应的方法可以查看到有关当前脚本运行环境(多为浏览器)的相关信息,并使用一些扩展功能。对此不熟悉的开发者可以浏览[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator)中相关API的介绍,例如使用`getBattery()`可以获取电池状态。 +2.`navigator.getUserMedia`方法 +`getUserMedia`方法为我们提供了访问网络摄像头或麦克风的权限,该方法接受一个对象作为参数,通过该对象即可获得来自多媒体设备的数据。 +3.`canvas`标签 +HTML5强大的扩展功能之一,提供了丰富的图像绘制方法,也是HTML可以作为游戏开发工具的原因之一,本次开发中使用`canvas.getContext('2d')`提供的图像操作方法. +4.像素数组 +使用`canvas`绘图上下文中的`getImageData()`获得的像素信息为一个定型数组,每个像素点的rgba色彩信息分别存放在数组中,故数据的格式为:[第一像素r值,第一像素g值,第一像素b值,第一像素a值,第二像素r值,第二像素g值......],通过各类函数公式对像素数据进行处理即可获得各类不同的滤镜效果。 + +## 基本思路 + +1.调用`navigator.getUserMedia()`方法,若调用成功则返回数据流,若调用失败则在控制台打印相关信息; +2.成功调用网络摄像头后,将返回的数据对象绑定至video标签的srcObject属性(注意此处getUserMedia()方法成功调用时触发的回调函数中会传递一个`stream`对象,该对象直接赋值给video.src是没有作用的),并当流数据开始传递时,视频自动播放; +3.点击`takePhoto()`函数时调用`canvas`绘图上下文中的`drawImage()`方法将视频中当前帧的图像绘制在canvas上,该方法第一个参数可以为图像或视频,其余参数与绘图区域尺寸相关(该方法有多种调用模式,感兴趣的读者可自行学习); +4.滤色:在全局中保存滤色范围的上下限,每次滑块数据发生改变后,使用`canvas`绘图上下文中的`getImageData()`获得画布上指定区域内各像素点的颜色数据,数据被保存在返回对象的data属性中,通过遍历修改像素色彩数组中的数据改变图像的表现,修改后调用`putImageData()`方法将像素点重新绘制在`canvas`上。 +5.点击`savePhoto()`函数时调用`canvas`的`toDataUrl()`方法即可获得canvas中的图像数据,默认格式为png,也可修改为其他格式,生成的图像数据指定给`img.src`时即可预览图片; +6.在`img`标签外添加`a`标签,并为其添加`download`属性,当点击链接时,即可将生成的图片保存至本地。 + +## 过程指南 +1.申请调用WebCam +```js +function askWebcam() { + navigator.getUserMedia = navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia; + if (navigator.getUserMedia) { + navigator.getUserMedia({ + audio: false, + video: { + width: 300, + height: 200 + } + }, function(stream) { + //若成功 + video.srcObject = stream; + video.onloadedmetadata = function(e) { + video.play(); + } + }, function(err) { + console.log('Error occured:' + err.name); + }); + } else { + console.log('this navigator doesn\'t support webcam!'); + } +} +``` +2.截图函数 `takePhoto()`,此处将原始图像数据保留一份,否则使用滤色函数处理后,被滤掉的颜色无法恢复,保存了原始图像数据后,只需要重新绘制在canvas上即可。 +```js +function takePhoto() { + ctx.drawImage(video, 0, 0, 300, 200); + //将原始截图保存, + origindata = ctx.getImageData(0,0,300,200); +} +``` +3.色彩过滤 +在所有滑块的父元素上监听`change`事件,当滑块的值发生改变时,先通过e.target.name确定是哪个颜色的范围要求发生了变化,再访问e.target.value获得变化值,通过与全局变量`filter`作比较来获得滤色值的上下限要求。 +重点难点解释如下: +```js + /*startPos表示操作像素点数据时的起点,从canvas获取到的像素数据每四个值表示一个像素点例如滑块为红色,则只需要改变像素数组中第0,4,8......个元素的值。通过target.value的首字母即可判断滤色过程应该检查的颜色*/ + const startPos = {'r':0,'g':1,'b':2}[target.name[0]]; + /*filterMin和filterMax表示相应的滤色范围上下限,若修改了红色滤色范围则取红色范围值。若修改蓝色的滤色范围,则取蓝色。checkFilter()函数将改变后的值与滤色标准`filter`进行比较,将更改滤色标准后需要调整的颜色类别(r,g,b)对应的上下限返回给结果。*/ + var tempFilter = checkFilter(target.name, target.value); + const filterMin = tempFilter.min; + const filterMax = tempFilter.max; +``` +4.保存图片 +使用`canvas.toDataUrl()`方法将`canvas`画布保存为图片,默认为png格式,该数据可作为`img.src`的值,也可利用`a`标签将其下载. +```js +//保存图片 +function savePhoto() { + img.src = canvas.toDataURL(); + a.href = canvas.toDataURL(); + a.setAttribute('download', 'handsome'); +} +``` +## 延伸思考 +####如何制作一个简易的钢铁侠面板 +1.本次的编程任务中我们已经可以调用网络摄像头获得视频数据; +2.将webcam获取到的数据绘制在canvas中时,同时将可视化面板的UI绘制在canvas中; +3.视频的本质是连续播放的图片,当图片播放的速度超过每秒24张时,我们将看到连续的动画,当我们以相应的频率重绘canvas上的图像时,即可看到合成后的视频; +4.如果要获得更加逼真的效果,可以做更多的图像识别方面的扩展延伸,本篇只做抛砖引玉,不再做更深入的探究。 +伪代码片段: +```js +setInterval(drawImg(),1000/24); +function drawImg(){ + //获得来自webcam的数据并将其绘制在canvas上 + +} +``` + diff --git a/19 - Webcam Fun/effects.png b/19 - Webcam Fun/effects.png new file mode 100644 index 0000000..03247bb Binary files /dev/null and b/19 - Webcam Fun/effects.png differ diff --git a/19 - Webcam Fun/index-finished-Dashrun.html b/19 - Webcam Fun/index-finished-Dashrun.html new file mode 100644 index 0000000..60a79b6 --- /dev/null +++ b/19 - Webcam Fun/index-finished-Dashrun.html @@ -0,0 +1,58 @@ + + + + + Get User Media Code Along! + + + + +
    +
    + + +
    + + + + + +
    + + + + + + +
    + + + + + +
    +
    +
    +

    照片区域:

    + +
    +
    +

    实时视频区域:

    + +
    +
    +

    变色画布区域:

    +
    + + 暂未保存图像 + +
    +
    +
    + + + + + + + diff --git a/19 - Webcam Fun/index-start.html b/19 - Webcam Fun/index-start.html new file mode 100644 index 0000000..5909522 --- /dev/null +++ b/19 - Webcam Fun/index-start.html @@ -0,0 +1,45 @@ + + + + + Get User Media Code Along! + + + + +
    +
    + + +
    + + + +
    +
    + + + + + + + diff --git a/19 - Webcam Fun/script.js b/19 - Webcam Fun/script.js new file mode 100644 index 0000000..b6f3a5a --- /dev/null +++ b/19 - Webcam Fun/script.js @@ -0,0 +1,119 @@ +window.onload = function() { + canvas = document.querySelector('canvas'); + video = document.querySelector('video'); + ctx = canvas.getContext('2d'); + img = document.querySelector('#myimg'); + slider = document.querySelector('.rgb'); + a = document.querySelector('a'); + //滤色范围记录 + filter = { + rmin: 0, + rmax: 255, + gmin: 0, + gmax: 255, + bmin: 0, + bmax: 255 + } + + //调用摄像头数据 + askWebcam(); + + //绑定change事件动态修改图片颜色 + slider.onchange = function(e) { + //先将canvas恢复至原始截图 + ctx.putImageData(origindata, 0, 0); + const target = e.target; + //startPos表示操作像素点数据时的起点,从canvas获取到的像素数据每四个值表示一个像素点 + //例如滑块为红色,则只需要改变像素数组中第0,4,8......个元素的值。 + const startPos = { + 'r': 0, + 'g': 1, + 'b': 2 + }[target.name[0]]; + //filterMin和filterMax表示相应的滤色范围上下限,若修改了红色滤色范围则取红色范围值。 + //若修改蓝色的滤色范围,则取蓝色。 + var tempFilter = checkFilter(target.name, target.value); + const filterMin = tempFilter.min; + const filterMax = tempFilter.max; + //从canvas获取像素数据 + var img = ctx.getImageData(0, 0, 300, 200); + + + var imgd = img.data; + //色彩过滤 + for (var i = startPos, len = imgd.length; i < len; i += 4) { + if (imgd[i] < filterMin) { + imgd[i] = filterMin; + } else if (imgd[i] > filterMax) { + imgd[i] = filterMax; + } + } + //将修改后的像素数据重绘制至canvas + ctx.putImageData(img, 0, 0); + img.src = canvas.toDataURL(); + } + +} + +//点击函数 +function takePhoto() { + ctx.drawImage(video, 0, 0, 300, 200); + //将原始截图保存 + origindata = ctx.getImageData(0, 0, 300, 200); +} + +//保存图片 +function savePhoto() { + img.src = canvas.toDataURL(); + a.href = canvas.toDataURL(); + a.setAttribute('download', 'handsome'); +} + +//申请网络摄像头操作权限 +function askWebcam() { + navigator.getUserMedia = navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia; + if (navigator.getUserMedia) { + navigator.getUserMedia({ + audio: false, + video: { + width: 300, + height: 200 + } + }, function(stream) { + //若成功 + video.srcObject = stream; + video.onloadedmetadata = function(e) { + video.play(); + } + }, function(err) { + console.log('Error occured:' + err.name); + }); + } else { + console.log('this navigator doesn\'t support webcam!'); + } +} + +//滤色函数 +function checkFilter(name, value) { + var _min; + var _max; + var _antiname = { + 'rmin': 'rmax', + 'rmax': 'rmin', + 'gmin': 'gmax', + 'gmax': 'gmin', + 'bmin': 'bmax', + 'bmax': 'bmin' + }[name] + filter[name] = value; + //当下限值超过上限时,将两者对调 + _min = Math.min(filter[name], filter[_antiname]); + _max = Math.max(filter[name], filter[_antiname]); + console.log(filter); + return { + min: _min, + max: _max + } +} \ No newline at end of file diff --git a/19 - Webcam Fun/style.css b/19 - Webcam Fun/style.css new file mode 100644 index 0000000..bf5d004 --- /dev/null +++ b/19 - Webcam Fun/style.css @@ -0,0 +1,18 @@ +html,body{ + padding: 0; + margin: 0; + height: 100%; + width: 100%; +} +.player{ + height:200px; + width:300px; +} +.hasimg{ + display:inline-block; + vertical-align: top; +} +.strip{ + height:200px; + width:300px; +} \ No newline at end of file diff --git a/20 - Speech Detection/README.md b/20 - Speech Detection/README.md new file mode 100644 index 0000000..5b62c17 --- /dev/null +++ b/20 - Speech Detection/README.md @@ -0,0 +1,52 @@ +# 20 Speech Detection 中文指南 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 20 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-09-04 +最后更新:2017-09-07 + +## 挑战任务 +本次的挑战任务,是利用浏览器内置`Web speech API`,将自己所说的话输出在页面上,仅chrome浏览器支持。 +说明:由于只有chrome浏览器实现了该接口,而语音识别需要将捕捉到的信息发送至google服务器进行处理,故本文档只提供解决思路和参考代码。 + +## 实现效果 +![结果展示](https://github.com/soyaine/JavaScript30/blob/master/20%20-%20Speech%20Detection/effects.png) + +## 相关知识 +有关语音识别接口`SpeechRecognition`的说明,可查看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechRecognition)中的相关解释。 + +## 基本思路 +1.新建语音识别对象; +2.开启语音识别服务; +3.通过监听`result`事件,实时获取捕获到的语音信息; +4.通过监听`end`事件,当一次语音捕获结束后,重新开启该功能,实现持续的语音监听功能。 + +## 过程指南 +1.由于目前只有chrome浏览器实现了此功能,故直接使用带有前缀的构造函数来构建一个语音识别对象。 +```js +var speech = new webkitSpeechRecognition(); +``` +2.设置语音识别对象的基本属性,并开启该功能。 +```js + speech.interimResults = true; + //返回即时语音,即时语音是指SpeechRecognitionResult.isFinal 为false时捕获到的信息。 + speech.lang = 'en-US';//设置语音识别类别为英语 + speech.start();//开启功能 +``` +3.监听收到结果事件,将语音识别结果输出在DOM元素上。 +```js + speech.addEventListener('result', (e) => { + const results = Array.from(e.results) + // e.results中保存的是识别的结果,本来并不是数组,需要将其转换为数组,方便使用其map、join等方法。 + .map(result => result[0]) + .map(result => result.transcript) // 获取到每一段话,是一个数组类型 + .join(''); // 将每一段话连接成字符串 + //将结果输出在页面上 + words.innerHTML = results; + } +``` + +## 延伸思考 +由于国内网络原因,可考虑使用[科大讯飞的语音识别sdk](http://www.xfyun.cn/),感兴趣的同学可自行尝试实现。 diff --git a/20 - Speech Detection/effects.png b/20 - Speech Detection/effects.png new file mode 100644 index 0000000..e4ed64a Binary files /dev/null and b/20 - Speech Detection/effects.png differ diff --git a/20 - Speech Detection/index-finished-Dashrun.html b/20 - Speech Detection/index-finished-Dashrun.html new file mode 100644 index 0000000..60328d2 --- /dev/null +++ b/20 - Speech Detection/index-finished-Dashrun.html @@ -0,0 +1,93 @@ + + + + + Speech Detection + + + +
    +
    + + + + + + + + diff --git a/20 - Speech Detection/index-start.html b/20 - Speech Detection/index-start.html new file mode 100644 index 0000000..fa472df --- /dev/null +++ b/20 - Speech Detection/index-start.html @@ -0,0 +1,60 @@ + + + + + Speech Detection + + + +
    +
    + + + + + + + + diff --git a/21 - Geolocation/README.md b/21 - Geolocation/README.md new file mode 100644 index 0000000..7c44d84 --- /dev/null +++ b/21 - Geolocation/README.md @@ -0,0 +1,52 @@ +# 21 Geolocation 中文指南 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 21 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-09-08 +最后更新:2017-09-11 + +## 挑战任务 +本次的挑战任务,是利用浏览器内置`Web Geolocation API`,将获取到的地理位置及相关坐标,与`index-start.html`中的可视化指南针连接在一起。 + +## 实现效果 +![结果展示](https://github.com/soyaine/JavaScript30/blob/master/21%20-%20Geolocation/effects.png) +由于笔记本电脑一般不带速度及方向传感器,从结果中可以看到返回值中`heading`及`speed`键值均为`null`,为演示可视化效果,代码中采用手动赋值的方式进行演示。 + +## 相关知识 +1.有关地理位置接口`Geolocation`的说明,可查看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation)中的相关解释。 + +2.`getCurrentPosition()`方法和`watchPosition()`方法 +`getCurrentPosition()`方法在调用时返回一次相关信息,`watchPosition()`方法调用后将持续返回相关信息,两个方法调用时除了传入相关的回调函数外,还需要传入`options`配置对象作为第三参数,`options`相关键值如下: +- `enableHighAccuracy`参数表示是否高精度可用,为Boolean类型,默认为false,如果开启,响应时间会变慢,同时,在手机设备上会用掉更多的流量,也就是money了。 +- `timeout`参数表示等待响应的最大时间,默认是0毫秒,表示无穷时间。 +- `maximumAge`表示应用程序的缓存时间。单位毫秒,默认是0,意味着每次请求都是立即去获取一个全新的对象内容。 + +## 过程指南 +1.使用`getCurrentPosition()`方法获得相关信息 +```js + if(navigator.geolocation){ + navigator.geolocation.getCurrentPosition(success, error, options); + }else{ + console.log('Your broswer does not support the Geolocation API'); + } +``` +2.当成功返回结果时,在控制台输出结果,并根据结果对相应的DOM元素进行样式调整 +```js +function success(pos) { + console.log(pos); + var crd = pos.coords; + console.log('Your current position is:'); + console.log('Latitude : ' + crd.latitude); + console.log('Longitude: ' + crd.longitude); + console.log('More or less ' + crd.accuracy + ' meters.'); + + //改变传感器速度值和罗盘的指向 + speed.innerHTML = crd.speed; + arrow.style.transform = `rotate(${crd.heading}deg)`; + +}; +``` + + diff --git a/21 - Geolocation/effects.png b/21 - Geolocation/effects.png new file mode 100644 index 0000000..c06ca86 Binary files /dev/null and b/21 - Geolocation/effects.png differ diff --git a/21 - Geolocation/index-finished-Dashrun.html b/21 - Geolocation/index-finished-Dashrun.html new file mode 100644 index 0000000..86ebe3b --- /dev/null +++ b/21 - Geolocation/index-finished-Dashrun.html @@ -0,0 +1,100 @@ + + + + + Document + + + + + + +

    + 0 + KM/H +

    + + + + + diff --git a/21 - Geolocation/index-start.html b/21 - Geolocation/index-start.html new file mode 100644 index 0000000..f3ed587 --- /dev/null +++ b/21 - Geolocation/index-start.html @@ -0,0 +1,63 @@ + + + + + Document + + + + + + +

    + 0 + KM/H +

    + + + + + diff --git a/22 - Follow Along Link Highlighter/README.md b/22 - Follow Along Link Highlighter/README.md new file mode 100644 index 0000000..36bd906 --- /dev/null +++ b/22 - Follow Along Link Highlighter/README.md @@ -0,0 +1,74 @@ +# 22 Follow Along Link Highliter 中文指南 + +> 本篇作者:©[大史不说话](https://github.com/dashnowords)——Chinasoft Frontend Web Developer + +> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 22 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) + +> 创建时间:2017-09-12 +最后更新:2017-09-16 + +## 挑战任务 +初始文档`index-start.html`提供了一组使用`