目标:动态展示代码与效果
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.length在n+=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] === " "
将' '转义为实体字符
1
2
3
4
5
6
7
8
9
10
|
if (string[n] === "\n") {
//是回车就改成<br> 累加赋值给string2
string2 += "<br>";
} else if(string[n] === " "){
// 转义缩进
string2 += " "
} 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)把展示的字符串同时写到HTML和Style里
- 同时写入
<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
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 变成一个圆
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/
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%);
}
|
加两个神秘的小球
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%);
}
|
微元素中不能再套伪元素
背景
白色背景太单调了,我们来点背景
1
2
3
|
html {
background: rgba(200,200,200,0.8);
}
|
代码文字折行
以及确定宽高
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方向滚动条
1
2
3
|
#demo {
word-break: break-all;
}
|
页面布局适配
1
2
3
4
5
|
@media (max-width500px){
body {
color: red;
}
}
|
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;
}
}
|
上半部分文字超出,到下方
1
2
3
4
5
6
7
8
|
@media (max-width:500px) {
#html {
/* 上一半 */
width: 100vw;
height: 50vh;
overflow: auto;
}
}
|
代码自动向下滚
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删掉
验证
生成的dist目录里的index.html能否运行
- 点开链接,加上路径
/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
- 文章链接:
- 版权声明
- 非自由转载-非商用-非衍生-保持署名
- 河
掘
思
知
简