如何给localStorage设置一个过期时间?

引言 这个话题其实在上次分享<小程序填坑记里讲过了>已经讲过(大佬可绕过哦~),但后来群里/评论都有些同学,提到了一些疑问,问能否单独整理一篇更为详细的分享,讲解一下细节和完善提到的不足,如是有了下文????。 —— 「 用心分享 做有温度的攻城狮,我是首席填坑官——苏南 」各位大佬早安,这里是@IT·平头哥联盟,我是首席填坑官∙苏南,用心分享 做有温度的攻城狮。公众号:honeyBadger8,群:912594095思考点 从我们接触前端起,第一个熟悉的存储相关的Cookie或者来分析我们生活中密切相关的淘宝、物流、闹钟等事物来说起吧,Cookie从你设置的时候,就会给个时间,不设置默认会话结束就过期;淘宝购物 从你下单付款起,就会给这件货物设置一个收货期限时间,过了这个时间自动认为你收货(即订单结束);闹钟 你设置的提醒时间,其实也就是它的过期时间;再比如与您每天切身相关的产品需求,过完需求,你给出的上线时间,也就是这个需求的过期时间;再通俗点讲,您今年的生日过完到明年生日之间也是相当于设置了有效期时间;以上种种,我们能得出一个结论任何一件事、一个行为动作,都有一个时间、一个节点,甚至我们可以黑localStorage,就是一个完善的API,为什么不能给一个设置过期的机制,因为sessionStorage、Cookie并不能满足我们实际的需求。实现思路 抱歉,黑localStorage不完善,有点夸张了,综合上述的总结,问题就简单了,给localStorage一个过期时间,一切就都so easy ?到底是不是,来看看具体的实现吧:简单回顾//示例一:localStorage.setItem(’test’,1234567);let test = localStorage.getItem(’test’);console.log(typeof test, test); //示例二:localStorage[’name’] = ‘苏南’;console.log(localStorage[’name’]);/输出:“1234567” ,‘苏南’,这里要注意,1234567 存进去时是number 取出来就成string了/重写 set(存入) 方法:首先有三个参数 key、value、expired ,分别对应 键、值、过期时间,过期时间的单位可以自由发挥,小时、分钟、天都可以,注意点:存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify,这个时间如何设置呢?在这个值存入的时候在键(key)的基础上扩展一个字段,如:key+’expires’,而它的值为当前 时间戳 + expired过期时间具体来看一下代码 :set(key, value, expired) { /* * set 存储方法 * @ param {String} key 键 * @ param {String} value 值, * @ param {String} expired 过期时间,以分钟为单位,非必须 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 / let source = this.source; source[key] = JSON.stringify(value); if (expired){ source[${key}__expires__] = Date.now() + 100060expired }; return value;}重写 get(获取) 方法:获取数据时,先判断之前存储的时间有效期,与当前的时间进行对比;但存储时expired为非必须参数,所以默认为当前时间+1,即长期有效;如果存储时有设置过期时间,且在获取的时候发现已经小于当前时间戳,则执行删除操作,并返回空值;注意点:存储的值可能是数组/对象,取出后不能直接返回,需要转换 JSON.parse,具体来看一下代码 :get(key) { / * get 获取方法 * @ param {String} key 键 * @ param {String} expired 存储时为非必须字段,所以有可能取不到,默认为 Date.now+1 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 / const source = this.source, expired = source[${key}__expires__]||Date.now+1; const now = Date.now(); if ( now >= expired ) { this.remove(key); return; } const value = source[key] ? JSON.parse(source[key]) : source[key]; return value;}重写 remove(删除) 方法:删除操作就简单了,;remove(key) { const data = this.source, value = data[key]; //首席填坑官∙苏南的专栏 delete data[key]; delete data[${key}__expires__]; return value;}优化点:记得上次有个同学,是这么评论的:「 删除缓存能放到constructor里面执行么,放到get里面 不取就一直在那是不是不太好?」;所以本次优化做一个初始化删除操作,清除已经过期的数据;为什么不用for in而是 for ? for in循环遍历对象的属性时,原型链上的所有属性都将被访问,解决方案:使用hasOwnProperty方法过滤或Object.keys会返回自身可枚举属性组成的数组;class storage { constructor(props) { this.props = props || {} this.source = this.props.source || window.localStorage this.initRun(); } initRun(){ / * set 存储方法 * @ param {String} key 键 * @ param {String} value 值,存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify * @ param {String} expired 过期时间,以分钟为单位 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 */ const reg = new RegExp("expires"); let data = this.source; let list = Object.keys(data); if(list.length > 0){ list.map((key,v)=>{ if( !reg.test(key )){ let now = Date.now(); let expires = data[${key}__expires__]||Date.now+1; if (now >= expires ) { this.remove(key); }; }; return key; }); }; }}总结: 以上就是今天为大家总结的分享,您GET到了吗?小程序、sessionStorage、localStorage,都适用,做些许调整即可哦,希望今天的分享能给您带来些许成长,如果觉得不错,记得关注下方公众号哦,每周第一时间为您推最新分享????????。更多文章:easy-mock 最好的备胎没有之一immutability因React官方出镜之使用总结分享!面试踩过的坑,都在这里了~你应该做的前端性能优化之总结大全!如何给localStorage设置一个过期时间?动画一点点 - 如何用CSS3画出懂你的3D魔方?作者:苏南 - 首席填坑官链接:https://blog.csdn.net/weixin_…交流:关注公众号邀请您加入交流群 honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

November 15, 2018 · 2 min · jiezi

浅谈easy-mock 最好的备胎没有之一

引言 今天我们来聊聊Mock,随着互联网发展,这两年前后端分离的开发模式兴起,Mock也从以住的幕后走上了台面,让更多的人而得知,以前传统的开发方式Mock大多局限在后端人员接触较多一些。 Mock已经是老生常谈了,网上一搜索就很多,各位前辈们都讲的很到位,但今天我只讲它——easy-mock。 为什么会突然来聊它,这个就说来话长了,个人简介里就说过,专注于分享工作中遇到的坑,但这一次不是我的坑,来源于QQ群友(# 如果您有想知道的故事,而正好我也会,那么就由我为您讲出来吧,欢迎留言哦 # ),请看下图:这里是@IT·平头哥联盟,我是首席填坑官—苏南,用心分享 做有温度的攻城狮。什么是Mock 什么是Mock?? Mock其实就是真实数据存在之前,即调试期间的代替品,是个虚拟的存在,用人话讲它就是个备胎,如女生长的好看,追她的人多,但又不是很满意但也不拒绝,在自己心仪的小哥哥出现之前,一直吊着你????!如何Mock数据?不要告诉我 new 一个哦,对象可以 new,备胎可new不出来呢????;方法一:最low的方式将 Mock 数据写在代码里、json文件里;方法二:利用 Charles 、Fiddler等代理工具,将 URL 映射到本地文件;方法三:本地起 Mock Server,即mockjs,有点麻烦每次修改了后还要重启服务,nodemon能解决,但开的东西多了,电脑卡出翔,维护也麻烦;方法四:规范些的公司自己已经集成了一套mock数据体系;重点来了:easy-mock一个在线 Mock 平台,活儿好又性感是你备胎的最佳选择。当然优秀的你可能还有很多其他的方式,欢迎补充。//mock 基本使用示例import Mock from “mockjs”;Mock.mock({ “code”: 0, “message”: “请求成功”, “data|20”: [{ “name”: “@cname”,//cname 中文,name 英文 “userId”: “@id”, “lastDate”: “@datetime” }]})什么是easy-mock,能给我们带来什么?Easy Mock 是一个可视化,并且能快速生成 模拟数据 的持久化服务,Easy Mock 支持基于 Swagger 创建项目,以节省手动创建接口的时间;简单点说:Easy Mock就是一个在线创建mock的服务平台,帮你省去你 配置、安装、起服务、维护、多人协作Mock数据不互通等一系列繁琐的操作, 它能在不用1秒钟的时间内给你所要的一切,呼之即来、挥之即去的2018最优秀备胎没有之一,完全不用担心负任何责任哦。更多优点它在等你去发现哦……深入浅出 - 简介就跟人一样长的再好看,了解过后才懂,一样东西也是如何,谁用谁知道;Easy Mock支持团队协作,也可以是个人项目,以下以个人项目为例,与多人协作没有区别,只是少了成员邀请;深入浅出 - Mock语法回顾@ip -> 随机输出一个ip;@id -> 随机输出长度18的字符,不接受参数;“array|1-10” -> 随机输出1-10长度的数组,也可以直接是固定长度;“object|2” -> 输出一个两个key值的对象,"@image()" 返回一个占位图url,支持size, background, foreground, format, text;等等,这里就不再一一介绍。深入浅出 - 创建一个接口它的写法,跟Mock.js一模一样,上面代码已经展示过,更多示例使用Easy Mock创建一个接口,请看下图:深入浅出 - 高阶用法 Function在线编辑,它也能支持 function ,是不是很优秀,能获取到全部请求头,可以让我们像写在js里一样写逻辑,写运算,当然它肯定是还有很多局限性的,如并不支持ES6,有一点需要注意的是 function 里要写传出Mock对象,不能直接@…,来看示例:对象描述MockMock 对象_req.url获得请求 url 地址_req.method获取请求方法_req.params获取 url 参数对象_req.querystring获取查询参数字符串(url中?后面的部分),不包含 ?_req.query将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字字符串则返回一个空对象_req.body当 post 请求以 x-www-form-urlencoded 方式提交时,我们可以拿到请求的参数对象…_req.cookies、ip、host等等,我只是一个代码的搬运,更详细请看这里//简单模拟登录,根据用户传入的参数,返回不同逻辑数据{ defaultName:function({_req}){ return _req.query.name; }, code: function({_req}){ return this.defaultName ? 0 : -97; }, message: function({_req}) { return this.defaultName ? “登录成功” : “参数错误”; }, data: function({req,Mock}){ return this.defaultName ? { token: Mock.mock("@guid()"), userId: Mock.mock("@id(5)"), cname: Mock.mock("@cname()"), name: Mock.mock("@name()"), avatar: Mock.mock("@image(200x100, #FF6600)"), other:"@IT·平头哥联盟-首席填坑官∙苏南 带你再谈Mock数据之easy-mock" }:{} }}深入浅出 - 在线调试再优秀的工程师写出的代码也一样要测试,没有人敢说自己的代码无Bug,Easy Mock 它是真的懂你的,已经为你准备好了,接口编写好后,立马就能让你测试,是不是觉得很棒棒呢??如果是你自己本地写mock数据,你需要重启服务、手动写参数、可能还需要整个测试页,知道你已经非常饥渴迫切的想要知道,具体的调试方式了:看不清吗??已经为你备好在线调试链接,支持POST、GET、PUT等方式,因gif图加载比较大,就不一一演示了结尾: 天下无不散之宴席,又到说再见的时候了,以上就是今天苏南为大家带来的分享,您GET到了吗?Easy Mock更多强大之处自己去折腾吧,#用心分享 做有温度的攻城狮#,希望今天的分享能给您带来些许成长,如果觉得不错记得点个赞哦,,顺便关注下方公众号就更棒了呢,每周为您推最新分享????????。更多文章:immutability因React官方出镜之使用总结分享!小程序项目之做完项目老板给我加了6k薪资~小程序项目之填坑小记面试踩过的坑,都在这里了~你应该做的前端性能优化之总结大全!如何给localStorage设置一个过期时间?动画一点点 - 如何用CSS3画出懂你的3D魔方?动画一点点 - 手把手教你如何绘制一辆会跑车SVG Sprites Icon的使用技巧作者:苏南 - 首席填坑官链接:https://blog.csdn.net/weixin…交流群:912594095、公众号:honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

November 7, 2018 · 1 min · jiezi

小程序项目之填坑小记

作者:首席填坑官∙苏南公众号:honeyBadger8,本文原创,著作权归作者所有,转载请注明原链接及出处。简诉 是的,真的,你没有看错,我就是上次那个加薪的,但是现在问题来了,最近又搞了个小程序的需求,又填了不少坑,其中的辛酸就不说了,说多了都是泪????????,此处省略三千字 ………^……,说重点吧,反正最后就是差点这让老板叫走人了,你说优秀不优秀~。 前段时间网上一直说的“<u>你可以骂那些中年人,尤其是有车有房的……</u>”,虽然我没有房、也没有车,但也坚决不做那个可以随便骂的中年人(人到中年不如狗??),不存在的啦,这个仇宝宝已经记下了????,先分享一下最近遇到的几个坑吧。 —— 我是首席填坑官——苏南,早上好,新的一周加油。填坑一,canvas遮挡问题:随着小程序的API调整,很多东西都要用户手动授权,不能直接调用后,toast、弹窗这种提示的场景越来越多了,下图就是公司活动的canvas合成,现在微信API不让直接调用授权了,某些场景要多一个弹窗来提示用户开启设置,但当遇上canvas API这个大佬后,一切都变了,谁都只能站在它后面,场景一 :如之前拒绝授权了,后续引导用户打开设置页,即 wx.openSetting,下图就是:坑一 小结 :当遇上这种情况,我的第一思路是 设置样式:visibility: hidden;opacity:0;,但是结果是让人失望的,canvas 大佬就是大佬,这两属性在手机上失效了,该显示还是显示,你阻挡不了它的光辉,真的,不信可以去测试!解决思路:canvas 图片合成,获取到图片的地址后,隐藏canvas,改用image标签显示,这种场景有局限性,如果你的需求是echart交互的,显示挂了;cover-view 貌似也是有局限,<cover-view /> 内只能嵌套 <cover-view /> 和 <cover-image />,view 标签的子节点树在真机上都会被忽略,这是我测试后的浏览器给出的警告,如果自定modal,要加button按钮让用户点击后授权某功能,肯定也就挂了 ;当弹窗出现的时候,隐藏canvas,这种比较暴力,但覆盖面广,任何场景都能照顾到,却也影响体验;把canvas定位移动到屏幕之外绘制内容;有同学可能说直接使用原生的 wx.showModal,但官方目前,button还不支持设置open-type属性;等微信小程序官方修复????,好吧,看到这里你肯定笑了~,这不是一个方法,估计还没等到老板真叫你走人了,欢迎大佬们补充!!!填坑二,Maximum call stack size exceeded发现这个bug,要从最近换了个手机说起,用了3年的5S终于歇菜了(再也买不起iphone了~),换了个android vivo x23, 以为从此走上人生巅峰了,现实却给了我一个响亮的耳光,又是一个记忆深刻的梗~,被组里的同事笑话好久!!话说,堆栈溢出,是怎么造成的呢?—— 循环引用;同时我又有些疑惑了,为什么其他手机都正常,就vivo 报了这个错,同样的代码,希望有大神看到能给于解惑!先来看个示例,简单演示一下 :let sum = 20; (function test(){ sum–; console.log(sum); test(); /* if( sum > 0 ){ test(); }*/ })()# 而项目中的报错是这样的 #: //fetch.js import wepy from ‘wepy’ import login from ‘./login’; ……省略N行 //login.js import {fetch} from “./fetch.js”; import Storage from “./storage.js”; ……省略N行 //更改后 login.js ,避免了循环引用 loginFn = ()=>{ require("./fetch.js").fetch({ url:"/wabApi/login/login", }); }坑二 小结 :循环引用,可以理解为 a.js内调用了b.js,b.js里又引用了a.js,所以在项目开发中要注意一下,看了下网上的讨论,这个问题需要等官方解决,貌似h5里是可以这样写的。填坑三,canvasGetImageData、canvasToTempFilePath这两个方法,之间的调用,要做一定的延时,不明白是为什么,如果不做延时,也不会报错,也不提示,方法执行完,canvas上还是空白的;但是让人尴尬的是,此在写总结的我,又验证了一下,不加setTimeout,调试器上可以,真机挂了!目测跟绘制的目标对象大小有关系!其他微信API的调整,如:「 wx.getUserInfo」「 wx.getSetting」「 wx.openSetting」「 wx.getPhoneNumber」等这些现在需要添加按钮,用户手动来点击,带来的不便大家都知道了,就不再多说;字体文件 ,加载报错,但也能正常显示,而且只有第一次报错哦;其他还有待发现的坑…… @font-face { font-family: ’test’; src: url(“https://cdn-xx.xxx.com/common/font/font.ttf") format(’truetype’); font-weight: normal; font-style: normal; }扯淡段子 小明公司之前上线的小程序项目,好久没有迭代了,产品说有个需求要改一下,很快,就一点点东西,比如一个按钮UI调整一下,改了赶紧发上去,嗯,最好今天就发了审核吧; 这话,是你会怎么接呢??小明说要一天,产品就惊呆了????,这家伙没有发烧吧??小明后来经过半天的努力,终于让产品知道了小程序API更新后,再发布的相关流程都要改的; 有谁能理解小明的痛苦?有谁能理解小程序的API更新机制?更新过的API没有向下兼容的余地,已经发布过的就放过你了,但是你再改动,所有它改过的流程,你都要改一遍。结尾 开心一笑,给自己找点乐,为今天的分享画上圆满的句号,以上就是我最近的一次小小填坑记整理,希望能给其他同学带来些许帮助,文中如有理解不足之处,请指正????。作者:苏南 - 首席填坑官链接:https://blog.csdn.net/weixin_…交流群:912594095、公众号:honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

November 5, 2018 · 1 min · jiezi

如何用CSS3画出懂你的3D魔方?

作者:首席填坑官∙苏南公众号:honeyBadger8,群:912594095,本文原创,著作权归作者所有,转载请注明原链接及出处。前言 最近在写《每周动画点点系列》文章,上一期分享了< 手把手教你如何绘制一辆会跑车 >,本期给大家带来是结合CSS3画出来的一个立体3d魔方,结合了js让你随心所欲想怎么转,就怎么转,这里是 @IT·平头哥联盟,我是首席填坑官∙苏南(South·Su),我们先来看看效果,然后再分解它的实现过程吧绘制过程: 好吧,gif图看着好像有点不是很清晰,想在线预览的同学,可点击在线预览 ????,废话不多扯了,先来分析一下,看如何实现这个功能吧。∙ API预热 :本次示例是一个立体的正方形,既然有立体效果,肯定少不了CSS3中的 -webkit-perspective-透视、preserve-3d-三维空间,这个两个是重点哦,当然还有transform-origin、transition、transform等,先来回故一下 API 怎么是讲的吧:perspective 取值 :none :不指定透视 ;length :指定观察者与「z=0」平面的距离,使具有三维位置变换的元素产生透视效果。「z>0」的三维元素比正常大,而「z<0」时则比正常小,大小程度由该属性的值决定,不允许负值。transform-style 取值 :flat :指定子元素位于此元素所在平面内;preserve-3d :指定子元素定位在三维空间内,当该属性值为 preserve-3d时,元素将会创建局部堆叠上下文;小结 :决定一个变换元素看起来是处在三维空间还是平面内,需要该元素的父元素上定义 <’ transform-style ‘> 属性,也就是说想某元素有三维效果,需要设定它的父级有 preserve-3d 。transform-origin 取值 :percentage:用百分比指定坐标值。可以为负值;length:用长度值指定坐标值。可以为负值;left:指定原点的横坐标为left;center①:指定原点的横坐标为center;right:指定原点的横坐标为right;top:指定原点的纵坐标为top;center②:指定原点的纵坐标为center;bottom:指定原点的纵坐标为bottom;transform、transition等,就不介绍了/* perspective 使用示例:*/div{ -webkit-perspective:600px; perspective:600px;}/transform-style 使用示例:/.preserve{ transform-style:preserve-3d; -webkit-transform-style:preserve-3d;} /transform-origin 使用示例:/.preserve{ -webkit-transform-origin:50% 50% -100px; or -webkit-transform-origin:bottom; or -webkit-transform-origin:top; …………} ∙ 绘制6个面 :是的,我没有说错,就是6个面:上、正面、下、背面、左、右,上面API讲了这么多,来实践试一下吧,写6个div,结构大概是这样的,也是接下来的魔方需要的结构:<div class=“cube”> <div class=“cube-inner running”> <p class=“single-side s1”><span>最</span></p> <p class=“single-side s2”><span>懂</span></p> <p class=“single-side s3”><span>你</span></p> <p class=“single-side s4”><span>的</span></p> <p class=“single-side s5”><span>魔</span></p> <p class=“single-side s6”><span>方</span></p> </div></div>!!!发生了什么??是不是很吃惊??说好的值越大,透视效果越强的呢?后面明明藏了个妹子,怎么看没有透视出来?开始我也是跟你一样吃惊的,但瞬间就悟透了,少了rotate,加个它再来看看效果吧:.cube{ width:200px; height:200px; margin:10px auto; padding:260px; position:relative; -webkit-perspective:600px; perspective:600px; transition: .5s ;}.cube-inner{ width:200px; height:200px; position:relative; -webkit-transform-style:preserve-3d; transition:.3s; -webkit-transform-origin:50% 50% -100px; transform: rotateX(45deg);}.cube:hover{ /鼠标经过时,把 perspective 过渡到100 / -webkit-perspective:100px; perspective:100px;}既然API有效,那么拉下来我们就画出6个面吧,按:上、正面、下、背面、左、右,这个顺序来设置吧;首先,我们要指定它们是在三维空间内的preserve-3d,也就是6个面的父级要设置 transform-style 样式;以上都设置好后,再来看看6个面吧,为了便于区分,给它们每个都设置了不同颜色(用了css3的渐变 radial-gradient)——不想手写的同学推荐一个网站可在线设置你要的效果,复制样式即可,先来一睹风采,为了便于观察,整体角度旋转了10deg:说到渐变,偶然之间发现了一个有意思的东西hue-rotate,它能在你初始的颜色基础上旋转元素的色调及其内容,从而达到不同的效果。了解更多hue-rotate : The hue-rotate() CSS function rotates the hue of an element and its contents. Its result is a <filter-function>. 上 - “最”:.cube-inner .single-side.s1{ /s1顶部/ left:0;top:-200px; background: radial-gradient(circle, rgba(255,255,255,.88), #00adff); background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #00adff); transform-origin:bottom; -webkit-transform-origin:bottom; transform:rotateX(90deg); -webkit-transform:rotateX(90deg);} 正面 - “懂”:下面就是默认的,什么都不用设置,所以就不展示了 ; 下面 - “你”:即底部,底部的设置,正好跟顶部它是相反的,一个origin 以 bottom为基准为坐标,一个以top为基准为坐标;.cube-inner .single-side.s3{ /s3底部/ left:0;top:200px; background: radial-gradient(circle, rgba(255,255,255,.88), #100067); background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #100067); transform-origin:top; -webkit-transform-origin:top; transform:rotateX(-90deg); -webkit-transform:rotateX(-90deg);} 背面 - “的”:即正面的后边,整体旋转了 135deg,让背面更直观能看到;translateZ 、rotateX 同时移动,形成透视的关系,让它看起来,在正面面的后面;下图二,把默认的正面,设置了透明度,可以看出,背面的透视效果;.cube-inner .single-side.s4{ /s4背部/ z-index:2; left:0;top:0; background: radial-gradient(circle, rgba(255,255,255,.88), #F0C); background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #F0C); transform:translateZ(-200px) rotateX(180deg) ; -webkit-transform:translateZ(-200px) rotateX(180deg) ; /rotateZ(-180deg) 左右旋转的时候,Z轴旋转180°,因为字是倒着的/} 左侧面 - “魔”:origin以right为基准,left负元素的宽度,rotateY轴旋转90deg;.cube-inner .single-side.s5{ /s5左侧/ left:-200px;top:0; background: radial-gradient(circle, rgba(255,255,255,.88),rgba(33,33,33,1)); background: -webkit-radial-gradient(circle, rgba(255,255,255,.88),rgba(33,33,33,1)); transform-origin:right; -webkit-transform-origin:right; transform:rotateY(-90deg) -webkit-transform:rotateY(-90deg)} 右侧面 - “方”:同理右侧,与左侧正好相反;.cube-inner .single-side.s6{ /s6右侧/ right:-200px;top:0; transform-origin:left; -webkit-transform-origin:left; background: radial-gradient(circle, rgba(255,255,255,.88), #f00); background: -webkit-radial-gradient(circle, rgba(255,255,255,.88), #f00); transform:rotateY(90deg); -webkit-transform:rotateY(90deg);}小结 : 嗯,以上魔方的6个面的绘制过程,基本已经完成,主要在在于transform-origin、rotate、translate等属性的应用,但为了让它更炫酷一些,我们还要给边角加一些光感。∙ 添加高光 :细心的宝宝,前面的布局应该已经发现了,每一行布局的p标签里,都多套了一层span,就是为高光光感,埋下的伏笔,一个平面正方形有四个边,after、before只有两,那么肯定要再套一层,当然方法很多,比如直接用border也是可以的,但比较麻烦,我就选择了现在要讲的这种:after、before设置1px的边框,设置一个线性渐变,中间是白色,两断是过渡到透明的,这样高光就有了,来看一组图吧:∙ CSS 360°旋转 :上面是一个鼠标经过的过渡动画,可以看出立体效果是已经有了,接下来就写一个CSS animation的动画,让它360度旋转,每个角都能看到,这样会显的很666;animation 配合 keyframes 使用,请看代码示例:.cube .cube-inner{ /-webkit-transform:rotateX(180deg) rotateY(0deg) ;/ animation: elfCube 10s infinite ease-in-out; -webkit-animation: elfCube 10s infinite ease alternate;}@keyframes elfCube { 0% { transform: rotateX(0deg) rotateY(0deg); } 50% { transform: rotateX(360deg) rotateY(360deg); } 100% { transform: rotateX(0deg) rotateY(0deg); }}@-webkit-keyframes elfCube { 0% { -webkit-transform: rotateX(0deg) rotateY(0deg); } 50% { -webkit-transform: rotateX(360deg) rotateY(360deg); } 100% { transform: rotateX(0deg) rotateY(0deg); }}∙ 跟随鼠标旋转 :说好的随着鼠标旋转呢??别慌,接下来就是带你装逼,带你飞的时候,首先我们要了解,鼠标在容器内所在的位置,X = e.pageX - ele.offsetLeft, Y = e.pageY - ele.offsetTop;同时要知道元素内的中心点:centerX = width/2,centerY =height/2;然后得出值:axisX = X - centerX,axisY = Y - centerY;PS : 开始尝试想的是鼠标从哪个方向进入,得到它的角度,但发现旋转效果不明显 ,有兴趣的同学可以尝试一下:(((Math.atan2(Y, X) * (180 / Math.PI)) + 180) / 90),参考司徒大神的JS判断鼠标从什么方向进入一个容器;最后,给容器绑上事件:mouseover、mousemove、mouseout,鼠标进入时,暂停css的动画,不然会相互打架哦! ……getAxisX(e){ let left = this.cubeEle.offsetLeft; return e.pageX - left - (this.cubeW/2) * (this.cubeW>this.cubeH ? this.cubeH/this.cubeW : 1);}getAxisY(e){ let top = this.cubeEle.offsetTop; return e.pageY - top - (this.cubeH/2) * (this.cubeH>this.cubeW ? this.cubeW/this.cubeH : 1);} ………… …………run(){ this.cubeEle.addEventListener(‘mouseover’,(e)=>this.hoverOut(e),false); this.cubeEle.addEventListener(‘mousemove’,(e)=>this.move(e),false); this.cubeEle.addEventListener(‘mouseout’,(e)=>this.hoverOut(e),false);}hoverOut(e){ //进入/离开 e.preventDefault(); this.axisX = this.getAxisX(e), this.axisY = this.getAxisY(e); if(e.type == ‘mouseout’){ //离开 this.axisX=0; this.axisY = 0; console.log(“离开”) this.cubeInner.className=“cube-inner running”; }else{ this.cubeInner.className=“cube-inner”; console.log(“进入”) }; let rotate = rotateX(${-this.axisY}deg) rotateY(${-this.axisX}deg); this.cubeInner.style.WebkitTransform = this.cubeInner.style.transform = rotate;} ……结尾:-webkit-perspective,-webkit-transform-style,-webkit-transform-origin,radial-gradient、linear-gradient,transform:rotate、translate、scale,transition,animation;以上就是今天为大家带来的分享,以及使用到的知识点API,如文章中有不对之处,烦请各位大神斧正,想学习更多前端知识,记得关注我的公众号哦文章源码获取-> blog-resource ????想直接在线预览 ????作者:苏南 - 首席填坑官交流群:912594095,公众号:honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

October 29, 2018 · 2 min · jiezi

佛系码农~手把手教你如何绘制一辆会跑车

作者:首席填坑官∙苏南来源:@IT·平头哥联盟 交流群:912594095。本文原创,著作权归作者所有,转载请注明原链接及出处前言 灵感来源于前些天捡到钱了,就想着是时候给自己买辆车了,工作这么多年了应该对自己好一点,在网上搜索了一下看到这个车型。其实几年前是买过一辆的,但是不到一个月就被人偷了,伤心了好久。这次一定锁好,上三把锁保证小偷再也偷不走了,于是我拿着钱去买了些益力多,跟同事分享了,心情还是比较愉悦的。—— @IT·平头哥联盟,我是首席填坑官∙苏南(South·Su) ^_^~ 但想来作为一名程序(嗯,还是个菜鸟,专业首席填坑官哦????),车基本是用不上的啦,为啥?因为有改不完的bug,记得刚毕业那时候最大的梦想是:“撩个妹子 携手仗剑天涯,惩奸除恶、劫富济贫,快意人生~”,无奈一入IT深似海,从此BUG改不完啊。所以还是多学习吧,这不就学着画了个车满足一下自己的心里安慰,在这里把大家一起分享一下,唉,有点扯偏了~,大家先来看一下最终的效果图吧! 过程解析: 效果已经看了到,有没有感觉很牛B??其实也就一般般啦~,接下来就让我带大家一起分解一下它的实现过程吧 canvas中文名中:画布,它就跟我们在纸上画画一样,画某样东西之前,我们要先学会构思、拆解你要画的东西,就跟汽车、手机等东西一样,一个成品都是由很多零件组成的,当你拆解开来,一点点完成再组装的,就会变的容易的多。绘制地平线 :首先我们基于画布的高度取一定的比例,在底部画一条线;从观察动画,它还有几个点,这个是用于视差滚动的时候,来欺骗我们的眼睛的,直接一条线肯定再怎么动也没有用,点的移动可以形成一个动画的效果;再加一点修饰,几个点移动有点太单调了,大家可以想像一下,当你骑车的时候,车的速度与周围的事物、建筑、人产生一个交差,那种感觉是很刺激的,那么我们也来加一点东西,让动画看起来更丰富一些,我选择了 三条线,线本身有个渐变过渡的效果,比纯色要灵动些动画看起来更逼真,而且初始它是不在画布范围内的,这个点要注意一下;下面的两张图,第二张是生成gif工具里截出来的,它就是动画的分解,其实所谓的动画,也是由一张张静态图组成,然后快速过渡,让视觉形成了视差,最后欺骗了大脑,我看见动画了……知识点:lineTo、strokeStyle、stroke、restore等,这里不一一讲解了,如有不了解可自行查看 w3school API, horizon(){ /** * 轮子的底部,也称地平线: 1.清除画布 2.画一条直线,且高度6px 本文@IT·平头哥联盟-首席填坑官∙苏南分享,非商业转载请注明原链接及出处 / this.wheelPos = []; this.ctx.save(); this.ctx.clearRect(0, 0, this.canvasW, this.canvasH); let horizonX = 0,horizonY = this.canvasH-100; this.ctx.beginPath(); this.ctx.strokeStyle = this.color; this.ctx.lineWidth=6; this.ctx.moveTo(horizonX,horizonY); this.ctx.lineTo(this.canvasW,horizonY); this.ctx.closePath(); this.ctx.stroke(); Array.from({length:5}).map((k,v)=>{ let dotProportion = (this.canvasW0.49)v-this.oneCent; this.wheelPos.push({x:dotProportion,y:horizonY-this.wheelRadius}); let startX = dotProportion-(this.animateNum2); //用于动画滚动移动 this.ctx.beginPath(); this.ctx.strokeStyle = “#f9f8ef”; this.ctx.lineWidth=6; this.ctx.moveTo(startX,horizonY); this.ctx.lineTo(startX+5,horizonY); this.ctx.closePath(); this.ctx.stroke(); }); this.ctx.restore(); this.shuttle(); // this.wheel(); } shuttle(){ /** * 画几根横线,有点视差,感觉骑车在飞速穿梭的感觉: 本文@IT·平头哥联盟-首席填坑官∙苏南分享,非商业转载请注明原链接及出处 / let shuttleX = this.canvasW+100, shuttleY = this.canvasH/6; let shuttleW = shuttleX+100; [0,40,0].map((k,v)=>{ let random = Math.random()+2; let x = shuttleX+k-(this.animateNum(2.2random)); let y = shuttleY+v24; let w = shuttleW+k-(this.animateNum*(2.2random)); let grd=this.ctx.createLinearGradient(x,y,w,y); grd.addColorStop(0,"#30212c"); grd.addColorStop(1,"#fff"); this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle = grd; this.ctx.lineWidth=3; this.ctx.moveTo(x,y); this.ctx.lineTo(w,y); this.ctx.stroke(); this.ctx.closePath(); }); }绘制车轮 :接下来我们来画车的两个轮子,轮子的位置在哪里呢?我也是观察了有一会才发现的,其实刚才的地平线,两点的位置,就是车轮的中心点;所以在刚才绘制点的时候,就记录了5个点的坐标,这样就省去了一次计算,中间有两次是我们需要的知识点:arc、fill console.log(this.wheelPos); this.wheelPos = this.wheelPos.slice(1,3); //这里取1-3 console.log(this.wheelPos); this.wheelPos.map((wheelItem,v)=>{ let wheelItemX = wheelItem.x, wheelItemY= wheelItem.y-this.wheelBorder/1.5; //外胎 this.ctx.beginPath(); this.ctx.lineWidth=this.wheelBorder; this.ctx.fillStyle = “#f5f5f0”; this.ctx.strokeStyle = this.color; this.ctx.arc(wheelItemX,wheelItemY,this.wheelRadius,0,Math.PI2,false); this.ctx.closePath(); this.ctx.stroke(); this.ctx.fill(); //最后两轮胎中心点圆轴承 this.axisDot(wheelItemX,wheelItemY); this.ctx.restore(); }); this.ctx.restore();同理,上面画好了两个圆,但车轮肯定有轴承,前后轮我做了些汪样的处理,后轮是实心的加了个填充;前轮是画了一点断点的圆,用于动画的转动,在外轮的半径上进行缩小一定比较,画内圈,这里我取了外圈的.94,作为内圆的半径,还加了两个半圆的描边修饰,让动画跑起来的时候,车轮有动起来的感觉,半圆 Math.PI 就是一个180,(Math.PI * degrees) / 180; degrees 就是我们想要绘制的起始/结束角度;从下图可以看出,圆的填充用了 放射性渐变,createRadialGradient-创建放射状/环形的渐变(用在画布内容上) context.createRadialGradient(x0,y0,r0,x1,y1,r1); + createRadialGradient API 说明: x0 = 渐变的开始圆的 x 坐标 y0 = 渐变的开始圆的 y 坐标 r0 = 开始圆的半径 x1 = 渐变的结束圆的 x 坐标 y1 = 渐变的结束圆的 y 坐标 r1 = 结束圆的半径 详细使用请看下面代码的实例 let scaleMultiple = this.wheelRadius*.94; let speed1 = this.animateNum2; //外圈半圆速度 let speed2 = this.animateNum3; //内小圈半圆速度 //后轮 if(v === 0){ //内圆 this.ctx.beginPath(); let circleGrd=this.ctx.createRadialGradient(wheelItemX,wheelItemY,18,wheelItemX,wheelItemY,scaleMultiple); circleGrd.addColorStop(0,"#584a51"); circleGrd.addColorStop(1,"#11090d"); this.ctx.fillStyle = circleGrd; this.ctx.arc(wheelItemX,wheelItemY,scaleMultiple,0,Math.PI2,false); this.ctx.fill(); this.ctx.closePath(); //两个半圆线 [ {lineW:2,radius:scaleMultiple.6,sAngle:getRads(-135+speed1) , eAngle:getRads(110+speed1)}, {lineW:1.2,radius:scaleMultiple*.45,sAngle:getRads(45+speed2) , eAngle:getRads(-50+speed2)} ].map((k,v)=>{ this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle ="#fff"; this.ctx.lineWidth=k.lineW; this.ctx.arc(wheelItemX,wheelItemY,k.radius,k.sAngle,k.eAngle,true); this.ctx.stroke(); this.ctx.closePath(); }); this.ctx.restore(); } 拉下来我们就拿前轮开刀 :前轮也是画了几个半圆,大概就是以某个角度为起点,然后分别画几个半圆,整体是一个半径,中间有断开,如: eAngle = [0,135,270], sAngle = [-45,0,180];就能画出如下图的圆: 具体实现请看下面代码 : //两个圆,再缩小一圈,画线圆 Array.from({length:3}).map((k,v)=>{ let prevIndex = v-1 <= 0 ? 0 : v-1; let eAngle = v135, sAngle = -45+(prevIndex45)+v90; let radius = scaleMultiple.75; let color = “#120008”; this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle = color; this.ctx.lineWidth=3.5; this.ctx.arc(wheelItemX,wheelItemY,radius,getRads(sAngle+speed1),getRads(eAngle+speed1),false); this.ctx.stroke(); this.ctx.closePath(); if(v<2){ //再缩小一圈 let eAngleSmaller = 15+ v210, sAngleSmaller = -30+v90; let radiusSmaller = scaleMultiple*.45; this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle = color; this.ctx.lineWidth=3; this.ctx.arc(wheelItemX,wheelItemY,radiusSmaller,getRads(sAngleSmaller+speed2),getRads(eAngleSmaller+speed2),false); this.ctx.stroke(); this.ctx.closePath(); } this.ctx.restore(); });绘制车身车架 :车架,应该也是本次分享中较大的难点之一,刚开始我也是这么认为的,但认真冷静、冷静、静静之后分析也还好,最开始是用了最笨的办法,lineTO、moveTo、一根一根线的画,画到一半时发现画两个三角或者一个菱形即可,然后再把几根主轴重新画一下,于是两种方法都尝试了一下,先说三角的吧,配合下面画的一个图讲解一下,找到圆盘的中心点,介于后轮半径之上;分析车架的结构,我们可以看为是一个菱形,也可以看着是两个三角形,这里以三角为例,菱形可以看 carBracket2方法;首先算出三角形的起点、再算出三角形的角度、高度,请看下面示图;最后在后轮的中心点盖上一个圆点 用于遮挡三角的部分菱形 就要简单些的,但看起来逼格没有这么高端,就是用lineTo点对点的划线,以上就是车架的绘制过程,其实感觉菱形是是要简单、代码量也少些的,有兴趣的同学可以自己尝试一下,大家可以看下面的主要代码,新手上路,如果有更好的方式,欢迎老司机指点:结论 :使用moveTo把画布坐标从O移动到A点 x/y,lineTo从A开始画到B结束,再从B到C点,闭合,即一个三角完成//方法二:三角形 …………此处省略N行代码 [ { moveX:triangleX1, moveY:triangleY1, lineX1:coordinateX, lineY1:triangleH1, lineX2:discX, lineY2:discY, }, { moveX:triangleX2+15, moveY:triangleY2, lineX1:triangleX1, lineY1:triangleY1, lineX2:discX, lineY2:triangleH2, }, ].map((k,v)=>{ this.ctx.beginPath(); this.ctx.moveTo(k.moveX,k.moveY); //把坐标移动到A点,从A开始 this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=coordinateW; this.ctx.lineTo(k.lineX1,k.lineY1);//从A开始,画到B点结束 this.ctx.lineTo(k.lineX2,k.lineY2); //再从B到C点,闭合 this.ctx.closePath(); this.ctx.stroke(); this.ctx.restore(); }); ……//方法一:菱形 …………此处省略N行代码 this.ctx.beginPath(); this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=coordinateW; this.ctx.moveTo(polygon1X,polygon1Y); this.ctx.lineTo(coordinateX,height); this.ctx.lineTo(discX,discY); this.ctx.lineTo(polygon2X,polygon1Y+5); this.ctx.lineTo(polygon2X-5,polygon1Y); this.ctx.lineTo(polygon1X,polygon1Y); this.ctx.closePath(); this.ctx.stroke(); ……绘制车的豪华宝坐、扶手 :坐位一开始是比较懵逼的,不知道如何下手,圆也不圆、方也不方,后面又去复习一下canvas的API,发现了quadraticCurveTo能满足这个需求,—— 二次贝塞尔曲线画完之后,思考了很久,也没有发现什么技巧,或者规律,可能数学学的不好,没办法只能这样慢慢描了扶手也是一样的,开始尝试quadraticCurveTo,半天也没画成功,后面尝试去找了它邻居bezierCurveTo,—— 三次贝塞尔曲线提示:三次贝塞尔曲线需要三个点。前两个点是用于三次贝塞尔计算中的控制点,第三个点是曲线的结束点。曲线的开始点是当前路径中最后一个点知识点:quadraticCurveTo、bezierCurveTo、createLinearGradient //坐位 this.ctx.restore(); let seatX = (discX-85),seatY=discY-140; let curve1Cpx = [seatX-5,seatY+30,seatX+75,seatY+8]; let curve2Cpx =[seatX+85,seatY-5,seatX,seatY]; this.ctx.beginPath(); // this.ctx.fillStyle = this.gearColor; let grd=this.ctx.createLinearGradient(seatX,seatY,seatX+10,seatY+60); //渐变的角度 grd.addColorStop(0,"#712450"); grd.addColorStop(1,"#11090d"); this.ctx.fillStyle = grd; this.ctx.moveTo(seatX,seatY); this.ctx.quadraticCurveTo(…curve1Cpx); this.ctx.quadraticCurveTo(…curve2Cpx); this.ctx.fill(); //车前轴上的手柄 let steeringX = lever1X-20,steeringY = lever1Y-45; let steeringStep1 = [steeringX+40,steeringY-10,steeringX+40,steeringY-10,steeringX+35,steeringY+15] let steeringStep2 = [steeringX+30,steeringY+25,steeringX+25,steeringY+23,steeringX+18,steeringY+23] this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle = “#712450”; this.ctx.lineWidth=coordinateW; this.ctx.moveTo(steeringX,steeringY); //40 60; this.ctx.bezierCurveTo(…steeringStep1); this.ctx.bezierCurveTo(…steeringStep2); this.ctx.stroke(); this.ctx.closePath();绘制车的发动机、脚踏板 :到了这里,也快接近本文的尾声了,接下来要讲的是是车辆中最重要的部分,车中间齿轮盘,一辆车没有它,你做的再好也是白搭了;前面多次讲到齿轮的中心点,包括两个三角都是以它的中心计算的三角角度,知道了位置那就容易了,一样的先画几个圆,每个按一定的比例缩小;然后外围再画一圈锯齿,这样齿轮大概就画好了,齿轮的技巧在于以圆盘为中心点,画一圈线,它跟时钟的刻度原理是一样的;脚踏板,这个好理解,就是用lineTo画两跟线,其中一根进行一个90度的旋转就ok了,但重点是它在动画过程中的一个过程呢,我的分析过程是这样:竖着的这根轴是,以圆盘齿轮的中点为基点 N* (Math.PI / 180)转动;横着的这根轴,也就是脚踏板,它是以竖着的轴底部为Y轴中心点,以自身宽度的二分之一为X轴为中心点,同样以 N* (Math.PI / 180)的 rotate角度旋转。说了这么多,我们来看几张动态图吧,顺便贴上代码: discGear(coordinateX,coordinateY,coordinateW){ //车中间齿轮盘 disc let discX = coordinateX,discY = coordinateY; let discRadius = this.wheelRadius*.36;//车轮的3.6; let discDotX = discX+discRadius+8,discDotY = discRadius/.98; this.ctx.restore(); this.ctx.save(); this.ctx.translate(discX,discY); // this.ctx.rotate(-(Math.PI/2)); Array.from({length:30}).map((v,index)=>{ let radian = (Math.PI / 15) ; this.ctx.beginPath(); this.ctx.lineCap=“round”; this.ctx.strokeStyle = this.color; this.ctx.rotate(radian); this.ctx.lineWidth=3; this.ctx.moveTo(0,discDotY); this.ctx.lineTo(1.5,discDotY); // ctx.arc(discDotX,discDotY,6,0,Math.PI2,false); this.ctx.closePath(); this.ctx.stroke(); }); this.pedal(discX,discY,discRadius); this.pedal(discX,discY,discRadius,1); this.ctx.restore(); } pedal(coordinateX,coordinateY,discRadius,turnAngle=0){ //脚踏板,分两次初始化,一次在中心齿轮绘制之前,一次在之后, let pedalX = coordinateX, pedalY = coordinateY - discRadius.7; let pedalW = 6, pedalH = discRadius1.9; let radian = (this.animateNum)(Math.PI / 180) ; let radianHor = (this.animateNum)(Math.PI / 180) ; let turnAngleNum = 1; let moveY = 28; if(turnAngle !== 0){ this.ctx.rotate(-180(Math.PI/180)); turnAngleNum = (Math.PI/180); }; this.ctx.beginPath(); this.ctx.rotate(radianturnAngleNum); this.ctx.lineCap=“round”; this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=pedalW; this.ctx.moveTo(-1,moveY); this.ctx.lineTo(0,pedalH); this.ctx.closePath(); this.ctx.stroke(); this.ctx.save(); let pedalHorW = pedalH/1.5,pedalHorH=pedalW; this.ctx.translate(0,pedalH); this.ctx.beginPath(); this.ctx.rotate(-radianHor); this.ctx.lineCap=“round”; this.ctx.fillStyle = “#fff”; this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth =2; this.ctx.roundRect(-pedalHorW/2,-2,pedalHorW,pedalHorH,5); this.ctx.closePath(); this.ctx.fill(); this.ctx.stroke(); this.ctx.restore(); }绘制车的链条 :链条用的是 bezierCurveTo ,cp1x,cp1y,cp2x,cp2y,x,y等参数画出来的,具体看下面代码吧,其实就是两个半椭圆的拼接…… //链条 let chainW = ( coordinateX+discRadius - this.wheelPos[0].x) / 2; let chainX = this.wheelPos[0].x +chainW-5 ; let chainY = coordinateY; this.ctx.save(); this.ctx.translate(chainX,chainY+4.8); this.ctx.rotate(-2(Math.PI/180)); let r = chainW+chainW*.06,h = discRadius/2; this.ctx.beginPath(); this.ctx.moveTo(-r, -1); this.ctx.lineWidth=3; this.ctx.strokeStyle = “#1e0c1a”; this.ctx.bezierCurveTo(-r,h1.5,r,h4,r,0); this.ctx.bezierCurveTo(r,-h4,-r,-h1.5,-r,0); this.ctx.closePath(); this.ctx.stroke(); this.ctx.restore();尾声 以上就是今天@IT·平头哥联盟-首席填坑官∙苏南给你带来的分享,整个车的绘制过程,感觉车架部分应该还有更好的做法,如果您有更好的建议及想法,欢迎斧正,最后送上完整的示例图! 文章源码获取-> blog-resource ???? 想直接在线预览 ????作者:苏南 - 首席填坑官来源:@IT·平头哥联盟链接:https://honeybadger8.github.i…交流群:912594095本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

October 22, 2018 · 3 min · jiezi

这份优化清单,你做了哪些?

引言 大家好,这里是@IT·平头哥联盟,我是首席填坑官——苏南(South·Su),今天是国庆节的第二天,这个假期没有外出(不要问我为什么,自己脑补~????),前些天分享了一篇前端面试汇总的文章,有些同学在群里问了其中的一些细节,其中大家最关心的性能优化这块,今天整理了公司项目中的一些认为不错的点,跟大家一起分享,如有理解错误,请纠正。优化概括1、首先最基本的,CSS样式表放在页面头部Head内且link链式引入,javascript放在底部body结束标签前避免阻塞。2、js/html/css/图片都做压缩合并,图片预加载、懒加载,也是老生常谈了,在这里推荐一个图片无损极限压缩的工具,能压小60~80%左右,比较麻烦的是每次要手动操作——TinyPNG,有兴趣的同学了可以了解一下他们的API,自己封装一个服务调用压缩,不过免费次数有限制哦。3、减少DOM元素数量,减少DOM的操作:减少 DOM 元素数量,合理利用:after、:before等伪类,避免页面过深的层级嵌套;优化javascript性能,减少DOM操作次数(或集中操作),能有效规避页面重绘/重排;如何才算少?抱歉,这个没有办法给出一个标准精确的答案,只能说尽可能去做优化,如数据分页、首屏直出、按需加载等。4、静态资源CDN分发:CDN的意图就是尽可能的减少资源在转发、传输、链路抖动等情况下顺利保障信息的连贯性;通俗的讲就是CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上———曾经人们都说距离产生美,后来变了都说距离产生小三,在这里距离产生的是用户跑路了,所以足以说明CDN的重要性;CDN采用各节点缓存的机制缓存很严重,当我们项目的静态资源(只是之前存放在cdn上的资源)修改后,如果CDN缓存没有做相应更新,则看到的还是旧的网页,解决的办法是刷新缓存,七牛云、腾讯云都可单独针对某个文件/目录进行刷新;广告常说:XX酒虽好,可不要贪杯哦,CDN托管也是如此,合理使用:图片、常用js组件、css重置样式等,即不常改动的文件即可走CDN,包括项目内的一些介绍页;img标签要设置高宽,同样这么做它也能减少页面重绘/重排,使用 WebP 格式图片,它能对原图(png)做到近98%压缩,当然它也不是完美的:WebP最初在2010年发布,目标是减少文件大小,支持无损、有损压缩,动态、静态图片,压缩比率优于 GIF、JPEG、JPEG2000、PNG 等格式,非常适合用于网络等图片传输,现在开始已经被越来越多的浏览器支持,当然 WebP 格式也有它的缺点,算法相对其他格式更加复杂,会在节省流量资源的同时会占用计算资源,对计算机造成更大的负担,WebP支持的像素最大数量是16383x16383。有损压缩的WebP仅支持8-bit的YUV4:2:0格式。而无损压缩(可逆压缩)的WebP支持VP8L编码与8-bit之ARGB色彩空间。又无论是有损或无损压缩皆支持Alpha透明通道、ICC色彩配置、XMP诠释数据,更详细支持说明:caniuse.com优势:体积小几乎可以毫不夸张的说,已经小的不能再小了;小而美的同时,还质量好,几乎看不出来与原图差别;曾经的动态图gif、jpeg压缩都会不清晰,但现在对它来说都是so easy~。缺点/困难:目前并不是所有浏览器都支持WebP,因此需要解决浏览器适配问题;<br/>对于已上线的项目,采用WebP需要替换大量图片,工作量太大(不确定后台程序是否能搞定)。5、域名拆分:什么叫拆分域名?很多公司初始项目搭建,都只申请了一个域名,站点的所有内容(html/php/jsp、js、css、img等都放在一个域名下),域名拆分主要为了增加浏览器资源请求的并行度即并发问题,让浏览器能同时发起更多的请求,也解决了请求默认携带的cookie问题,减少了数据传输字节;如何拆分?以现在前后端分离式开发为例,建议分为三大类:前端类 - 项目业务本身的htm、css、js、图标/片等;静态类 - 即上述提到的CDN资源类;动态类 - 可归为后端API接口类;以下为各浏览器请求并发数,数据来源于chorme搜索,珍爱生命,远离某……????:浏览器HTTP/1.1HTTP/1.0Chrome66火狐66Safari44IE1166IE91010………………6、 减少http请求次数是的,你没有看错,就是减少http请求次数,节省网络请求时间,但你可能又会问,前面不是让拆分域名吗??一个是部署拆分,一个是请求减少,没毛病哦;首先我们来了解一下http的请求过程(简单通俗的阐述一下):DNS 域名解析 - 1. 拿出电话,找到某个接头人的号码;发起 TCP 的 3 次握手 - 2. 接通后暗号:A)、你好,你好,我是长江一号,请问能听到吗?B),你好,我是长江二号,能听到你讲话,你能听到我说什么吗?A)、能听到,我们开始讲正事吧……;正常数据传输中…… - 3. 聊的很嗨;结束传输断链的 4 次挥手 - 4. 聊完了,准备告别:A)、(可以是服务端,也可以是客户端)该说的我都说完了,你自己看着办吧;B)、好的我也说完了;B)、(B紧接着又跟A发了条信息),再见;A)、然后A收到B的话,而B那边已经放下手机挂了,A等了一下听B没有再说啥,也就挂了(挂个毛啊,婊子无情,戏子无义,陪你唠嗑这么久,都不给个好评~????);当然,现在的HTTP/2.0的处理有所不同,2.0过程还有TLS/SSL的处理,HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL(Certificate Authority,申请证书)加密传输协议,HTTPS加密传输、身份认证的网络协议,内容传输经过完整性校验、内容经过对称加密,每个连接生成一个唯一的加密密钥、第三方无法伪造服务端(客户端)身份等众多优势,同时也有劣势因为做的事情多了中间对接的次数同样需要时间,这也是HTTPS更慢的根本原因。上两图吧,这样大家看着清晰一些,但暂时只列了HTTP/1.0的,HTTP/2.0的图下次有时间再补,是有一个大佬指点我的哦,说这样看起来更骚气,大家会更喜欢,哈哈~:结论:从上面的这个过程可以看出,每一次请求都这么复杂,减少http的请求次数是不是很有必要呢??答案是肯定的,我们会以以下几个维度来进行优化:合并 JS、CSS 文件;图片/图标 sprites 合并,或使用iconfont字体图标,或者SVG Sprites;什么是Svg Sprites?;资源按需加载,即当前页面用到什么,就加载什么,避免加载与当前页面无关的事情,这一点现在的React/Vue/Angular等MVVM框架,基于webpack编译打包工具,做的很好;前端数据的缓存(如:一个列表页,进入详情,再返回,这个用户的交互行为是很频繁的,可以对列表的数据进度一个缓存,不用每次返回都进行加载,比如5分钟更新一次。7、 数据设置缓存,好累写不动了,http缓存的设置,之前的面试汇总????如何设置http缓存?吧;8、 站点服务端开启Gzip压缩,当然还可以了解一下Brotli 或 Zopfli ,据说 Brotli 比 Gzip 和 Deflate更有效,有兴趣的同学可以了解一下;8、 避免重定向,尽量减少 iframe 使用,它会阻塞主页面的渲染;9、 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性);10、 合理使用dns-prefetch、prefetch、 preload、 defer、async:dns-prefetch:使用dns-prefetch对项目中用到的域名进行 DNS 预解析,减少 DNS 查询,如: <link rel=“dns-prefetch” href="//honeybadger8.github.io"/>;BAT各大巨头都是这么干的,请看下图,dns的详细解析过程今天先不讲了,码字码不动了,写分享比加班做项目还累,望体谅~;prefetch: 它是一个优先级非常低的资源加载标识,浏览器会在空闲时(即主进程资源加载完成后)下载带有 prefetch标识的资源并缓存到disk,在后续模块使用到这个文件的时候,会直接从缓存读取;该功能webpack有个插件,配置后编译能自动插入到页面上;preload:没错,它就是一个可以预加载资源的属性,详细说明请看官方API,一般情况下我们可能会对接下来的业务需要的audio、img、font、script等资源进行预先加载(甚至是下一个路由页面哦),这样能达到0秒打开页面的效果!暂时就想到这么多了,欢迎补充……总结:推荐几个工具:WebPagetest、Lighthouse、SpeedCurve、New Relic 等主动/被动监测工具,都能高效帮助我们分析发现问题的所在,从而对症下药;DNS预解析的是非重要的,它是一个url到解析IP,到查询根服务器的一个过程,可能会在下一次单独总结出来分享,有兴趣的同学也可以自行先了解一下,要把一个项目做好,每一个细节都很重要,希望今天的分享能给大家的工作带来些许帮助,谢谢!文章分享计划: 最近一直在思考,如何有规化的分享工作中的积累,国庆这些天也一直看了很多大神写的博客,最后综合自身的能力及时间,决定先尝试写一个# 动画 #系列文章,动画可能主要包含(CSS/Canvas)两部分,欢迎大家持续关注! 以上就是今天的分享,新手上路中,我会努力让自己变得更优秀、写出更好的文章,文章中有不对之处,烦请各位大神斧正。如果你觉得这篇文章对你有所帮助,请记得点赞哦~。作者:苏南 - 首席填坑官来源:@IT·平头哥联盟链接:https://honeybadger8.github.i…交流群:912594095[资源获取/交流群]、386485473(前端) 、260282062(测试)本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

October 15, 2018 · 1 min · jiezi