zl程序教程

您现在的位置是:首页 >  其他

当前栏目

38·灵魂前端工程师养成-[项目]让皮卡丘动起来

工程师项目前端 起来 灵魂 38 养成
2023-06-13 09:11:07 时间

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


项目初始化


复制上次的皮卡丘

那个熟悉的pikachu项目,然后使用parcel运行起来

MacBook-pro:pikachu-2 driverzeng$ parcel src/index.html 

打开浏览器访问


添加一个html页面

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <script src="pikachu.js"></script>
</body>
</html>

引入js,然后创建出来js文件

然后用parcel启动pikachu.html

MacBook-pro:pikachu-2 driverzeng$ parcel src/pikachu.html 

添加滚动字幕

接下来我们一步一步的实现功能,首先在html中创建一个div

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo"></div>
    <script src="pikachu.js"></script>
</body>
</html>

然后我们使用JS往这个div中,插入字...插它

let n = 1
demo.innerHTML = n

网页上就会出来一个 1

接下来,我们实现让这个数字,自增

let n = 1
demo.innerHTML = n
setInterval(()=>{
    n+=1
    demo.innerHTML = n
},1000)

它在自己长大...1秒钟,长大一次

既然n可以变,那我们就来个字符串。

const string = '大家好,我是曾老湿,我们来测试一下页面打字的效果。'
let n = 1
demo.innerHTML = string.substring(0,n)
setInterval(()=>{
    n+=1
    demo.innerHTML = string.substring(0,n)
},1000)

速度有点慢,可以自己调整

但是这样是有bug的,现在我们看不到,后台的控制台可以看到

const string = '大家好,我是曾老湿,我们来测试一下页面打字的效果。'
let n = 1
demo.innerHTML = string.substring(0,n)
setInterval(()=>{
    console.log(`${n}:${string.substring(0,n)}`)
    n+=1
    demo.innerHTML = string.substring(0,n)
},1000)

这个n一直在输出内容...即便是页面的字打完了,后台的n还在变化。我们需要它打完字就停住,否则以后日志没法看。

计时器,每次都会返回一个id,所以我们判断一下字符串的长度,然后把计时器取消即可。

const string = '大家好,我是曾老湿,我们来测试一下页面打字的效果。'
let n = 1
demo.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    console.log(`${n}:${string.substring(0,n)}`)
    n+=1
    demo.innerHTML = string.substring(0,n)
    if(n>string.length){
        window.clearInterval(id)
        return
    }
},300)

可以看的到,根据字符串长度就能判断出来,然后让它停止。

添加CSS内容和样式

接下来,我们把皮卡丘的html嵌入这个html的div中

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo"></div>
    <div id="demo2">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>

页面上什么都 看不出来,接下来我们就是偷偷摸摸的插入CSS,但是这个css在js中插入到html里面,把刚才的那一句话

const string = `
    body{
        background: red;
    }
`
let n = 1
demo.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    console.log(`${n}:${string.substring(0,n)}`)
    n+=1
    demo.innerHTML = string.substring(0,n)
    if(n>string.length){
        window.clearInterval(id)
        return
    }
},200)

字出来了,但是我们想要的css样式改变没有出来。

那我们就加一个style标签

const string = `
<style>
    body{
        background: red;
    }
</style>
`
let n = 1
demo.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    console.log(`${n}:${string.substring(0,n)}`)
    n+=1
    demo.innerHTML = string.substring(0,n)
    if(n>string.length){
        window.clearInterval(id)
        return
    }
},200)

这就太刺激了,我们居然可以偷偷摸摸的加入css的样式

接下来我想同时写入css内容并且样式一起跟着改变,我们需要再来个div

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo"></div>
    <div id="demo2"></div>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>
const string = `
<style>
    body{
        background: red;
    }
</style>
`
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    if(n>string.length){
        window.clearInterval(id)
        return
    }
},50)

那我们 把皮卡丘的CSS代码直接复制过来

const string = `
<style>
*{box-sizing: border-box;margin: 0;padding: 0;}
*::before,*::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 100vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
</style>
`
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    if(n>string.length){
        window.clearInterval(id)
        return
    }
},0)

卧槽,居然阔以,那就很开熏了

但是皮卡丘在下面,字体在上面,一上一下,也不知道他俩想干嘛~~~ 想。

优化代码


绝对定位

首先我们需要把皮卡丘绝对定位

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo2"></div>
    <div id="demo"></div>
    <style>
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>

不会往下压了,但是代码不滚动了,我们看不到,所以我们需要一个滚动条,我们先把demo2隐藏


添加滚动条

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo2"></div>
    <div id="demo"></div>
    <style>
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>


保证代码在框内

隐藏后,我们给代码部分价格border,发现,代码超出了边框部分。所以我们需要overflow一下

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo2"></div>
    <div id="demo"></div>
    <style>
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow: scroll;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>

有了滚动条,但是滚动条好丑啊,并且代码也不会动,所以我们 要把滚动条拉下去


下拉滚动条

const string = `
<style>
*{box-sizing: border-box;margin: 0;padding: 0;}
*::before,*::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 100vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
</style>
`
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = 999999
},0)

先简单粗暴设置一个 999999

这样代码就一直在滚动了,滚动条一直自动往下拉,但是999999太粗暴了,一看就不是专业前端写的,所以我们要 保证,滚动条和内容的高度保持一致即可。

我们发现,使用 demo.scrollHeight可以获取到内容的高度,那就加到代码里

demo.scrollTop = demo.scrollHeight

这样即可,但是其实也是错的,因为滚动条的高度也是有的,但是至少这样能拉到底,而且不那么low


隐藏滚动条

我们 把overflow改成hidden,给他隐藏起来

#demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow: hidden;
        }

代码依然可以滚动 ,由此可见,对于JS来说,有没有滚动条都可以滚动,但是用户现在是没有办法,上下滚动代码了。


隐藏滚动条,但是让用户可以滚动

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <div id="demo2"></div>
    <div id="demo"></div>
    <style>
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>

代码不显示style,要不太丑

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <style id="demo2"></style>
    <div id="demo"></div>
    <style>
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>
const string = `
*{box-sizing: border-box;margin: 0;padding: 0;}
*::before,*::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 100vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
`
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
},0)

增加暂停、快速、慢速等按钮


先添加按钮

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <style id="demo2"></style>
    <div id="demo"></div>
    <div id="buttons">
        <button id="btnPause">暂停</button>
        <button id="btnPlay">播放</button>
        <button id="btnSlow">慢速</button>
        <button id="btnNormal">中速</button>
        <button id="btnFast">快速</button>
    </div>
    <style>
        #buttons{
            position: fixed;
            right: 0;
            top: 0;
            z-index: 10;
            display: flex;
            flex-direction: column;
            margin-top: 10px;
            margin-right: 10px;
        }
        #buttons>button{
            margin-bottom: 10px;
        }
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>


修改按钮bug

添加完按钮会发现 ,刷新页面,先变大再变小,为啥呢?因为之前皮卡丘的style.css里面css的reset,都是*,所以我们需要修改一下 。

const string = `
.sink *{box-sizing: border-box;margin: 0;padding: 0;}
.sink *::before,.skin *::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 50vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
`
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
},0)

做html中css的reset

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <style id="demo2"></style>
    <div id="demo"></div>
    <div id="buttons">
        <button id="btnPause">暂停</button>
        <button id="btnPlay">播放</button>
        <button id="btnSlow">慢速</button>
        <button id="btnNormal">中速</button>
        <button id="btnFast">快速</button>
    </div>
    <style>
        * {box-sizing: border-box;}
        *::before,*::after {box-sizing: border-box;}
        #buttons{
            position: fixed;
            right: 0;
            top: 0;
            z-index: 10;
            display: flex;
            flex-direction: column;
            margin-top: 10px;
            margin-right: 10px;
        }
        #buttons>button{
            margin-bottom: 10px;
        }
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>


修改按钮大小

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <style id="demo2"></style>
    <div id="demo"></div>
    <div id="buttons">
        <button id="btnPause">暂停</button>
        <button id="btnPlay">播放</button>
        <button id="btnSlow">慢速</button>
        <button id="btnNormal">中速</button>
        <button id="btnFast">快速</button>
    </div>
    <style>
        * {box-sizing: border-box;}
        *::before,*::after {box-sizing: border-box;}
        #buttons{
            position: fixed;
            right: 0;
            top: 0;
            z-index: 10;
            display: flex;
            flex-direction: column;
            margin-top: 10px;
            margin-right: 10px;
        }
        #buttons>button{
            margin-bottom: 10px;
            padding: 4px 8px;
        }
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>


导出代码

突然发现皮卡丘的CSS好长还不能折叠,我们再优化一下。

我们单独创建一个pikachu_css.js文件存储css的字符串

const string = `
.sink *{box-sizing: border-box;margin: 0;padding: 0;}
.sink *::before,.skin *::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 50vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
`
export default string

然后再用pikachu.js导入那个文件

import string from './pikachu_css.js'
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
},0)

实现暂停功能

暂停其实就是停下当前的计时器。

import string from './pikachu_css.js'
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let id = setInterval(()=>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
},0)

btnPause.onclick = () =>{
    window.clearInterval(id)
    return
}

实现播放功能

思路就是,刚才已经取消了,就跟你今天闹钟响了,巴扎黑~~给它砸了,那么请问,明天还会响么?不会,那么怎么办?买个新的呗。

import string from './pikachu_css.js'
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)

const run = () =>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
}
let id = setInterval(()=>{
    run()
},0)

btnPause.onclick = () =>{
    window.clearInterval(id)
}

btnPlay.onclick = () =>{
    id = setInterval(()=>{
        run()
    },0)
}

实现播放速度功能

import string from './pikachu_css.js'
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)

let time = 100

const run = () =>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
}
let id = setInterval(()=>{
    run()
},time)

btnPause.onclick = () =>{
    window.clearInterval(id)
}

btnPlay.onclick = () =>{
    id = setInterval(()=>{
        run()
    },time)
}

btnSlow.onclick = () =>{
    window.clearInterval(id)
    time = 500
    id = setInterval(() => {
        run()
    },time)
}

btnNormal.onclick = () =>{
    window.clearInterval(id)
    time = 100
    id = setInterval(() => {
        run()
    },time)
}

btnFast.onclick = () =>{
    window.clearInterval(id)
    time = 0
    id = setInterval(() => {
        run()
    },time)
}

代码重构

import string from './pikachu_css.js'
let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)
let time = 100
const run = () =>{
    n+=1
    if(n>string.length){
        window.clearInterval(id)
        return
    }
    demo.innerText = string.substring(0,n)
    demo2.innerHTML = string.substring(0,n)
    demo.scrollTop = demo.scrollHeight
}
const play =() =>{
    id = setInterval(run,time)
}
const pause = () =>{
    window.clearInterval(id)
}
let id = play()
btnPause.onclick = () =>{
    pause()
}

btnPlay.onclick = () =>{
    id = play()
}

btnSlow.onclick = () =>{
    pause()
    time = 500
    play()
}

btnNormal.onclick = () =>{
    pause()
    time = 100
    play()
}

btnFast.onclick = () =>{
    pause()
    time = 0
    play()
}

面向对象优化

import string from './pikachu_css.js'
const  demo = document.querySelector('#demo')
const  demo2 = document.querySelector('#demo2')

let n = 1
demo.innerText = string.substring(0,n)
demo2.innerHTML = string.substring(0,n)

let time = 100

const player = {
    run: () =>{
        n+=1
        if(n>string.length){
            window.clearInterval(id)
            return
        }
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        demo.scrollTop = demo.scrollHeight
    },
    play: () =>{
        return setInterval(player.run,time)
    },
    pause: () =>{
        window.clearInterval(id)
    },
    slow: () =>{
        player.pause()
        time = 500
        id =  player.play()
    },
    normal: () =>{
        player.pause()
        time = 100
        id = player.play()
    },
    fast: () =>{
        player.pause()
        time = 0
        id = player.play()
    }
}

let id = player.play()

document.querySelector('#btnPause').onclick = () =>{
    player.pause()
}
document.querySelector('#btnPlay').onclick = () =>{
    id = player.play()
}
document.querySelector('#btnSlow').onclick = player.slow
document.querySelector('#btnNormal').onclick = player.normal
document.querySelector('#btnFast').onclick = player.fast

添加对象初始化方法

import string from './pikachu_css.js'
const  demo = document.querySelector('#demo')
const  demo2 = document.querySelector('#demo2')
let n = 1
let time = 100
let id

const player = {
    init: () =>{
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        player.play()
    },
    run: () =>{
        n+=1
        if(n>string.length){
            window.clearInterval(id)
            return
        }
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        demo.scrollTop = demo.scrollHeight
    },
    play: () =>{
        id = setInterval(player.run,time)
    },
    pause: () =>{
        window.clearInterval(id)
    },
    slow: () =>{
        player.pause()
        time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        time = 0
        player.play()
    }
}

player.init()

document.querySelector('#btnPause').onclick = player.pause
document.querySelector('#btnPlay').onclick = player.play
document.querySelector('#btnSlow').onclick = player.slow
document.querySelector('#btnNormal').onclick = player.normal
document.querySelector('#btnFast').onclick = player.fast

绑定事件

import string from './pikachu_css.js'
const  demo = document.querySelector('#demo')
const  demo2 = document.querySelector('#demo2')
let n = 1
let time = 100
let id

const player = {
    init: () =>{
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        player.play()
        player.bindEvents()
    },
    bindEvents: () =>{
        document.querySelector('#btnPause').onclick = player.pause
        document.querySelector('#btnPlay').onclick = player.play
        document.querySelector('#btnSlow').onclick = player.slow
        document.querySelector('#btnNormal').onclick = player.normal
        document.querySelector('#btnFast').onclick = player.fast
    },
    run: () =>{
        n+=1
        if(n>string.length){
            window.clearInterval(id)
            return
        }
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        demo.scrollTop = demo.scrollHeight
    },
    play: () =>{
        id = setInterval(player.run,time)
    },
    pause: () =>{
        window.clearInterval(id)
    },
    slow: () =>{
        player.pause()
        time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        time = 0
        player.play()
    }
}

player.init()

简化id

import string from './pikachu_css.js'
const  demo = document.querySelector('#demo')
const  demo2 = document.querySelector('#demo2')
let n = 1
let time = 100
let id

const player = {
    init: () =>{
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        player.play()
        player.bindEvents()
    },
    bindEvents: () =>{
        const hashTable = {
            '#btnPause' : player.pause,
            '#btnPlay' : player.play,
            '#btnSlow' : player.slow,
            '#btnNormal' : player.normal,
            '#btnFast' : player.fast
        }
        for(let key in hashTable){
            document.querySelector(key).onclick = hashTable[key]
        }
    },
    run: () =>{
        n+=1
        if(n>string.length){
            window.clearInterval(id)
            return
        }
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        demo.scrollTop = demo.scrollHeight
    },
    play: () =>{
        id = setInterval(player.run,time)
    },
    pause: () =>{
        window.clearInterval(id)
    },
    slow: () =>{
        player.pause()
        time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        time = 0
        player.play()
    }
}

player.init()

遍历事件,并且防止添加原型

import string from './pikachu_css.js'
const  demo = document.querySelector('#demo')
const  demo2 = document.querySelector('#demo2')
let n = 1
let time = 100
let id

const player = {
    init: () =>{
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        player.play()
        player.bindEvents()
    },
    events: {
        '#btnPause' : 'pause',
        '#btnPlay' : 'play',
        '#btnSlow' : 'slow',
        '#btnNormal' : 'normal',
        '#btnFast' : 'fast'
    },
    bindEvents: () =>{
        for(let key in player.events) if(player.events.hasOwnProperty(key)){
                const value  = player.events[key]
                document.querySelector(key).onclick = player[value]
        }
    },
    run: () =>{
        n+=1
        if(n>string.length){
            window.clearInterval(id)
            return
        }
        demo.innerText = string.substring(0,n)
        demo2.innerHTML = string.substring(0,n)
        demo.scrollTop = demo.scrollHeight
    },
    play: () =>{
        id = setInterval(player.run,time)
    },
    pause: () =>{
        window.clearInterval(id)
    },
    slow: () =>{
        player.pause()
        time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        time = 0
        player.play()
    }
}

player.init()

老手代码

import string from './pikachu_css.js'
const player = {
    id : undefined,
    time : 100,
    ui: {
        demo : document.querySelector('#demo'),
        demo2 : document.querySelector('#demo2')
    },
    n : 1,
    init: () =>{
        player.ui.demo.innerText = string.substring(0,player.n)
        player.ui.demo2.innerHTML = string.substring(0,player.n)
        player.play()
        player.bindEvents()
    },
    events: {
        '#btnPause' : 'pause',
        '#btnPlay' : 'play',
        '#btnSlow' : 'slow',
        '#btnNormal' : 'normal',
        '#btnFast' : 'fast'
    },
    bindEvents: () =>{
        for(let key in player.events) if(player.events.hasOwnProperty(key)){
                const value  = player.events[key]
                document.querySelector(key).onclick = player[value]
        }
    },
    run: () =>{
        player.n+=1
        if(player.n>string.length){
            window.clearInterval(player.id)
            return
        }
        player.ui.demo.innerText = string.substring(0,player.n)
        player.ui.demo2.innerHTML = string.substring(0,player.n)
        player.ui.demo.scrollTop = player.ui.demo.scrollHeight
    },
    play: () =>{
        player.id = setInterval(player.run,player.time)
    },
    pause: () =>{
        window.clearInterval(player.id)
    },
    slow: () =>{
        player.pause()
        player.time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        player.time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        player.time = 0
        player.play()
    }
}

player.init()

完整代码

pikachu.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pikachu的制作过程-曾老湿</title>
</head>
<body>
    <style id="demo2"></style>
    <div id="demo"></div>
    <div id="buttons">
        <button id="btnPause">暂停</button>
        <button id="btnPlay">播放</button>
        <button id="btnSlow">慢速</button>
        <button id="btnNormal">中速</button>
        <button id="btnFast">快速</button>
    </div>
    <style>
        * {box-sizing: border-box;}
        *::before,*::after {box-sizing: border-box;}
        #buttons{
            position: fixed;
            right: 0;
            top: 0;
            z-index: 10;
            display: flex;
            flex-direction: column;
            margin-top: 10px;
            margin-right: 10px;
        }
        #buttons>button{
            margin-bottom: 10px;
            padding: 4px 8px;
        }
        #demo2{
            display: none;
        }
        #demo{
            position: fixed;
            height: 50vh;
            top: 0;
            left: 0;
            width: 100%;
            border: 1px solid red;
            overflow-y: auto;
        }
        #demo::-webkit-scrollbar{
            display: none;
        }
        #html{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 50vh;
        }
    </style>
    <div id="html">
        <div class="skin">
            <div class="eye left"></div>
            <div class="eye right"></div>
            <div class="nose">
                <div class="yuan"></div>
            </div>
            <div class="face left"></div>
            <div class="face right"></div>
            <div class="mouth">
                <div class="up">
                    <div class="lip left"></div>
                    <div class="lip right"></div>
                </div>
                <div class="down">
                    <div class="yuan1">
                        <div class="yuan2"></div>
                    </div>
                </div>
            </div> 
        </div>
    </div>
    <script src="pikachu.js"></script>
</body>
</html>

pikachu.js

import string from './pikachu_css.js'
const player = {
    id : undefined,
    time : 100,
    ui: {
        demo : document.querySelector('#demo'),
        demo2 : document.querySelector('#demo2')
    },
    n : 1,
    init: () =>{
        player.ui.demo.innerText = string.substring(0,player.n)
        player.ui.demo2.innerHTML = string.substring(0,player.n)
        player.play()
        player.bindEvents()
    },
    events: {
        '#btnPause' : 'pause',
        '#btnPlay' : 'play',
        '#btnSlow' : 'slow',
        '#btnNormal' : 'normal',
        '#btnFast' : 'fast'
    },
    bindEvents: () =>{
        for(let key in player.events) if(player.events.hasOwnProperty(key)){
                const value  = player.events[key]
                document.querySelector(key).onclick = player[value]
        }
    },
    run: () =>{
        player.n+=1
        if(player.n>string.length){
            window.clearInterval(player.id)
            return
        }
        player.ui.demo.innerText = string.substring(0,player.n)
        player.ui.demo2.innerHTML = string.substring(0,player.n)
        player.ui.demo.scrollTop = player.ui.demo.scrollHeight
    },
    play: () =>{
        player.id = setInterval(player.run,player.time)
    },
    pause: () =>{
        window.clearInterval(player.id)
    },
    slow: () =>{
        player.pause()
        player.time = 500
        player.play()
    },
    normal: () =>{
        player.pause()
        player.time = 100
        player.play()
    },
    fast: () =>{
        player.pause()
        player.time = 0
        player.play()
    }
}

player.init()

pikachu_css.js

const string = `
.sink *{box-sizing: border-box;margin: 0;padding: 0;}
.sink *::before,.skin *::after{box-sizing: border-box;}

.skin{
    position: relative;
    background: #ffe600;
    height: 50vh;
}
.nose{
    border: 10px solid #000;
    border-color:  #000 transparent transparent transparent;
    position: absolute;
    left: 50%;
    top: 145px;
    margin-left: -10px;
    z-index: 10;
}
@keyframes wave{
    0%{
        transform: rotate(0deg);
    }
    33%{
        transform: rotate(5deg);
    }
    66%{
        transform: rotate(-5deg);
    }
    100%{
        transform: rotate(0deg);
    }
}
.nose:hover{
    transform-origin: center  bottom;
    animation:  wave  300ms infinite linear;
}

.yuan{
    position: absolute;
    width: 20px;
    height: 6px;
    top: -16px;
    left: -10px;
    border-radius: 10px 10px 0 0;
    background: #000;
}
.eye{
    border: 2px solid #000;
    width: 64px;
    height: 64px;
    position: absolute;
    left: 50%;
    top: 100px;
    margin-left: -32px;
    background: #2e2e2e;
    border-radius: 50%;
}
.eye::before{
    content: '';
    display: block;
    border: 3px solid #000;
    width: 30px;
    height: 30px;
    background:#fff;
    border-radius: 50%;
    position: relative;
    left: 5px;
    top: 3px;
}
.eye.left{
    transform: translateX(-100px);
}

.eye.right{
    transform: translateX(100px);
}
.mouth{
    width: 200px;
    height: 200px;
    position: absolute; 
    left: 50%;
    top: 170px;
    margin-left: -100px;
}
.mouth .up{
    position: relative;
    top: -20px;
    z-index: 1;
}
.mouth .up .lip{
    border: 3px solid black;
    background: #ffe600;
    height: 30px;
    width: 100px;
    border-top-color: transparent;
    border-right-color: transparent; 
    position: relative;
    position: absolute;
    left: 50%;
    margin-left: -50px;
}
.mouth .up .lip.left{
    border-radius: 0 0 0 50px;
    transform: rotate(-15deg) translateX(-53px);
}
.mouth .up .lip.right{
    border-radius: 0 0 50px 0;
    transform: rotate(15deg) translateX(53px);
}
.mouth .up .lip::before{
    content: '';
    display: block;
    width: 7px;
    height: 30px;
    position: absolute;
    bottom: 0;
    background:#ffe600;
}
.mouth .up .lip.left::before{
    right: -6px;
}
.mouth .up .lip.right::before{
    left: -6px;
}
.mouth .down{
    height: 180px;
    position: absolute;
    top: 5px;
    width: 100%;
    overflow: hidden;
}
.mouth .down .yuan1{
    border: 3px solid black;
    width: 150px;
    height: 1000px;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -75px;
    border-radius: 75px / 300px;
    background: #9b000a;
    overflow: hidden;
}
.mouth .down .yuan1 .yuan2{
    width: 200px;
    height: 300px;
    background: #ff485f;
    position: absolute;
    bottom: -155px;
    left: 50%;
    margin-left: -100px;
    border-radius: 100px;
}
.face{
    position: absolute;
    left: 50%;
    border: 3px solid black;
    width: 88px;
    height: 88px;
    top: 200px;
    margin-left: -44px;
    z-index: 3;
    background: #ff0000;
    border-radius: 50%;
}
.face > img{
    position: absolute;
    top: 50%;
    left: 50%;
    display: none;
}
.face:hover > img{
    display: block;
}
.face.left{
    transform: translateX(-180px);
}
.face.left > img{
    transform: rotateY(180deg);
    transform-origin: 0 0;
}
.face.right{
    transform: translateX(180px);
}
`
export default string