画皮卡丘、小黄人、哆啦A梦、瑞克和莫提、琦玉,任何CSS画出的非图片图形,Canvas SVG
目录 § ⇧
回顾技术栈
CSS3 + ES6综合应用
- 必备知识:CSS3布局和定位、CSS3 transform、JS DOM操作
浏览器 JS 的能力
- 操作DOM
- 操作AJAX
目前的目标
- 用jQuery操作DOM(之后改成Vue React)
- 用axios操作AJAX
一个项目
- 60%的时间在写CSS 布局
- 20%的时间在写JS
- 20%的时间在想错在哪
选择模仿目标
勿原创,不是设计师
- 模仿别人的界面,代码自己写
- codepen.io
项目介绍
特点
- 移动端支持
- CSS绘制任意图形,并同步显示代码
- 界面简洁友好
- 使用指南
技术栈
SCSSparcel实时预览@media媒体查询- 表驱动编程-自动绑定事件
Git&GitHub- 短链处理
- 二维码链接
后续更新
- vue版本
- react版本
思路
- 实现手机端
- 写 HTML
- 写 SCSS
- 写 JS(事件监听、DOM 操作)
- 实现PC端
- 加 媒体查询 CSS
- 写 JS(单独处理PC端逻辑)
- 发布到
GitHubGitee
创建和使用
开发
|
|
build 命令
|
|
修改 package.json
|
|
取色工具 Snipaste
制作过程
index.html
|
|
基础SCSS样式
|
|
制作鼻子和眼睛样式
以页面宽度中线为基准,画鼻子
|
|
- 注意当写border 不同方向上数值的时候, 指的是那个方向上的厚度
- CSS用border模拟图形(三角 + 圆/椭圆)
- tips提示条
- 各同级元素的单位保持一致(px / em ),和百分比 / vh vm 混用注意
- 使用绝对定位时,没有普通文档流元素,body 元素高度设为
{min-height: 100vh;} - 对具体某个元素使用绝对定位时,父元素设为相对定位
- 平滑椭圆边线圆弧
border-bottom-left-radius: 60px 30px; transform属性会相互覆盖,必须一次性写出transform: translate(110px, -30px) scaleX(-1) rotate(-20deg);
制作眼睛
|
|
制作上嘴唇
|
|
制作下嘴唇
|
|
添加动态效果
|
|
移动端适配
px -> rem
动态显示代码
简单的测试
蹦字
|
|
动态样式
|
|
同时以文本和HTML的形式展示style标签
同时输出两种形式,一个用
innerHTML,另一个用innerText
- 用
innerText,以文本形式展示,但标签不会生效 同时用
innerHTML写入标签1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24// <div id="demo"></div> // <div id="demo2"></div> const string = ` <style> body { background-color: red; } </style> `; let n = 1; demo.innerText = string.substr(0, n); demo2.innerHTML = string.substr(0, n); // console.log(n); let id = setInterval(() => { n += 1; // console.log(n + ':' + string.substr(0, n) + '/' + string.length); if(n > string.length){ window.clearInterval(id); return } demo.innerText = string.substr(0, n); demo2.innerHTML = string.substr(0, n); console.log(n); }, 100);
先写好HTML到
<div id="html"></div>里
|
|
再动态显示 CSS 代码 动态加载CSS
- 分屏固定显示代码文本和效果
- 隐藏展示样式
#demo2的代码,不影响效果 - 写自动滚动到底部的代码,每写一句,就往下拉滚动条
- 滚动距离为 可滚动高度
demo.scrollHeight,再减去滚动条本身的高度 - 下拉的距离等于滚动的高度
demo.scrollTop = demo.scrollHeight - 对JS来说,有没有滚动条,都可以滚动页面,只是用户不能操作滚动,设置
overflow-y: auto 或者用另一种隐藏滚动条的方法(搜CSS 隐藏滚动条)
#demo::-webkit-scrollbar{display: none;}只是只能效果上看不见,仍可以用滚轮滚动页面1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21<style> #demo2{ display: none; } #demo { position: fixed; top: 0; left: 0; width: 100%; height: 30vh; border: 1px solid red; overflow: scroll; } #html { position: fixed; bottom: 0; left: 0; width: 100%; height: 80vh; } </style>
优化
- 先写好style标签,将
#demo2的div标签改为style标签 - 去除会影响页面整体样式的代码,写到别处,或者用BootCDN 的
normalize.css 只显示对页面有直观效果的CSS代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50<body> <style id="demo2"></style> <div id="demo"></div> <style> #demo2{ display: none; } #demo { background-color: whitesmoke; position: fixed; top: 0; left: 0; width: 100%; height: 30vh; border: 1px solid red; overflow-y: auto; } #demo::-webkit-scrollbar{ display: none; } #html { position: fixed; bottom: 0; left: 0; width: 100%; height: 80vh; } </style> <div id="html"> <div class="face"> <div class="eye"></div> <div class="eye eye-right"></div> <div class="nose-tip "></div> <div class="cheek"> <div class="cheek-left"></div> <div class="cheek-right"></div> </div> <div class="mouth"> <div class="lip"> <div class="lip-left"></div> <div class="lip-right"></div> </div> <div class="jaw"> <div class="lower-jaw"></div> </div> </div> </div> </div> <script src="test.js"></script> </body>
抽出默认样式代码
|
|
抽出动画代码
|
|
抽出string样式代码
|
|
模块化导出JS独立代码
将不相关的独立代码写到单独的文件中,导出,在需要的时候导入
|
|
stringDefault.js
|
|
string.js
|
|
stringAnimation.js
|
|
集中导入
|
|
- 导出的可以使变量、函数等
重构代码
1 2 3 4 5 6 7 8 9 10 11const run = () => { n += 1; if(n > string.length){ window.clearInterval(id); demo2.innerHTML += stringAnimation; return } demo.innerText = string.substr(0, n); demo2.innerHTML = stringDefault + string.substr(0, n); demo.scrollTop = demo.scrollHeight; }
添加暂停、变速按钮
|
|
|
|
暂停就是取消计时器,播放就是再设置一个计时器,变速就是更改间隔秒数
|
|
重复即丑,重构JS代码
|
|
按作用抽象成函数,重构后的代码,主逻辑清晰
|
|
把相似的代码封装成一个函数,取个名字,调用它
|
|
抽象事件函数,再重构
|
|
使用正规的语法,获取元素
|
|
用面向对象,再重构
- 先声明
const player = {...},后使用let id = player.play(); player.runplayer.pause1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68//init // 先声明 n, 再使用 n let n = 1; demo.innerText = string.substr(0, n); demo2.innerHTML = stringDefault + string.substr(0, n); // let intervalTime = 80; // const run = () => {...} // const play = () => {...} // const pause = () => {...} // let id = play(); // const slow = () => {...} // const normal = () => {...} // const fast = () => {...} // btnPause.onclick = pause; // btnPlay.onclick = () => {...} // btnSlow.onclick = slow; // btnNormal.onclick = normal; // btnFast.onclick = fast; // init let intervalTime = 80; let n = 1; const player = { run:() => { n += 1; if(n > string.length){ window.clearInterval(id); demo2.innerHTML += stringAnimation; return } demo.innerText = string.substr(0, n); demo2.innerHTML = stringDefault + string.substr(0, n); demo.scrollTop = demo.scrollHeight; }, play:() => { return setInterval(player.run, intervalTime); }, pause: () => { window.clearInterval(id); }, slow: () => { player.pause(); intervalTime = 80; id = player.play(); }, normal: () => { player.pause(); intervalTime = 16; id = player.play(); }, fast: () => { player.pause(); intervalTime = 0; id = player.play(); } }; let id = player.play(); btnPause.onclick = player.pause; btnPlay.onclick = () => { id = player.play(); } btnSlow.onclick = player.slow; btnNormal.onclick = player.normal; btnFast.onclick = player.fast;
初始化方法
|
|
内置功能
id = player.play();到player.play中
|
|
将绑定事件放入初始化函数
|
|
表驱动编程:哈希Map 避免重复代码声明事件
|
|
将hashTable放到声明中去
|
|
- 哈希Map,读取字符串
- 不能在定义对象时调用此对象
区别于函数,函数可以延迟定义
1 2 3 4 5let a = { xxx: a; // a 未定义 } a // {xxx: undefined}
防御型编程
|
|
player.events.toStringplayer.events.valueOf等原型链上继承的方法虽然默认是不可枚举- 但不排除对象的原型链被沙雕加了可遍历的属性
Object.prototype.x = 1,然而这是不希望被遍历到的console.log('key'+ key) - 用
for in遍历对象时会遍历到继承的属性,必须判断排除 用
hasOwnProperty判断1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25const player = { init: () => { demo.innerText = string.substr(0, n); demo2.innerHTML = stringDefault + string.substr(0, n); player.play(); player.eventBind(); }, events : { '#btnPause': 'pause', '#btnPlay': 'play', '#btnSlow': 'slow', '#btnNormal': 'normal', '#btnFast': 'fast' }, eventBind: () => { for(let key in player.events) { // 防御型编程 if(player.events.hasOwnProperty(key)){ const value = player.events[key]; // pause /play / slow /... document.querySelector(key).onclick = player[value]; // 用不同的变量去作为对象属性名 } } }, //... }这就属于防御型编程
另一种方法
|
|
目前代码的结构
|
|
将
player对象声明和初始化以及模块化的外代码全干掉
- 将
n: 1,放到player里 ->player.n - 将
intervalTime: 80,放到player里 ->player.intervalTime - 将
id: undefined放到player里 ->player.id 用
ui放两个页面元素,表示UI界面player.ui.demoplayer.ui.demo21 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69// 模块化 import stringDefault from './stringDefault.js' import string from './string.js' import stringAnimation from './stringAnimation.js' const player = { id: undefined, n: 1, intervalTime: 80, ui: { demo: document.querySelector('#demo'), demo2: document.querySelector('#demo2') }, init: () => { player.ui.demo.innerText = string.substr(0, player.n) player.ui.demo2.innerHTML = stringDefault + string.substr(0, player.n) player.play() player.eventBind() }, events : { '#btnPause': 'pause', '#btnPlay': 'play', '#btnSlow': 'slow', '#btnNormal': 'normal', '#btnFast': 'fast' }, eventBind: () => { /* 避免取到原型链上的属性 */ Object.getOwnPropertyNames(player.events).forEach( (key) => { const value = player.events[key] // pause /play / slow /... document.querySelector(key).onclick = player[value] }) }, pause: () => { window.clearInterval(player.id) }, run:() => { player.n += 1 if(player.n > string.length){ player.pause() player.ui.demo2.innerHTML += stringAnimation return } player.ui.demo.innerText = string.substr(0, player.n) player.ui.demo2.innerHTML = stringDefault + string.substr(0, player.n) player.ui.demo.scrollTop = player.ui.demo.scrollHeight }, play:() => { player.pause() player.id = setInterval(player.run, player.intervalTime) }, slow: () => { player.pause() player.intervalTime = 80 player.play() }, normal: () => { player.pause() player.intervalTime = 16 player.play() }, fast: () => { player.pause() player.intervalTime = 0 player.play() } } player.init()
美化按钮
|
|
|
|
|
|
总结
VSCode V.S. WebStorm
xxx.log -> console.log(xxx)
部署到 GitHub Pages
演示中,不能把
<style></style>写到<body></body>里,会被parcel删掉
- 单独保存样式到一个
style.css里,就好了
.gitignore
|
|
部署方式
每次改完代码,必须运行这一行,才能正确的请求 JS 和 CSS:
1parcel build src/index.html --public-url .
yarn build 一键发布
再次build的时,只需用
yarn init -y创建package.json
在
package.json中加一段脚本1 2 3"scripts": { "build":"rm -rf dist && parcel build src/index.html --no-minify --public-url ./" },
再次 yarn build
参考文章
相关文章
- 画一只皮卡丘 预览链接
- 会动的代码 预览链接
- 点击动画效果预览
- 示例作品,声音,快慢播放
- 代码 test.html
- 源代码1 源代码2 旧版代码
- CodePen - Pure CSS PokeBall Radio Button