JavaScript 30 Days Challenge

本文最后更新于:2022-09-21 01:51:47 UTC+08:00

项目地址:https://github.com/ligen131/JavaScript30

教程官网:https://javascript30.com/

本文记录做 Challenge 时的一些笔记和对教程方法的部分优化与拓展。

目录

项目 效果演示 主要内容
01 - JavaScript Drum Kit Github Page document.querySelector, element.addEventListener
02 - JS and CSS Clock Github Page CSS 动画 transform, transition
03 - CSS Variables Github Page CSS 自定义变量并修改其值 document.documentElement.style.setProperty
04 - Array Cardio Day 1 Github Page JavaScript 数组相关操作 filter(), map(), sort(), reduce()
05 - Flex Panel Gallery Github Page CSS flex
06 - Type Ahead Github Page fetch(), 正则表达式 RegExp, CSS perspective()
07 - Array Cardio Day 2 Github Page JavaScript 数组相关操作 some(), every(), find(), findIndex(), splice(), slice(), ... 扩展运算符
08 - Fun with HTML5 Canvas Github Page HTML5 canvas, CSS hsl() 等颜色函数, 鼠标事件

01 - JavaScript Drum Kit

发现问题:键盘按住不放会导致放开后按键(key) classList 一直处于 "key playing" 激活状态状态卡死,如下图。

解决:将 classList.add() 改为切换。

1
key.classList.toggle("playing");

拓展:增加了鼠标点击按键播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function playSound(event) {
var audio, key, keyCode;
if (event.type === "click") { // 鼠标按下
key = this;
keyCode = key.attributes.getNamedItem("data-key").value;
audio = document.querySelector(`audio[data-key="${keyCode}"]`);
}
else {
keyCode = event.keyCode;
audio = document.querySelector(`audio[data-key="${keyCode}"`);
}
if (!audio) return;
audio.currentTime = 0;
audio.play();
if (!key) key = document.querySelector(`div[data-key="${keyCode}"]`);
key.classList.toggle("playing");
}

keys.forEach((key) => {
key.addEventListener("click", playSound);
})

02 - JS and CSS Clock

JS 中直接修改 style 会嵌入 HTML 中,具有除 !important 之外的最高优先级。

设置定时任务(Heart Beat)

1
setInterval(Clock, 1000);

问题:每过一分钟,秒针都会从 450deg 瞬移到 90deg,效果是逆时针快速地转了一整圈,无法和下一分钟平滑过渡。分针、时针在分别过一小时、12 小时时同理。

解决:可以用变量记录已经转过的度数,每秒对应指针加上相应的度数即可,效果是跨过 0 秒时会从 450deg 变成 456deg,可以平滑过渡。或者,对 0 秒进行特判,使指针 style.transitionDuration = "0s"

第一种方案实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const HourHand = document.querySelector('.hour-hand');
const MinHand = document.querySelector('.min-hand');
const SecondHand = document.querySelector('.second-hand');
var baseTime = 0;
var sRotated = 90, mRotated = 90, hRotated = 90;
async function syncClock() {
const now = new Date();
baseTime = Math.floor(now.getTime() / 1000);
const s = now.getSeconds();
const m = now.getMinutes();
const h = now.getHours();
hRotated = (h % 12) * (360 / 12) + m * (360 / 12 / 60) + s * (360 / 12 / 60 / 60) + 90;
mRotated = m * (360 / 60) + s * (360 / 60 / 60) + 90;
sRotated = s * (360 / 60) + 90;
HourHand.style.transform = `rotate(${hRotated}deg)`;
MinHand.style.transform = `rotate(${mRotated}deg)`;
SecondHand.style.transform = `rotate(${sRotated}deg)`;
}
function clock() {
const now = Math.floor(new Date().getTime() / 1000);
const delta = now - baseTime;
const h = hRotated + delta * (360 / 12 / 60 / 60);
const m = mRotated + delta * (360 / 60 / 60);
const s = sRotated + delta * (360 / 60);
console.log(h, m, s, delta, baseTime);
HourHand.style.transform = `rotate(${h}deg)`;
MinHand.style.transform = `rotate(${m}deg)`;
SecondHand.style.transform = `rotate(${s}deg)`;
}
async function init() {
await syncClock();
setInterval(clock, 1000);
}
init();

03 - CSS Variables

CSS 图片模糊

1
2
3
img {
filter: blur(10px);
}

问题:input.addEventListener 的事件若使用教程中的 change 会导致调色板需要选色结束退出后才生效(部分浏览器),使用 input 可以实时生效。

1
inputs.forEach((input) => input.addEventListener("input", handleUpdate));

一开始用之前的 addEventListenerstyle 手动更改。

1
2
3
4
spacing.addEventListener("input", function(e) {
const value = e.srcElement.value;
img.style.borderWidth = `${value}px`;
});

04 - Array Cardio Day 1

ES6 直接返回函数语法 (value) => value * value

Array.prototype.sort(compareFunc) 比较函数有两个参数 a, b,需要交换(a 在 b 后)返回大于 0 的值,不需交换(a 在 b 前)返回小于 0 的值,保持原样(依浏览器而定,有些 a 在 b 前,有些 a 在 b 后)返回 0。一般而言,需要交换返回 1,无需交换返回 -1。如下列升序排序数组

1
2
3
const arr = [2, 7, 5];
console.log(arr.sort((a, b) => a > b ? 1 : -1));
// output: [2, 5, 7]

JS 中 const 数组经 arr.sort() 排序后仍然改变其值的顺序。🤔

贴几个 flex 相关链接

第一次知道 CSS 还有这么复杂的东西,看起来 CSS 引擎也是一个巨大的工程。

惯例先把图片存到本地,云端加载太慢。

问题:过快连续点击两次(双击)会导致无法触发 transitionend 事件,导致 panel.classList.toggle('active')panel 处于 open 状态时仍然触发上下文字的移动,效果如下图。(左图:均未处于 open 状态,但左边 LET'S 被双击导致错误地处于 active 状态;右图:均处于 open 状态,但左边 LET'S 被双击导致错误地处于非 active 状态)

解决:分是否处于 open 状态结束的 transitionend 事件两种情况讨论,当触发 transitionend 事件时判断 panel.classList 是否存在 open 类(下面实现用 querySelector() 判断是否存在处于 open 状态的 panel)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const panels = document.querySelectorAll('.panel');
panels.forEach((panel) => {
panel.addEventListener('click', (event) => {
panel.classList.toggle('open');
});
panel.addEventListener('transitionend', (event) => {
if (!event.propertyName.includes('flex')) return;
let first_child = panel.querySelector('.open p:first-child'), last_child;
if (first_child) {
last_child = panel.querySelector('.open p:last-child');
first_child.classList.add('active');
last_child.classList.add('active');
} else {
first_child = panel.querySelector('p:first-child');
last_child = panel.querySelector('p:last-child');
first_child.classList.remove('active');
last_child.classList.remove('active');
}
});
});

06 - Type Ahead

表格 CSS 样式用到了 perspactive 透视,值越大代表离 z 平面越远。参考:https://css-tricks.com/almanac/properties/p/perspective/;Mozilla 官方文档:https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective

由于涉及到大小写模糊查找的问题,includes(), match(), replace(string, string) 都区分大小写,故只能用正则表达式解决。modifiers 包含 "i" 可以使其不区分大小写。

1
2
3
4
var patt = new RegExp(pattern, modifiers);
// For example
var patt = new RegExp("\\w+", "ig");
var patt = /\w+/ig;

07 - Array Cardio Day 2

对象中的扩展运算符 ... 用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。

1
2
3
4
5
6
let bar = { a: 1, b: 2 };
let baz = { ...bar, c: 3 }; // { a: 1, b: 2, c: 3 }

bar = [1, 2, 3];
baz = [...bar, 4]; // [1, 2, 3, 4]
baz = [bar, 4]; // [[1, 2, 3], 4]

08 - Fun with HTML5 Canvas

HTML5 Canvas 教程:https://blog.csdn.net/u012468376/article/details/73350998

CSS 常用颜色函数:https://cloud.tencent.com/developer/article/1849536

CSS hsl()https://blog.csdn.net/liuhao9999/article/details/125973912


JavaScript 30 Days Challenge
https://ligen.life/2022/Javascript30/
作者
ligen131
发布于
2022年9月13日
更新于
2022年9月21日
许可协议