目标:动态展示代码与效果

CRM 实现效果、了解代码思路、会用会查相关API、可复现步骤(半盲打)、拓展

  • 可以将之前的项目:Canvas画板和拖动一个div项目也改成动态代码展示,当做练习

目录

  • 起手
  • 原理
  • 思路
  • 实现过程
    • 显示一段话,并加上样式
    • 制作太极
  • 永远都要考虑手机
  • 部署到 GitHub Pages

起手

  • 创建cv-1/src/路径
  • 创建index.html
  • 创建main.js
  • ! + Tab初始化index.html
  • 引入<script src="main.js"></script>
  • parcel --version,没的话,装yarn global add parcel
  • 启动parcel服务
  • parcel src/index.html

测试成功运行

  • <body>hi</body>里写个字,在main.js里写一句console.log('hi')
  • 看页面以及打开控制台,看是否生效

实现思路与细节

分解步骤,一步步由简单到复杂

抽象

1)让字在页面中一个个显示

  • HTML里写个数字
  • main.js里操作这个demo
  • 让数字变:1-> 2-> n
  • 让数字不停地变
  • 把数字改为字符串
  • 显示完整字符串,即显示从0个到n个字符串,实现“改变一段字”
  • 加几段字

2)加上样式

  • 不用<span id="demo"></span>,用<div id="html"></div>

实现原理

用到的API

  • ID选择器document.querySelector("#xxx"),赋值给xxx
  • 写入xxx.innerHTML = 123
  • 定时setTimeout(()=>{},0)
  • 封装函数,调用
  • 用递归函数,简化步骤(函数循环调用自己)
  • 截取字符串'string'.substring(0,n)
  • 字符串替换string.replace('\n','<br>')
  • string[0].charCodeAt()
  • 正则匹配string.replace(/\n/g,'<br>'),把所有的\n筛选出来

不用的API

  • setInterval(()=>{},0),用递归的setTimeout代替,好处是可以随时停止,即更自由地控制暂停/开始

实现过程

显示一段话

第一步:让字动起来

1)生成一个容器放数字

1
<span id="demo"></span>

2)在main.js里操作这个demo

1
2
3
4
/* 获取`demo` */
let demo = document.querySelector('#demo');
/* 在`demo`里写字 即设置内容 */
demo.innerHTML = 1;

parcel会自动刷新

3)让数字变

2s后让字变为2

1
2
3
setTimeout(()=>{
   demo.innerHTML = 2;
},2000)

4)让数字不停地变

用一个变量n,去保存当前变成的字demo.innerHTML = n

在‘定时器’中累加一次 n = n + 1

1
2
3
4
5
6
let n = 1;
demo.innerHTML = n;
setTimeout(()=>{
    n = n + 1;
    demo.innerHTML = n;
},1000)

联想一早响n次的闹钟

为什么不用setInterval

1
2
3
4
setInterval(() => {
    n = n + 1;
    demo.innerHTML = n;
}, 2000);

老手不用setInterval(()=>{},0),用递归的setTimeout代替,封装一个函数

4)递归函数每隔一段时间,调用自己

通过setTimeout模拟setInterval

1
2
3
4
5
6
7
8
let step = () => {
    console.log("1s后把n加1并显示");
    setTimeout(() => {
        n = n + 1;
        demo.innerHTML = n;
        step();
    }, 1000);
};
  • 好处是,可以加上停止的条件,更自由地控制
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let step = () => {
    console.log("1s后把n加1并显示");
    setTimeout(() => {
        n = n + 1;
        demo.innerHTML = n;
        if(n <= 5){ // 在第5次停止
            step();
        }else{
            // do noting
        }
    }, 1000);
};

5)把数字改为字符串,实现“改变一段字”

  • 让内容变为字符串let string = '...';
  • ES6的反引号 ‘`',可以处理换行的字符
  • 引入下标let n = 0;
  • 通过字符串的过装对象下标来一个个处理字符串中的字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* ... */
let string = `你好,我是一名前端新人`;
let n = 0; // 下标
demo.innerHTML = string[n]; // 初始
let step = () => {
    console.log("1s后把n加1并显示");
    setTimeout(() => {
        n = n + 1;
        demo.innerHTML = string[n];
        if (n < string.length) {
            step();
        } else {}
    }, 1000);
};
step();

在控制台看到,最后一个是undefined

  • 初始n = 0
  • 因为此时n < string.lengthn+=1
  • 代入n = 2,长度3
  • 仍要运一次step(),取到超过字符串长度下标值为undefined

6)改掉最后一个undefined

思考初步改掉if 的判断条件的顺序

console.log(n)

代入n = 10,看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* ... */
let n = 0;
demo.innerHTML = string[n]; // 初始
console.log(string.length);
let step = () => {
    setTimeout(() => {
        console.log(n); // n  = 10
        if (n >= string.length) {
            return;
        } else {
            n = n + 1; // n = 11
            demo.innerHTML = string[n]; // n = 11 长度超出
            step();
        }
    }, 500);
};

把判断条件改成if (n + 1 >= string.length)就ok了?

“暴力”尝试比“想”,更快

效果不对,console.log()

7)显示完整字符串,即显示从0个到n个字符串

string[n]换为string.substring(0,n)

效果不对,console.log()

程序员的“测不准定理”,只关注代码现在可以运行成功

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//++++++++++++++++++++++++++++++++++
    if (n + 1 >= string.length) {
//==================================
        return;
    } else {
        n = n + 1;
//++++++++++++++++++++++++++++++++++
        demo.innerHTML = string.substring(0, n);
//==================================
        step();
    }
}, 500);

效果不对,调整顺序

1
2
3
4
5
6
7
8
console.log(n);
n = n + 1;
demo.innerHTML = string.substring(0, n);
if (n >= string.length) {
    return;
} else {
    step();
}

代码逻辑优化

改代码顺序,即改代码逻辑,重调

需求一变,代码全变

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let step = () => {
    setTimeout(() => {
        n = n + 1;
        demo.innerHTML = string.substring(0, n);
//++++++++++++++++++++++++++++++++++
        if (n < string.length) {
            step();
        }
    }, 500);
};
step();

加几行字

1
2
3
4
5
6
7
8
let string = `
你好,我是一名前端新人
接下来,我要开始加样式了
我要加的样式是
body{
    color: red;
}
`;

但只显示在一行里

8)要显示换行的格式

string里加的回车都缩为空格了

问题:HTML源代码里敲回车会自动变成空格,从而使字符串变为一行

变成HTML里的回车,<br>标签

解决:string.replace('\n','<br>'),这也是文本编辑器中自动换行的原理

1
string = string.replace("\n", "<br>");

效果不对,并没换行

控制台中,判断字符

1
2
3
4
5
6
7
8
9
let string = `
你好,我是一名前端新人
接下来,我要开始加样式了
我要加的样式是
body{
    color: red;
}
`;
string[0].charCodeAt()
  • 将字符串在控制台里打出来看,每个下标对应的字符是什么
  • string[0].charCodeAt(),得出一个字符的Unicode编码10
  • 查看Ascii Table,换行符new line
1
2
string[0] // ""
string[0].charCodeAt() // 10
  • 运行string = string.replace("\n", "<br>");
  • 再看,只替换了第一个<br>,并没替换所有的

用正则表达式匹配,把所有的\n筛选出来

1
string.replace(/\n/g,'<br>');

9)完善细节

代码优化,细节,找一找重复的代码,想办法整合:demo.innerHTML = string.substring(0,n)目前重复出现了两次

let n = -1,将初始的的demo.innerHTML = string.substring(0,n)整合放入let step() = ...

1
2
n = n + 1; // 设初始值 n = -1,此步,n = 0
demo.innerHTML = string.substring(0, n); // 每次重新刷新整个字符串
  • 不符合常规从0开始,让if(n < string.length - 1),就可以n = 0开始

调慢速度后,回车的地方怎么出现了<

n = n + 1;加上console.log(string[n]);

一个字一个字蹦,解析到<br,就先显示出<

11)完整地解析<br>

重想全部方法:不能一下子全部替换

放弃使用string = string.replace(/\n/g, "<br>");

声明一个变量string2用作寄存字符串,string[n]只作为判断\n的标记,累加赋值给寄存器string2

  • string[n]不再作为累加的字符串,字符不再一一对应
  • 而是作为判断innnerHTML哪里出现了\n的标记
  • 重新声明一个sintrg2 = "",用作寄存字符串
  • 在过程中,每次遇到\n,每次都将string[n]替换一次成字符串<br>,累加赋值给字符串寄存器string2
  • 判断:string[n] === "\n"
  • 分叉判断:string2 += "<br>" : string2 += string[n]
  • 汇总:demo.innerHTML = string2

n进行加1的操作

string的第n个字符

判断取得字符是否回车?替换 :照抄

得到的结果保存到另一个字符串

判断n是否到最后一个?跳出 :继续n+1的操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* ... */
let string2 = "";
let n = 0;
let step = () => {
    setTimeout(() => {
        n = n + 1;
        if (string[n] === "\n") {
            string2 += "<br>";
        } else {
            string2 += string[n];
        }
        demo.innerHTML = string2;
        if (n < string.length - 1) {
            step();
        }
    }, 100);
};

超过三十行,大脑就不够用了

每过几行就回顾一下代码,做了些什么,达到的程度

在注释中,写出分步思路,每个变量的作用,开始的取值,最后一步的取值

简化if else成三目运算,n累加和步进step()写到一起

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let demo = document.querySelector("#demo");
let string = `
你好,我是一名前端新人
接下来,我要开始加样式了
我要加的样式是
body{
    color: red;
}
`;
let string2 = "";
let n = 0; // string[n]下标
let step = () => {
    setTimeout(() => {
        //是回车就改成<br> 累加赋值给string2
        //如果不是回车就照搬
        string2 += (string[n] === "\n" ? "<br>" : string[n])
        demo.innerHTML = string2;
        if (n < string.length - 1) {
            // n不是最后一个,步进
            n += 1;
            step();
        } else {
            // n是最后一个,什么也不做
        }
    }, 100);
};
step();

三大难题

  • 要不要加1(1-offset problem)
  • 怎么命名(naming problem)
  • 缓存失效(cache problem)

只有试

尽量把思路用注释写下来,防止遗忘


空格未显示

9)显示缩进,空格转义符

要显示空格

加个判断string[n] === " "

' '转义为实体字符&nbsp;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
if (string[n] === "\n") {
    //是回车就改成<br> 累加赋值给string2
    string2 += "<br>";
} else if(string[n] === " "){
    // 转义缩进
    string2 += "&nbsp;"
} else {
    //如果不是回车就照搬
    string2 += string[n];
}

body{ color: red; }如何生效?

第二步:同时展示HTML和加上样式

1)改样式

  • 加上<style id="style"></style>

测试可以更改样式

1
2
3
4
5
6
7
8
let style = document.querySelector("#style");
setTimeout(() => {
    style.innerHTML = `
    body{
        color: red;
    }
    `;
}, 3000);

2)把展示的字符串同时写到HTMLStyle

  • 同时写入<style id="style"></style>
1
2
3
4
5
6
let html = document.querySelector("#html");
let style = document.querySelector("#style");
//...
//...
    html.innerHTML = string2;
    style.innerHTML = string2;

问题:在控制台里<style id="style"></style>里混入了<br>&bsp;,并不能得到预期的效果

  • 用替换空格和换行前的字符串string.substring(0,n)

怎么style里有html,中文(字符)会使CSS失效

<style id="style"></style>里仍受中文字符影响

加注释 /* */

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let string = `
/*
** 你好,我是一名前端新人
** 接下来,我要开始加样式了
** 我要加的样式是
*/
body{
    color: red;
}
`;

html的时候同时写style

1
2
html.innerHTML = string2;
style.innerHTML = string.substring(0,n);

小结

  • 如何显示写入HTML里的内容
  • 如何写入Style样式
  • 如何让显示的内容的样式立即生效

制作太极

准备一个div

  • index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<body>
    <style id="style"></style>
    <style>
        #div1 {
            position: fixed;
            right: 20px;
            top: 20px;
        }
    </style>
    <div id="html"></div>
    <div id="div1"></div>
    <script src="main_log.js"></script>
</body>

把 div 变成一个圆

  • main.js里的string加上以下代码
1
2
3
4
5
#div1{
    border-radius: 50%;
    box-shadow: 0 0 3px rgba(0,0,0,0.5);
    border: none;
}

背景渐变实现一黑一白

gradient background generator https://cssgradient.io/

  • main.js里的string加上以下代码
1
2
3
#div1{
    background: linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 50%, rgba(0,0,0,1) 50%, rgba(0,0,0,1) 100%);
}

加两个神秘的小球

  • index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<style>
#div1::before,
#div1::after {
    content: '';
    display: block;
    width: 200px;
    height: 200px;
    position: absolute;
}
</style>

点睛

辐射渐变

  • radial-gradient
  • main.js里的string加上以下代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#div1::before,#div1::after{
    width: 100px;
    height: 100px;
    left: 50%;
    transform: translateX(-50%);
    border: 1px solid whitesmoke;
    border-radius: 50%;
    border:none;
}
#div1::before{
    top: 0;
    background: #000;
    background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 25%, rgba(0,0,0,1) 25%, rgba(0,0,0,1) 100%);
}
#div1::after{
    bottom: 0;
    left: 50%;
    background: #fff;
    background: radial-gradient(circle, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 25%, rgba(255,255,255,1) 25%, rgba(255,255,255,1) 100%, rgba(0,0,0,1) 100%);
}

微元素中不能再套伪元素

背景

白色背景太单调了,我们来点背景

  • main.js里的srtring加上以下代码
1
2
3
html {
    background: rgba(200,200,200,0.8);
}

代码文字折行

以及确定宽高

  • index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<style>
#html {
    text-overflow: ellipsis;
    overflow-y: visible;
    word-wrap: break-word;
    word-break: break-word;
    width: 50vw;
    height: 100vh;
}
</style>

优化以及永远都要考虑手机

自动处于底部

  • JS控制滚动条
  • window.scrollTo(x,y)
  • 加到main.js
1
2
3
html.innerHTML = string2;
style.innerHTML = string.substring(0, n);
window.scrollTo(0,9999);

干掉X方向滚动条

  • CSS设置换行
1
2
3
#demo {
  word-break: break-all;
}

页面布局适配

  • 媒体查询,测试有效性
1
2
3
4
5
@media (max-width500px){
    body {
        color: red;
    }
}
  • 上下各50%
1
2
3
4
5
6
7
8
9
@media (max-width:500px) {
    #html {
        height: 50vh;
        border: 1px solid red;
    }
    #div1{
        height:50vh;        border: 1px solid red;
    }
}

变成椭圆,未包裹div,定位不可固定

  • 媒体查询的优先级更高,样式被影响
  • 设置的宽高是固定数值,因此变形

外面再套一个div#div1Wrapper,专门用来定位

1
2
3
4
<div id="html"></div>
<div id="div1Wrapper">
    <div id="div1"> </div>
</div>

对应在样式里修改

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@media (max-width:500px) {
    #html { /* 上一半 */
        height: 50vh;
        border: 1px solid red;
    }
    #div1Wrapper { /* 下一半 */
        height: 50vh;
        border: 1px solid red;
    }
    #div1 {
        position: relative;
        top: 0;
        right: 0;
    }
}

上半部分文字超出,到下方

  • 加滚动条overflow: auto;
1
2
3
4
5
6
7
8
@media (max-width:500px) {
    #html {
        /* 上一半 */
        width: 100vw;
        height: 50vh;
        overflow: auto;
    }
}

代码自动向下滚

  • 之前设置的是浏览器的滚动条,现在要加上div的滚动条

  • 在JS里加一句html.scrollTo(0, 9999);

1
2
window.scrollTo(0, 9999);
html.scrollTo(0, 9999);

关键帧动画里,transform属性要完全覆盖之前的,相同的部分(状态)也要保留

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#div1{
    animation: 5s linear infinite  taiJi;
}
@keyframes taiJi{
    from{
        left: 50%;
        transform : translateX(-50%) rotate(0deg);
    }
    to{
        left: 50%;
        transform : translateX(-50%) rotate(360deg);
    }
}

注意别忘了写CSS_reset,以及修改 六个meta值,适配移动页面,抄大厂的

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,viewport-fit=cover">

部署到 GitHub Pages

演示中,不能把<style></style>写到<body></body>里,会被parcel删掉

  • 单独保存样式到一个style.css里,就好了

验证

生成的dist目录里的index.html能否运行

1
2
cd dist
hs . -c-1
  • 点开链接,加上路径/index.html查看,可以运行
  • src里的不能直接运行
  • 配置Setting>GitHub Pages,选master
  • 等出现网址,打开(得到404页),加上路径/dist/index.html
  • 等待
  • 在此页面下,打开开发者工具,Network选项卡,勾选Disable Cache禁用缓存,刷新

验证Network里,生成的main.***.js请求路径是否正确,包含/cv-1/dist/index.html

  • 手动更改打包路径

参考

1
2
parcel --help
parcel build --help
  • 更改parcel build --public-url <url>
  • 验证
1
2
cd cv-1
parcel build src/index.html --public-url fuck
  • 打开重新生成的dist/index.html里,查找fuck,找到src='fuck/main.***.js'
  • fuck调试法
  • fuck的位置替换为dist
1
parcel build src/index.html --public-url dist
  • 打开重新生成的dist/index.html里,查看到<script src="dist/main.***.js"></script>

.gitignore

1
2
**/node_modules
**.cache

上传提交

忽略建仓操作,具体看

1
2
3
git add .
git commit -m 'update build'
git push

发现路径错/dist/dist/...

重新生成

1
2
parcel build src/index.html
parcel build src/index.html --public-url .
  • 确保main.***.js路径在当前目录
  • index.html里的<script src="main.***.js" ></script>中,main.***.js前无其他路径

部署方式

  • 每次改完代码,必须运行这一行,才能正确的请求 JS 和 CSS:
1
parcel build src/index.html --public-url .

动态化项目复盘

保留静态样式

把JS入门项目动态化

把画板项目也动起来:动态显示构建Canvas画板项目

回顾注释·回顾代码·回顾思路

过程·过程·过程

未适配safari




参考文章

FrankFang的完整代码:https://github.com/FrankFang/cv-1/tree/master/src

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名