8

50天用JavaScript完成50个web项目,我学到了什么?

 2 years ago
source link: https://segmentfault.com/a/1190000040481518
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.Expanding Cards

效果如图所示:

  1. CSS:弹性盒子布局中的flex属性:让所有弹性盒模型对象的子元素都有相同的长度,且忽略它们内部的内容。
  2. JavaScript:利用[].filter.call()方法可快速实现简单的选项卡切换。如上述示例源码:
const panelItems = document.querySelectorAll(".container > .panel");
panelItems.forEach(item => {
    item.addEventListener('click',() => {
        [].filter.call(item.parentElement.children,el => el !== item).forEach(el => el.classList.remove('active'));
        item.classList.add('active')
    });
});

2.Progress Steps

效果如图所示:

2.gif

  1. CSS:CSS变量的用法以及弹性盒子垂直水平居中外加伪元素的用法。
  2. JavaScript:计算进度条的宽度,类名的操作。如上述示例部分源码如下:
function handleClass(el){
    let methods = {
        addClass,
        removeClass
    };
    function addClass(c){
        el.classList.add(c);
        return methods;
    };
    function removeClass(c){
        el.classList.remove(c);
        return methods;
    }
    return methods
}

3.Rotating Navigation Animation

效果如图所示:

3.gif

  1. CSS:CSS弹性盒子布局加rotate动画。
  2. JavaScript:添加和移除类名的操作。如上述示例部分源码如下:
const addClass = (el,className) => el.classList.add(className);
const removeClass = (el,className) => el.classList.remove(className);

4.hidden-search-widget

效果如图所示:

4.gif

  1. CSS:CSS过渡动画 + 宽度的更改 + inputplaceholder样式。
  2. JavaScript:添加和移除类名的操作。如上述示例部分源码如下:
.search.active > .input {
    width: 240px;
}
.search.active > .search-btn {
    transform: translateX(238px);
}
.search > .search-btn,.search > .input {
    border: none;
    width: 45px;
    height: 45px;
    outline: none;
    transition: all .3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
    border-radius: 8px;
}
searchBtn.addEventListener('click',() => {
    searchContainer.classList.toggle('active');
    searchInput.focus();
})

5.Blurry Loading

效果如图所示:

5.gif

  1. CSS:CSS filter属性的用法;。
  2. JavaScript:定时器加动态设置样式。如上述示例部分源码如下:
let load = 0;
let timer = null;
let blurryLoadingHandler = function(){
    load++;
    if(load > 99){
        clearTimeout(timer)
    }else{
        timer = setTimeout(blurryLoadingHandler,20);
    }
    text.textContent = `页面加载${ load }%`;
    text.style.opacity = scale(load,0,100,1,0);
    bg.style.filter = `blur(${scale(load,0,100,20,0)}px)`;
}
blurryLoadingHandler();

这里有一个非常重要的工具函数(后续有好几个示例都用到了这个工具函数),如下所示:

const scale = (n,inMin,inMax,outerMin,outerMax) => (n - inMin) * (outerMax - outerMin) / (inMax - inMin) + outerMin;

这个工具函数的作用就是将一个范围数字映射到另一个数字范围。比方说,将1 ~ 100的数字范围映射到0 ~ 1之间。
详情

6.Scroll Animation

效果如图所示:

6.gif

  1. CSS:CSS位移动画。
  2. JavaScript:动态创建元素+元素滚动事件监听+视图可见区域的判断 + 防抖函数。如上述示例部分源码如下:
function debounce(fn,time = 100){
    let timer = null;
    return function(){
        if(timer)clearTimeout(timer);
        timer = setTimeout(fn,time);
    }
}
let triggerBottom = window.innerHeight / 5 * 4;
boxElements.forEach((box,index) => {
   const top = box.getBoundingClientRect().top;
   if(top <= triggerBottom){
       box.classList.add('show');
   }else{
      box.classList.remove('show');
   }
})

7. Split Landing Page

效果如图所示:

7.gif

  1. CSS:CSS过渡特效 + 弹性盒子垂直水平居中 + CSS定位 + 宽度的更改。
  2. JavaScript:鼠标悬浮事件 + 类名的操作。如上述示例部分源码如下:
HTMLElement.prototype.addClass = function(className) {
    this.classList.add(className);
};
HTMLElement.prototype.removeClass = function(className){
    this.classList.remove(className);
}
const on = (el,type,handler,useCapture = false) => {
    if(el && type && handler) {
        el.addEventListener(type,handler,useCapture);
    }
}
on(leftSplit,'mouseenter',() => container.addClass('hover-left'));
on(leftSplit,'mouseleave',() => container.removeClass('hover-left'));
on(rightSplit,'mouseenter',() => container.addClass('hover-right'));
on(rightSplit,'mouseleave',() => container.removeClass('hover-right'));

从这个示例,我也知道了mouseenter、mouseleavemouseover、mouseout的区别,总结如下:

  1. enter只触发1次,只有等到鼠标离开了目标元素之后再进入才会继续触发,同理leave也是如此理解。而over与out就是不断的触发。
  2. enter进入子元素,通过e.target也无法区分是移入/移出子元素还是父元素,而over与out则可以区分。

8.Form Wave

效果如图所示:

8.gif

  1. CSS:CSS渐变 + 弹性盒子垂直水平居中 + CSS过渡动画 + CSS位移变换 + 关注焦点伪类选择器与同级元素选择器的用法。
  2. JavaScript:字符串替换成标签 + 动态创建元素。如上述示例部分源码如下:
const createLetter = v => v.split("").map((letter,idx) => `<span style="transition-delay:${ idx * 50 }ms">${ letter }</span>`).join("");

9.Sound Board

效果如图所示:

9.gif

  1. CSS:CSS渐变 + 弹性盒子垂直水平居中 + 基本样式。
  2. JavaScript:动态创建元素 + 播放音频(audio标签)。如上述示例部分源码如下:
sounds.forEach(sound => {
    const btn = create('button');
    btn.textContent = sound;
    btn.type = "button";
    const audio = create('audio');
    audio.src = "./audio/" + sound + '.mp3';
    audio.id = sound;
    btn.addEventListener('click',() => {
        stopSounds();
        $('#' + sound).play();
    });
    buttonContainer.appendChild(btn);
    buttonContainer.insertAdjacentElement('beforebegin',audio);
});

10. Dad Jokes

效果如图所示:

10.gif

  1. CSS:CSS渐变 + 弹性盒子垂直水平居中 + 基本样式。
  2. JavaScript:事件监听 + fetch API请求接口。如上述示例部分源码如下:
const api = 'https://icanhazdadjoke.com';
const on = (el,type,handler,useCapture = false) => {
    if(el && type && handler){
        el.addEventListener(type,handler,useCapture);
    }
}
on(getJokeBtn,'click',request);
request();
async function request(){
    const res = await fetch(api,headerConfig);
    const data = await res.json();
    content.innerHTML = data.joke;
}

11. Event Keycodes

效果如图所示:

11.gif

  1. CSS:CSS渐变 + 弹性盒子垂直水平居中 + 基本样式。
  2. JavaScript:键盘事件监听 + 事件对象的属性。如上述示例部分源码如下:
const container = document.querySelector('#container');
window.addEventListener("keydown",event => {
    createKeyCodeTemplate(event);
});

function createKeyCodeTemplate(e){
    const { key,keyCode,code } = e;
    let template = "";
    [
        {
            title:"event.key",
            content:key === " " ? "Space" : key
        },
        {
            title:"event.keyCode",
            content:keyCode
        },
        {
            title:"event.code",
            content:code
        }
    ].forEach(value => {
        template += `<div class="key"><small>${ value.title }</small>${ value.content }</div>`;
    });
    container.innerHTML = template;
}

12. Faq Collapse

效果如图所示:

12.gif

  1. CSS:font-awesome字体库的使用 + 伪元素选择器 + 定位 + CSS渐变 + 基本样式。
  2. JavaScript:动态创建元素 + 类名的切换 + 事件代理。如上述示例部分源码如下:
const faqs = document.querySelectorAll('.faq-container > .faq');
container.addEventListener('click',e => {
   if(e.target.className.indexOf('faq-icon') > -1){
      faqs[[].indexOf.call(faqs,e.target.parentElement)].classList.toggle('active');
   }
});

13. Random Choice Picker

效果如图所示:

13.gif

  1. CSS:基本样式。
  2. JavaScript:动态创建元素 + 类名的切换 + 延迟定时器的用法 + 随机数 + 键盘事件的监听。如上述示例部分源码如下:
function createTags(value,splitSymbol){
    if(!value || !value.length)return;
    const words = value.split(splitSymbol).map(v => v.trim()).filter(v => v);
    tags.innerHTML = '';
    words.forEach(word => {
        const tag = document.createElement('span');
        tag.classList.add('tag');
        tag.innerText = word;
        tags.appendChild(tag);
    })
}
function randomTag(){
    const time = 50;
    let timer = null;
    let randomHighLight = () => {
        const randomTagElement = pickerRandomTag();
        highLightTag(randomTagElement);
        timer = setTimeout(randomHighLight,100);
        setTimeout(() => {
            unHighLightTag(randomTagElement);
        },100);
    }
    randomHighLight();
    setTimeout(() => {
        clearTimeout(timer);
        setTimeout(() => {
            const randomTagElement = pickerRandomTag();
            highLightTag(randomTagElement);
        },100);
    },time * 100);
}
function pickerRandomTag(){
    const tagElements = document.querySelectorAll('#tags .tag');
    return tagElements[Math.floor(Math.random() * tagElements.length)];
}

14. Animated Navigation

效果如图所示:

14.gif

  1. CSS:基本样式 + 定位 + 位移变换以及角度旋转。
  2. JavaScript:类名的切换。如上述示例部分源码如下:
toggle.addEventListener('click',() => nav.classList.toggle('active'));

15. Incrementing Counter

效果如图所示:

15.gif

  1. CSS:基本样式 + CSS渐变 + font-awesome字体库的使用。
  2. JavaScript:动态创建元素 + 定时器实现增量相加。如上述示例部分源码如下:
function updateCounterHandler() {
    const counters_elements = document.querySelectorAll('.counter');
    counters_elements.forEach(element => {
        element.textContent = '0';
        const updateCounter = () => {
            const value = +element.getAttribute('data-count');
            const textContent = +element.textContent;
            const increment = value / 100;
            if (textContent < value) {
                element.textContent = `${Math.ceil(increment + textContent)}`;
                setTimeout(updateCounter, 10);
            } else {
                element.textContent = value;
            }
        }
        updateCounter();
    });
}

16. Drink Water

效果如图所示:

16.gif

  1. CSS:基本样式 + CSS渐变 + CSS过渡特效。
  2. JavaScript:正则表达式 + 循环 + 样式与内容的设置。如上述示例部分源码如下:
if (actives.length === l) {
                setHeightVisible('0', 'hidden', '350px', 'visible');
                setTextContent("100%", "0L");
            } else if (actives.length === 0) {
                setHeightVisible('350px', 'visible', '0', 'hidden');
                setTextContent("12.5%", "2L");
            } else {
                const h1 = unitHei * (l - actives.length) + 'px';
                const h2 = unitHei * actives.length + 'px';
                setHeightVisible(h1, 'visible', h2, 'visible');
                const t1 = (unitHei * actives.length / 350) * 100 + "%";
                const t2 = (cups[i].textContent.replace(/ml/, "").replace(/\s+/, "") - 0) * (l - actives.length) / 1000 + 'L';
                setTextContent(t1, t2);
}

17. movie-app

效果如图所示:

17.gif

  1. CSS:基本样式 + CSS渐变 + CSS过渡特效 + 清除浮动。
  2. JavaScript:接口请求 + 键盘事件的监听。如上述示例部分源码如下:
search.addEventListener('keydown',e => {
        if(e.keyCode === 13){
            let value = e.target.value.replace(/\s+/,"");
            if(value){
                getMovies(SEARCH_API + value);
                setTimeout(() => {
                    e.target.value = "";
                },1000);
            }else{
                window.location.reload(true);
            }
        }
    })

PS:这个示例效果由于接口访问受限,需要翻墙访问。

18. background-slider

效果如图所示:

18.gif

  1. CSS:基本样式 + CSS渐变 + 阴影效果 + 定位 + 伪元素选择器。
  2. JavaScript:背景设置与类名的操作。如上述示例部分源码如下:
let currentActive = 0;
function setBackgroundImage(){
    const url = slideItems[currentActive].style.backgroundImage;
    background.style.backgroundImage = url;
}
function setSlideItem(){
    const currentSlide = slideItems[currentActive];
    const siblings = [].filter.call(slideItems,slide => slide !== currentSlide);
    currentSlide.classList.add('active');
    siblings.forEach(slide => slide.classList.remove('active'));
}

19. theme-clock

效果如图所示:

19.gif

  1. CSS:基本样式 + CSS变量 + 阴影效果 + 定位。
  2. JavaScript:中英文的切换以及主题模式的切换,还有日期对象的处理。如上述示例部分源码如下:
function setCurrentDate(){
    const date = new Date();
    const month = date.getMonth();
    const day = date.getDay();
    const time = date.getDate();
    const hour = date.getHours();
    const hourForClock = hour % 12;
    const minute = date.getMinutes();
    const second = date.getSeconds();
    const amPm = hour >= 12 ? langText[currentLang]['time-after-text'] : langText[currentLang]['time-before-text'];
    const w = currentLang === 'zh' ? dayZHs : days;
    const m = currentLang === 'zh' ? monthZHs : months;
    const values = [
        `translate(-50%,-100%) rotate(${ scale(hourForClock,0,11,0,360) }deg)`,
        `translate(-50%,-100%) rotate(${ scale(minute,0,59,0,360) }deg)`,
        `translate(-50%,-100%) rotate(${ scale(second,0,59,0,360) }deg)`
    ];
    [hourEl,minuteEl,secondEl].forEach((item,index) => setTransForm(item,values[index]));
    timeEl.innerHTML = `${ hour }:${ minute >= 10 ? minute : '0' + minute } ${ amPm }`;
    dateEl.innerHTML = `${w[day]},${ m[month]}<span class="circle">${ time }</span>${ langText[currentLang]['date-day-text'] }`;
    timer = setTimeout(setCurrentDate,1000);
}

PS:本示例也用到了与示例5一样的工具函数scale函数

20. button-ripple-effect

效果如图所示:

20.gif

  1. CSS:基本样式 + CSS渐变 + CSS动画。
  2. JavaScript:坐标的计算 + 偏移 + 定时器。如上述示例部分源码如下:
const x = e.clientX;
const y = e.clientY;
const left = this.offsetLeft;
const top = this.offsetTop;

const circleX = x - left;
const circleY = y - top;
const span = document.createElement('span');
span.classList.add('circle');
span.style.left = circleX + 'px';
span.style.top = circleY + 'px';
this.appendChild(span);
setTimeout(() => span.remove(),1000);

21. drawing-app

效果如图所示:

21.gif

  1. CSS:基本样式。
  2. JavaScript:canvas API + mouse事件以及计算xy坐标 + ewColorPicker的用法。如上述示例部分源码如下:
function mouseDownHandler(e){
    isPressed = true;
    x = e.offsetX;
    y = e.offsetY;
}
function throttle(fn,wait = 100){
    let done = false;
    return (...args) => {
        if(!done){
            fn.call(this,args);
            done = true;
        }
        setTimeout(() => {
            done = false;
        },wait);
    }
}

22. drag-n-drop

效果如图所示:

  1. CSS:基本样式 + CSS渐变 + CSS弹性盒子布局。
  2. JavaScript:drag event API + 随机生成图片。如上述示例部分源码如下:
function onDragStart(){
    this.classList.add('drag-move');
    setTimeout(() => this.className = "invisible",200);
}
function onDragEnd(){
    this.classList.add("drag-fill");
}

function onDragOver(e){
    e.preventDefault();
}
function onDragEnter(e){
    e.preventDefault();
    this.classList.add('drag-active');
}
function onDragLeave(){
    this.className = "drag-item";
}
function onDrop(){
    this.className = "drag-item";
    this.appendChild(dragFill);
}

23. content-placholder

效果如图所示:

23.gif

  1. CSS:基本样式 + CSS渐变 + CSS卡片样式。
  2. JavaScript:骨架屏加载效果。如上述示例部分源码如下:
animated_bgs.forEach(bg => bg.classList.remove("animated-bg"));
animated_bgs_texts.forEach(text => text.classList.remove("animated-bg-text"));

24. sticky-navbar

效果如图所示:

24.gif

  1. CSS:基本样式 + CSS渐变 + 固定定位导航。
  2. JavaScript:滚动事件。如上述示例部分源码如下:
window.addEventListener("scroll",e => {
    if(window.scrollY > nav.offsetHeight + 100) {
        nav.classList.add("active");
    }else{
        nav.classList.remove("active");
    }
})

PS:这里也做了移动端的布局。

25. double-slider

效果如图所示:

25.gif

  1. CSS:基本样式 + CSS渐变 + 位移变换。
  2. JavaScript:轮播图的实现思路,主要还是利用transformY移动端使用transformX。如上述示例部分源码如下:
function setTransform(){
    let translate = isHorizontal() ? "translateX" : "translateY";
    leftSlide.style.transform = `${ translate }(${position * currentIndex}px)`;
    rightSlide.style.transform = `${ translate }(${-(position * currentIndex)}px)`;
}

26. toast-notification

效果如图所示:

26.gif

  1. CSS:基本样式 + 消息提示框的基本样式。
  2. JavaScript:封装一个随机创建消息提示框。如上述示例部分源码如下:
function createNotification({message = null,type = null,auto = false,autoTime = 1000,left = 0,top = 0}){
    const toastItem = createEle("div");
    let closeItem = null;
    if(!auto){
        closeItem = createEle("span");
        closeItem.innerHTML = "×";
        closeItem.className = "toast-close-btn";
    }
    toastItem.className = `toast toast-${type}`;
    toastItem.textContent = message;
    if(closeItem)toastItem.appendChild(closeItem);
    container.appendChild(toastItem);
    const leftValue = (left - toastItem.clientWidth) <= 0 ? 0 : left - toastItem.clientWidth - 30;
    const topValue = (top - toastItem.clientHeight) <= 0 ? 0 : top - toastItem.clientHeight - 30;
    toastItem.style.left = leftValue + 'px';
    toastItem.style.top = topValue + 'px';
    if(auto){
        setTimeout(() => {
            toastItem.remove();
        },autoTime);
    }else{
        closeItem.addEventListener("click",() => {
            toastItem.remove();
        });
    }
    
}

消息提示框实现思路可以参考这篇文章

27. github-profiles

效果如图所示:

27.gif

  1. CSS:基本样式。
  2. JavaScript:try-catch处理异常语法 + axios API请求github API + async-await语法。如上述示例部分源码如下:
async function getRepoList(username){
    try {
        const { data } = await axios(githubAPI + username + '/repos?sort=created');
        addRepoList(data);
    } catch (error) {
        if(error.response.status == 404){
            createErrorCard("查找仓库出错!");
        }
    }
}

28. double-click-heart

效果如图所示:

28.gif

  1. CSS:基本样式 + CSS画爱心。
  2. JavaScript:事件次数的计算。如上述示例部分源码如下:
function clickHandler(e){
    if(clickTime === 0){
        clickTime = Date.now();
    }else{
        if(Date.now() - clickTime < 400){
            createHeart(e);
            clickTime = 0;
        }else{
            clickTime = Date.now();
        }
    }
}

29. auto-text-effect

效果如图所示:

29.gif

  1. CSS:基本样式。
  2. JavaScript:定时器 + 定时器时间的计算。如上述示例部分源码如下:
let time = 300 / speed.value;
writeText();
function writeText(){
    text.innerHTML = string.slice(0,idx);
    idx++;
    if(idx > string.length){
        idx = 1;
    }
    setTimeout(writeText,time);
}
speed.addEventListener("input",e => time = 300 / e.target.value);

30. password generator

效果如图所示:

30.gif

  1. CSS:基本样式布局。
  2. JavaScript:中英文切换 + 选择框事件监听 + 随机数 + copy API。如上述示例部分源码如下:
function getRandomLower(){
    // a ~ z 的code为 97 ~ 122
    // 可根据charCodeAt()方法获取
    return String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
function getRandomUpper(){
    // A ~ Z 的code为 65 ~ 90
    // 可根据charCodeAt()方法获取
    return String.fromCharCode(Math.floor(Math.random() * 26) + 65);
}
function getRandomNumber(){
    // 0 ~ 9的code为48 ~ 57
    // 可根据charCodeAt()方法获取
    return String.fromCharCode(Math.floor(Math.random() * 10) + 48);
}

31. good-cheap-fast

效果如图所示:

31.gif

  1. CSS:CSS实现开关小组件样式 + 基本布局样式。
  2. JavaScript:inputchange事件的监听。如上述示例部分源码如下:
const checkBoxElements = document.querySelectorAll(".switch-container input[type='checkbox']");
checkBoxElements.forEach(item => item.addEventListener("change",e => {
    const index = Array.from(checkBoxElements).indexOf(e.target);
    if(Array.from(checkBoxElements).every(v => v.checked)){
        if(index === 0){
            checkBoxElements[2].checked = false;
        }else if(index === 1){
            checkBoxElements[0].checked = false;
        }else{
            checkBoxElements[1].checked = false;
        }
    }
}));

32. notes-app

效果如图所示:

32.gif

  1. CSS:基本样式 + 卡片布局。
  2. JavaScript:marked.js的使用 + localStorage API存储数据 + 鼠标光标位置的计算。如上述示例部分源码如下:
 on($(".edit", note), "click", e => {
      const isFocus = textarea.getAttribute("data-focus");
      if (isFocus === "false") {
            textarea.setAttribute("data-focus","true");
            if(textarea.classList.contains("hidden")){
                textarea.classList.remove("hidden");
            }
            if(!focusStatus){
                textarea.value = notes[index].text;
            }
            const text = textarea.value.trim();
            // console.log(text);
            if (textarea.setSelectionRange) {
                textarea.focus();
                textarea.setSelectionRange(text.length, text.length);
            }else if (textarea.createTextRange) {
                const range = textarea.createTextRange();
                range.collapse(true);
                range.moveEnd('character', text.length);
                range.moveStart('character', text.length);
                range.select();
            }
     } else {
        textarea.setAttribute("data-focus","false");
        notes[index].text = textarea.value;
        main.innerHTML = marked(notes[index].text);
        setData("notes", notes);
    }
});

33. animated-countdown

效果如图所示:

33.gif

  1. CSS:基本样式 + 位移、旋转、缩放动画。
  2. JavaScript:动画事件 + 动画的创建与重置。如上述示例部分源码如下:
function runAnimation(){
    const numArr = $$("span",numGroup);
    const nextToLast = numArr.length - 1;
    numArr[0].classList.add("in");
    numArr.forEach((num,index) => {
        num.addEventListener("animationend",e => {
            const {animationName} = e;
            if(animationName === "goIn" && index !== nextToLast){
                num.classList.remove("in");
                num.classList.add("out");
            }else if(animationName === "goOut" && num.nextElementSibling){
                num.nextElementSibling.classList.add("in");
            }else{
                counter.classList.add("hide");
                final.classList.add("show");
            }
        })
    })
}
function resetAnimation(){
    counter.classList.remove("hide");
    final.classList.remove("show");
    const numArr = $$("span",numGroup);
    if(numArr){
        numArr.forEach(num => num.classList.value = '');
        numArr[0].classList.add("in");
    }
}

34. image-carousel

效果如图所示:

  1. CSS:基本样式布局。
  2. JavaScript:定时器实现轮播。如上述示例部分源码如下:
function changeCarouselItem(){
    if(currentIndex > carouselItems.length - 1){
        currentIndex = 0;
    }else if(currentIndex < 0){
        currentIndex = carouselItems.length - 1;
    }
        carousel.style.transform = `translateX(-${ currentIndex * carouselContainer.offsetWidth     }px)`;
}

PS:这里额外踩了一个定时器的坑,也就是说,比如我们使用setTimeout模拟实现setInterval方法在这里是会出现问题的,我在js代码里添加了注释说明。

// let interval = mySetInterval(run,1000);
// Why use this method can't achieve the desired effect?
// Use this method as follow to replace window.setInterval,clicked the prev button more to get the stranger effect.
// Maybe this method does not conform to the specification,So make sure to use window.setInterval.
// function mySetInterval(handler,time = 1000){
//     let timer = null;
//     const interval = () => {
//         handler();
//         timer = setTimeout(interval,time);
//     }
//     interval();
//     return {
//         clearMyInterval(){
//             clearTimeout(timer);
//         }
//     }
// }

这是因为我们用setTimeout实现的定时器并不符合规范,setInterval默认会有10ms的延迟执行。
参考规范

35. hover board

效果如图所示:

35.gif

  1. CSS:基本样式 + CSS渐变。
  2. JavaScript:动态创建元素 + 悬浮事件。如上述示例部分源码如下:
function setColor(element){
    element.style.background = `linear-gradient(135deg, ${ randomColor() } 10%, ${ randomColor() } 100%)`;
    element.style.boxShadow = `0 0 2px ${ randomColor() },0 0 10px ${ randomColor() }`;
}
function removeColor(element){
    element.style.background = `linear-gradient(135deg, #1d064e 10%, #10031a 100%)`;
    element.style.boxShadow = `0 0 2px #736a85`;
}

36. pokedex

效果如图所示:

36.gif

  1. CSS:基本样式布局 + CSS渐变。
  2. JavaScript:fetch API接口请求 + 创建卡片。如上述示例部分源码如下:
function createPokemon(pokemon){
    const pokemonItem = document.createElement("div");
    pokemonItem.classList.add("pokedex");

    const name = pokemon.name[0].toUpperCase() + pokemon.name.slice(1).toLowerCase();
    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];
    pokemonItem.style.background = `linear-gradient(135deg, ${ color } 10%, ${ randomColor() } 100%)`;
    pokemonItem.innerHTML = `
    <div class="pokedex-avatar">
        <img src="https://pokeres.bastionbot.org/images/pokemon/${pokemon.id}.png" alt="the pokemon">
    </div>
    <div class="info">
        <span class="number">#${ id }</span>
        <h3 class="name">${ name }</h3>
        <small class="type">Type:<span>${ type }</span></small>
    </div>`;
    container.appendChild(pokemonItem);
}

特别说明:接口似乎不太稳定,也许是我网络原因,图片没有加载出来。

37. mobile-tab-navigation

效果如图所示:

37.gif

  1. CSS:基本样式布局 + CSS渐变实现手机布局。
  2. JavaScript:感觉就是移动端实现一个轮播图切换,利用opacity的设置,没什么好说的。如上述示例部分源码如下:
function hideAllElement(nodeList){
    nodeList.forEach(item => item.classList.remove("active"));
}
navItems.forEach((item,index) => {
    item.addEventListener("click",() => {
        hideAllElement(navItems);
        hideAllElement(carouselItems);
        item.classList.add("active");
        carouselItems[index].classList.add("active");
    })
})

38. password-strength-background

效果如图所示:

38.gif

  1. CSS:其实主要是使用tailwind.css这个原子化CSS框架。
  2. JavaScript:监听输入框事件,然后改变背景模糊度。如上述示例部分源码如下:
password.addEventListener("input",e => {
    const { value } = e.target;
    const blur = 20 - value.length * 2;
    background.style.filter = `blur(${ blur }px)`;
});

39. 3D-background-boxes

效果如图所示:

39.gif

  1. CSS:vw、vh布局 + skew倾斜变换。
  2. JavaScript:循环 + 背景图定位的设置。如上述示例部分源码如下:
function createBox(){
    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 * 15 }vw ${ -i * 15 }vh`;
            boxContainer.appendChild(box);
        }
    }
}

40. Verify Your Account

效果如图所示:

40.gif

  1. CSS:基本样式布局 + input的一些特别样式设置。
  2. JavaScript:JavaScript focus事件。如上述示例部分源码如下:
.code-container .code:focus {
    border-color: #2397ef;
}
.code::-webkit-outer-spin-button,
.code::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.code:valid {
    border-color: #034775;
    box-shadow: 0 10px 10px -5px rgba(0, 0, 0, 0.25);
}
function onFocusHandler(nodeList){
    onFocus(nodeList[0]);
    nodeList.forEach((node,index) => {
        node.addEventListener("keydown",e => {
            // console.log(e.key);
            if(e.key >= 0 && e.key <= 9){
                nodeList[index].value = "";
                setTimeout(() => onFocus(nodeList[index + 1]),0);
            }else{
                setTimeout(() => onFocus(nodeList[index - 1]),0);
            }
        })
    });
}
function onFocus(node){
    if(!node)return;
    const { nodeName } = node;
    return nodeName && nodeName.toLowerCase() === "input" && node.focus();
}

41. live-user-filter

效果如图所示:

41.gif

  1. CSS:基本样式布局 + 滚动条样式。
  2. JavaScript:fetch API接口请求 + input事件过滤数据。如上述示例部分源码如下:
async function requestData(){
    const res = await fetch('https://randomuser.me/api?results=50');
    const { results } = await res.json();
    result.innerHTML = "";

    results.forEach(user => {
        const listItem = document.createElement("li");
        filterData.push(listItem);
        const { picture:{ large },name:{ first,last },location:{ city,country } } = user;
        listItem.innerHTML = `
            <img src="${ large }" alt="${ first + ' ' + last }" />
            <div class="user-info">
                <h4>${ first }  ${ last }</h4>
                <p>${ city },${ country }</p>
            </div>
        `;
        result.appendChild(listItem);
    })
}

42. feedback-ui-design

效果如图所示:

42.gif

  1. CSS:CSS画爱心 + 基本样式布局。
  2. JavaScript:选项卡切换功能的实现。如上述示例部分源码如下:
ratingItem.forEach(item => {
    item.addEventListener("click",e => {
        siblings(item).forEach(sib => sib.classList.remove("active"));
        item.classList.add("active");
        selectRating = item.innerText;
    })
});
send.addEventListener("click",() => {
    selectRatingItem.innerText = selectRating;
    sendPage.classList.add("hide");
    receivePage.classList.remove("hide");
});
back.addEventListener("click",() => {
    selectRating = $(".rating.active").innerText;
    selectRatingItem.innerText = $(".rating.active").innerText;
    sendPage.classList.remove("hide");
    receivePage.classList.add("hide");
});

43. custom-range-slider

效果如图所示:

43.gif

  1. CSS:input元素的样式设置(兼容写法) + 基本样式布局。
  2. JavaScript:input range元素的input事件监听。如上述示例部分源码如下:
input[type="range"]::-webkit-slider-runnable-track {
    background-image: linear-gradient(135deg, #E2B0FF 10%, #9F44D3 100%);
    width: 100%;
    height: 12px;
    cursor: pointer;
    border-radius: 4px;
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 25px;
    height: 25px;
    border-radius: 50%;
    background-image: linear-gradient(135deg, #d9e2e6 10%, #e4e1e4 100%);
    border: 1px solid #b233e4;
    cursor: pointer;
    margin-top: -6px;
}
range.addEventListener("input",e => {
    // string to the number
    const value = +e.target.value;
    const label = e.target.nextElementSibling;

    const range_width = getStyle(e.target,"width");//XXpx
    const label_width = getStyle(label,"width");//XXpx

    // Due to contain px,slice the width
    const num_width = +range_width.slice(0,range_width.length - 2);
    const num_label_width = +label_width.slice(0,label_width.length - 2);

    const min = +e.target.min;
    const max = +e.target.max;

    const left = value * (num_width / max) - num_label_width / 2 + scale(value,min,max,10,-10);

    label.style.left = `${left}px`;
    label.innerHTML = value;
});

44. netflix-mobile-navigation

效果如图所示:

44.gif

  1. CSS:导航宽度的变化 + CSS渐变 + 基本样式布局。
  2. JavaScript:点击按钮切换导航栏的显隐。如上述示例部分源码如下:
function changeNav(type){
    navList.forEach(nav => nav.classList[type === "open" ? "add" : "remove"]("visible"));
}

45. quiz-app

效果如图所示:

45.gif

  1. CSS:卡片布局 + 基本样式。
  2. JavaScript:表单提交 + 选择题的计算。如上述示例部分源码如下:
submit.addEventListener("click",() => {
    const answer = getSelected();
    if(answer){
        if(answer === quizData[currentQuestion].correct){
            score++;
        }
        currentQuestion++;
        if(currentQuestion > quizData.length - 1){
            quizContainer.innerHTML = `
                <h2>You answered ${ score } / ${ quizData.length } questions correctly!</h2>
                <button type="button" class="btn" onclick="location.reload()">reload</button>
            `
        }else{
            loadQuiz();
        }
    }
})

46. testimonial-box-switcher

效果如图所示:

46.gif

  1. CSS:CSS动画 + 宽度的改变实现进度条 + font-awesome字体库的使用 + 基本样式布局。
  2. JavaScript:定时器的用法,注意定时器的执行时间与设置进度条的执行动画时间一样。如上述示例部分源码如下:
.progress-bar {
    width: 100%;
    height: 4px;
    background-color: #fff;
    animation: grow 10s linear infinite;
    transform-origin: left;
}
@keyframes grow {
    0% {
        transform: scaleX(0);
    }
}
function updateTestimonial(){
    const { text,name,position,photo } = testimonials[currentIndex];
    const renderElements = [testimonial,username,role];
    userImage.setAttribute("src",photo);
    order.innerHTML = currentIndex + 1;
    [text,name,position].forEach((item,index) => renderElements[index].innerHTML = item);
    currentIndex++;
    if(currentIndex > testimonials.length - 1){
        currentIndex = 0;
    }
}

特别说明:CSS也是可以实现进度条的。

47. random-image-feed

效果如图所示:

47.gif

  1. CSS:图片布局 + 基本样式布局 + CSS提示框的实现(定位 + 伪元素) + CSS实现回到顶部效果。
  2. JavaScript:随机数 + (滚动事件的监听)控制回到顶部按钮的显隐。如上述示例部分源码如下:
function onLoad(rows = 5) {
    container.innerHTML = "";
    for (let i = 0; i < rows * 3; i++) {
        const imageItem = document.createElement("img");
        imageItem.src = `${refreshURL}${getRandomSize()}`;
        imageItem.alt = "random image-" + i;
        imageItem.loading = "lazy";
        container.appendChild(imageItem);
    }
}
function getRandomSize() {
    return `${getRandomValue()}x${getRandomValue()}`;
}
function getRandomValue() {
    return Math.floor(Math.random() * 10) + 300;
}
window.onload = () => {
    changeBtn.addEventListener("click", () => onLoad());
    window.addEventListener("scroll", () => {
        const { scrollTop, scrollHeight } = document.documentElement || document.body;
        const { clientHeight } = flexCenter;
        back.style.display = scrollTop + clientHeight > scrollHeight ? 'block' : 'none';
    })
    onLoad();
}

48. todo-list

效果如图所示:

48.gif

  1. CSS:基本样式布局 + CSS渐变。
  2. JavaScript:keydowncontextmenu事件的监听 + localStorage API存储数据。如上述示例部分源码如下:
function addTodo(todo){
    let inputValue = input.value;
    if(todo){
        inputValue = todo.text;
    }
    if(inputValue){
        const liItem = document.createElement("li");
        liItem.innerText = inputValue;
        if(todo && todo.complete){
            liItem.classList.add("complete");
        }
        liItem.addEventListener("click",() => {
            liItem.classList.toggle("complete");
            updateList();
        });
        liItem.addEventListener("contextmenu",(e) => {
            e.preventDefault();
            liItem.remove();
            updateList();
        });
        todoList.appendChild(liItem);
        input.value = "";
        updateList();
    }
}
function updateList(){
    const listItem = $$("li",todoList);
    const saveTodoData = [];
    listItem.forEach(item => {
        saveTodoData.push({
            text:item.innerText,
            complete:item.classList.contains("complete")
        })
    });
    localStorage.setItem("todoData",JSON.stringify(saveTodoData));
}

49. insect-catch-game

效果如图所示:

49.gif

  1. CSS:基本样式布局。
  2. JavaScript:中英文切换 + 替换元素中的文本(不包含标签元素) + 定时器的用法。如上述示例部分源码如下:
function replaceText(el,text,wrapSymbol = ""){
    let newText = [];
    if(wrapSymbol){
        // why not use split method?,because it can filter the wrap symbol.
        const getIndex = (txt) => txt.search(new RegExp("\\" + wrapSymbol));
        let searchIndex = getIndex(text),i = 0,len = text.length;
        while(searchIndex !== -1){
            newText.push(text.slice(i,searchIndex) + wrapSymbol);
            i = searchIndex;
            if(getIndex(text.slice(searchIndex + 1)) === -1){
                newText.push(text.slice(searchIndex + 1,len));
            }
            searchIndex = getIndex(text.slice(searchIndex + 1));
        }
    }
    const walk = (el,handler) => {
        const children = Array.from(el.childNodes);
        let wrapIndex = children.findIndex(node => node.nodeName.toLowerCase() === "br");
        children.forEach((node,index) => {
            if(node.nodeType === 3 && node.textContent.replace(/\s+/,"")){
                wrapSymbol ? handler(node,newText[index - wrapIndex < 0 ? 0 : index - wrapIndex]) : handler(node,text);;
            }
        });
    }
    walk(el,(node,txt) => {
        const parent = node.parentNode;
        parent.insertBefore(document.createTextNode(txt),node);
        parent.removeChild(node);
    });
}

以上工具函数的实现参考这里来实现的

特别说明:这个示例算是一个比较综合的示例了。

50. kinetic-loader

效果如图所示:

50.gif

  1. CSS:CSS旋转动画 + 基本样式布局。
    如上述示例部分源码如下:
@keyframes rotateA {
    0%,25% {
        transform: rotate(0deg);
    }
    50%,75% {
        transform: rotate(180deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
@keyframes rotateB {
    0%,25% {
        transform: rotate(90deg);
    }
    50%,75% {
        transform: rotate(270deg);
    }
    100% {
        transform: rotate(450deg);
    }
}

最后一天,额外的实现了一个404效果,算是特别结尾吧,是不是很花哨(^_^)。这算是一个CSS动画的综合使用,如下所示:

51. 404 page

效果如图所示:

51.gif

  1. CSS:CSS动画的用法 + 基本样式布局 + svg图标元素的样式设置。
    如上述示例部分源码如下:
@keyframes background {
    from {
        background: linear-gradient(135deg,#e0e0e0 10%,#ffffff 90%);
    }
    to {
        background: linear-gradient(135deg,#ffffff 10%,#e0e0e0 90%);
    }
}
@keyframes stampSlide {
    0% {
        transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
    }
    100% {
        transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-4000px);
    }
}
@keyframes slide {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(-200px);
    }
}
@keyframes roll {
    0% {
        transform: rotateZ(0deg);
    }
    85% {
        transform: rotateZ(90deg);
    }
    87% {
        transform: rotateZ(88deg);
    }
    90% {
        transform: rotateZ(90deg);
    }
    100% {
        transform: rotateZ(90deg);
    }
}
@keyframes zeroFour {
    0% {
        content:"4";
    }
    100% {
        content:"0";
    }
}
@keyframes draw {
    0% {
        stroke-dasharray: 30 150;
        stroke-dashoffset: 42;
    }
    100% {
        stroke:rgba(8, 69, 131, 0.9);
        stroke-dasharray: 224;
        stroke-dashoffset: 0;
    }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK