联系信息

  • 地址: 广州市天河区岑村圣堂大街8号二层P区0381房
  • 电话: 17727624776
登录 注册
  • 17727624776
  • 广州市天河区岑村圣堂大街8号二层P区0381房
img
  • 2025-09-02
  • 常兴文

移动HTML5前端框架—MUI

  一、 使用该框架之前的准备工作 1. 新建含mui的HTML文件 在Hbuilder中,新建HTML文件,选择”含mui的HTML“模板,可以快速生成mui页面模板,该模板默认处理了mui的js、css资源引用。 2.输入mheader 顶部标题栏是每个页面都必需的内容,在Hbuilder中输入mheader,可以快速生成顶部导航栏。 3.输入mbody 除顶部导航、底部选项卡两个控件之外,其它控件都建议放在.mui-content控件内,在Hbuilder中输入mbody,可快速生成包含.mui-content的代码块。   二、UI组件 1.accordion(折叠面板) 折叠面板和二级列表类似,如下: <ul class="mui-table-view"> <li class="mui-table-view-cell mui-collapse"> <a class="mui-navigate-right" href="#">面板1</a> <div class="mui-collapse-content"> <p>面板1子内容</p> </div> </li> </ul> <ul class="mui-table-view"> <li class="mui-table-view-cell mui-collapse"> <a class="mui-navigate-right" href="#">面板2</a> <div class="mui-collapse-content"> <p>面板2子内容</p> </div> </li> </ul>   2.buttons(按钮) 普通按钮 在button节点上增加.mui-btn类,就可以生成默认按钮;如果需要其他颜色的按钮,则继续增加对应class即可,比如.mui-btn-blue即可变成蓝色按钮 <button type="button" class="mui-btn">默认</button> <button type="button" class="mui-btn mui-btn-primary">蓝色</button> <button type="button" class="mui-btn mui-btn-success">绿色</button> <button type="button" class="mui-btn mui-btn-warning">黄色</button> <button type="button" class="mui-btn mui-btn-danger">红色</button> <button type="button" class="mui-btn mui-btn-royal">紫色</button> 运行之后的效果如下:   若希望无底色、有边框的按钮,仅需增加.mui-btn-outlined类即可,代码如下: <button type="button" class="mui-btn mui-btn-outlined">默认</button> <button type="button" class="mui-btn mui-btn-primary mui-btn-outlined">蓝色</button> <button type="button" class="mui-btn mui-btn-success mui-btn-outlined">绿色</button> <button type="button" class="mui-btn mui-btn-warning mui-btn-outlined">黄色</button> <button type="button" class="mui-btn mui-btn-danger mui-btn-outlined">红色</button> <button type="button" class="mui-btn mui-btn-royal mui-btn-outlined">紫色</button> 运行结果如下:     3.gallery(图片轮播) 图片轮播继承自slide插件,因此其DOM结构、事件均和slide插件相同; 默认不支持循环播放,DOM结构如下: <div class="mui-slider"> <div class="mui-slider-group"> <div class="mui-slider-item"><a href="#"><img src="1.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="2.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="3.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="4.jpg" /></a></div> </div> </div> 假设当前图片轮播中有1、2、3、4四张图片,从第1张图片起,依次向左滑动切换图片,当切换到第4张图片时,继续向左滑动,接下来会有两种效果: 支持循环:左滑,直接切换到第1张图片; 不支持循环:左滑,无反应,继续显示第4张图片,用户若要显示第1张图片,必须连续向右滑动切换到第1张图片; 当显示第1张图片时,继续右滑是否显示第4张图片,是同样问题;这个问题的实现需要通过.mui-slider-loop类及DOM节点来控制;   若要支持循环,则需要在.mui-slider-group节点上增加.mui-slider-loop类,同时需要重复增加2张图片,图片顺序变为:4、1、2、3、4、1,代码示例如下: <div class="mui-slider"> <div class="mui-slider-group mui-slider-loop"> <!--支持循环,需要重复图片节点--> <div class="mui-slider-item mui-slider-item-duplicate"><a href="#"><img src="4.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="1.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="2.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="3.jpg" /></a></div> <div class="mui-slider-item"><a href="#"><img src="4.jpg" /></a></div> <!--支持循环,需要重复图片节点--> <div class="mui-slider-item mui-slider-item-duplicate"><a href="#"><img src="1.jpg" /></a></div> </div> </div> mui框架内置了图片轮播插件,通过该插件封装的JS API,用户可以设定是否自动轮播及轮播周期,如下为代码示例: //获得slider插件对象 var gallery = mui('.mui-slider'); gallery.slider({ interval:3000//自动轮播周期,若为0则不自动播放,默认为0; }); 因此若希望图片轮播不要自动播放,而是用户手动滑动才切换,只需要通过如上方法,将interval参数设为0即可。 若要跳转到第x张图片,则可以使用图片轮播插件的gotoItem方法,例如: //mui自带的on事件绑定,只能用事件委派方式 mui(".mui-content").on("tap","#btn",function(){ gallery.slider().gotoItem(2);//调到第三张图片,index从0开始 }); 4.input(输入表单) 所有包裹在.mui-input-row 类中的 input、textarea等元素都将被默认设置宽度属性为width: 100%; 。 将 label 元素和上述控件控件包裹在.mui-input-group中可以获得最好的排列。 (密码输入框右侧还自带了一个眼睛图标呢,我觉得特别好用)  代码如下: <form class="mui-input-group"> <div class="mui-input-row"> <label>用户名</label> <input type="text" class="mui-input-clear" placeholder="请输入用户名"> </div> <div class="mui-input-row"> <label>密码</label> <input type="password" class="mui-input-password" placeholder="请输入密码"> </div> </form> mui目前还提供了几个输入增强功能:快速删除、语音输入*5+ only和密码框显示隐藏密码。  1)快速删除:只需要在input控件上添加.mui-input-clear类,当input 控件中有内容时,右侧会有一个删除图标,点击会清空当前input的内容 代码如下: <form class="mui-input-group"> <div class="mui-input-row"> <label>快速删除</label> <input type="text" class="mui-input-clear" placeholder="请输入内容"> </div> </form> 2)搜索框:在.mui-input-row同级添加.mui-input-search 类,就可以使用search控件 代码如下: <div class="mui-input-row mui-search"> <input type="search" class="mui-input-clear" placeholder=""> </div> 3)语音输入*5+ only:为了方便快速输入,mui集成了 HTML5+的语音输入,只需要在对应input控件上添加.mui-input-speech 类,就可以在5+环境下使用语音输入   4)密码框:给input元素添加.mui-input-password类即可使用 代码如下: <form class="mui-input-group"> <div class="mui-input-row"> <label>密码框</label> <input type="password" class="mui-input-password" placeholder="请输入密码"> </div> </form> 5.list(列表) mui封装的列表组件是比较简单也很好用的。只需要在ul节点上添加.mui-table-view类、在li节点上添加.mui-table-view-cell类即可 <ul class="mui-table-view"> <li class="mui-table-view-cell">Item 1</li> <li class="mui-table-view-cell">Item 2</li> <li class="mui-table-view-cell">Item 3</li> </ul> 运行结果如下图: 自定义列表高亮颜色 点击列表,对应列表项显示灰色高亮,若想自定义高亮颜色,只需要重写.mui-table-view-cell.mui-active即可,如下:   /*点击变灰色高亮*/ .mui-table-view-cell.mui-active{ background-color: gray; } 右侧添加导航箭头 若右侧需要增加导航箭头,变成导航链接,则只需在li节点下增加a子节点,并为该a节点增加.mui-navigate-right类即可,如下: <ul class="mui-table-view"> <li class="mui-table-view-cell"> <a class="mui-navigate-right">Item 1</a> </li> <li class="mui-table-view-cell"> <a class="mui-navigate-right">Item 2</a> </li> <li class="mui-table-view-cell"> <a class="mui-navigate-right">Item 3</a> </li> </ul> 运行结果如下: 右侧添加数字角标等控件 mui支持将数字角标、按钮、开关等控件放在列表中;mui默认将数字角标放在列表右侧显示,代码如下:   <ul class="mui-table-view"> <li class="mui-table-view-cell">Item 1 <span class="mui-badge mui-badge-primary">1</span> </li> <li class="mui-table-view-cell">Item 2 <span class="mui-badge mui-badge-success">2</span> </li> <li class="mui-table-view-cell">Item 3 <span class="mui-badge">3</span> </li> </ul> 运行结果如下: (图文列表) 图文列表继承自列表组件,主要添加了.mui-media、.mui-media-object、.mui-media-body、.mui-pull-left/right几个类,如下为示例代码 <ul class="mui-table-view"> <li class="mui-table-view-cell mui-media"> <a href="javascript:;"> <img class="mui-media-object mui-pull-left" src="img/a.jpg"> <div class="mui-media-body"> 幸福 <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p> </div> </a> </li> <li class="mui-table-view-cell mui-media"> <a href="javascript:;"> <img class="mui-media-object mui-pull-left" src="img/b.jpg"> <div class="mui-media-body"> 木屋 <p class='mui-ellipsis'>想要这样一间小木屋,夏天挫冰吃瓜,冬天围炉取暖.</p> </div> </a> </li> <li class="mui-table-view-cell mui-media"> <a href="javascript:;"> <img class="mui-media-object mui-pull-left" src="img/c.jpg"> <div class="mui-media-body"> CBD <p class='mui-ellipsis'>烤炉模式的城,到黄昏,如同打翻的调色盘一般.</p> </div> </a> </li> </ul> 运行结果如下:  结束语:mui框架使用起来确实很方便,它还有很多控件供我们使用,具体可在mui官网查看。

查看详情
img
  • 2025-08-30
  • 孟勇捷

HTML meta标签总结与属性使用介绍

之前学习前端中,对meta标签的了解仅仅只是这一句。 <meta charset="UTF-8"> 但是自己却很不熟悉,于是把meta标签加入了寒假学习计划的最前方。 简介 在查阅w3school中,第一句话中的“元数据”就让我开始了Google之旅。然后很顺利的在英文版的w3school找到了想要的结果。(中文w3school说的是元信息,Google和百度都没有相关的词条。但元数据在Google就有详细解释。所以这儿采用英文版W3school的解释。) The <meta> tag provides metadata about the HTML document. Metadata will not be displayed on the page, but will be machine parsable. 不难看出,其中的关键是metadata,中文名叫元数据,是用于描述数据的数据。它不会显示在页面上,但是机器却可以识别。这么一来meta标签的作用方式就很好理解了。 用处 Meta elements are typically used to specify page description, keywords, author of the document, last modified, and other metadata. The metadata can be used by browsers (how to display content or reload page), search engines (keywords), or other web services 这句话对meta标签用处的介绍,简洁明了。翻译过来就是:meta常用于定义页面的说明,关键字,最后修改日期,和其它的元数据。这些元数据将服务于浏览器(如何布局或重载页面),搜索引擎和其它网络服务。 组成 meta标签共有两个属性,分别是http-equiv属性和name属性。 1. name属性 name属性主要用于描述网页,比如网页的关键词,叙述等。与之对应的属性值为content,content中的内容是对name填入类型的具体描述,便于搜索引擎抓取。meta标签中name属性语法格式是: <meta name="参数" content="具体的描述">。 其中name属性共有以下几种参数。(A-C为常用属性) A. keywords(关键字) 说明:用于告诉搜索引擎,你网页的关键字。举例: <meta name="keywords" content="Lxxyx,博客,文科生,前端"> B. description(网站内容的描述) 说明:用于告诉搜索引擎,你网站的主要内容。举例: <meta name="description" content="文科生,热爱前端与编程。目前大二,这是我的前端博客"> C. viewport(移动端的窗口) 说明:这个概念较为复杂,具体的会在下篇博文中讲述。这个属性常用于设计移动端网页。在用bootstrap,AmazeUI等框架时候都有用过viewport。 举例(常用范例): <meta name="viewport" content="width=device-width, initial-scale=1"> D. robots(定义搜索引擎爬虫的索引方式) 说明:robots用来告诉爬虫哪些页面需要索引,哪些页面不需要索引。content的参数有all,none,index,noindex,follow,nofollow。默认是all。 举例: <meta name="robots" content="none"> 具体参数如下: 1.none : 搜索引擎将忽略此网页,等价于noindex,nofollow。2.noindex : 搜索引擎不索引此网页。3.nofollow: 搜索引擎不继续通过此网页的链接索引搜索其它的网页。4.all : 搜索引擎将索引此网页与继续通过此网页的链接索引,等价于index,follow。5.index : 搜索引擎索引此网页。6.follow : 搜索引擎继续通过此网页的链接索引搜索其它的网页。 E. author(作者) 说明:用于标注网页作者举例: <meta name="author" content="Lxxyx,841380530@qq.com"> F. generator(网页制作软件) 说明:用于标明网页是什么软件做的举例: (不知道能不能这样写): <meta name="generator" content="Sublime Text3"> G. copyright(版权) 说明:用于标注版权信息举例: <meta name="copyright" content="Lxxyx"> //代表该网站为Lxxyx个人版权所有。 H. revisit-after(搜索引擎爬虫重访时间) 说明:如果页面不是经常更新,为了减轻搜索引擎爬虫对服务器带来的压力,可以设置一个爬虫的重访时间。如果重访时间过短,爬虫将按它们定义的默认时间来访问。举例: <meta name="revisit-after" content="7 days" > I. renderer(双核浏览器渲染方式) 说明:renderer是为双核浏览器准备的,用于指定双核浏览器默认以何种方式渲染页面。比如说360浏览器。举例: <meta name="renderer" content="webkit"> //默认webkit内核 <meta name="renderer" content="ie-comp"> //默认IE兼容模式 <meta name="renderer" content="ie-stand"> //默认IE标准模式 2. http-equiv属性 介绍之前,先说个小插曲。看文档和博客关于http-equiv的介绍时,有这么一句。 http-equiv顾名思义,相当于http的文件头作用。 一开始看到这句话的时候,我是迷糊的。因为我看各类技术名词,都会习惯性的去记住它的英文全称。equiv的全称是"equivalent",意思是相等,相当于。然后我脑子里出现了大大的迷惑:“HTTP相等?” 后来还准备去Segmentfault提问来着。结果在写问题的时候,突然反应出equivalent还有相当于的意思。意思就是相当于http的作用。至于文件头是哪儿出来的,估计是从其作用来分析的。我认为顾名思义并不能得出"相当于http的文件头作用"这个论断。 这个我所认为的http-equiv意思的简介。相当于HTTP的作用,比如说定义些HTTP参数啥的。 meta标签中http-equiv属性语法格式是: <meta http-equiv="参数" content="具体的描述"> 其中http-equiv属性主要有以下几种参数: A. content-Type(设定网页字符集)(推荐使用HTML5的方式) 说明:用于设定网页字符集,便于浏览器解析与渲染页面举例: <meta http-equiv="content-Type" content="text/html;charset=utf-8"> //旧的HTML,不推荐 <meta charset="utf-8"> //HTML5设定网页字符集的方式,推荐使用UTF-8 B. X-UA-Compatible(浏览器采取何种版本渲染当前页面) 说明:用于告知浏览器以何种版本来渲染页面。(一般都设置为最新模式,在各大框架中这个设置也很常见。)举例: <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> //指定IE和Chrome使用最新版本渲染当前页面 C. cache-control(指定请求和响应遵循的缓存机制) 用法1. 说明:指导浏览器如何缓存某个响应以及缓存多长时间。这一段内容我在网上找了很久,但都没有找到满意的。最后终于在Google Developers中发现了我想要的答案。 举例: <meta http-equiv="cache-control" content="no-cache"> 共有以下几种用法: no-cache: 先发送请求,与服务器确认该资源是否被更改,如果未被更改,则使用缓存。 no-store: 不允许缓存,每次都要去服务器上,下载完整的响应。(安全措施) public : 缓存所有响应,但并非必须。因为max-age也可以做到相同效果 private : 只为单个用户缓存,因此不允许任何中继进行缓存。(比如说CDN就不允许缓存private的响应) maxage : 表示当前请求开始,该响应在多久内能被缓存和重用,而不去服务器重新请求。例如:max-age=60表示响应可以再缓存和重用 60 秒。 用法2.(禁止百度自动转码) 说明:用于禁止当前页面在移动端浏览时,被百度自动转码。虽然百度的本意是好的,但是转码效果很多时候却不尽人意。所以可以在head中加入例子中的那句话,就可以避免百度自动转码了。举例: <meta http-equiv="Cache-Control" content="no-siteapp" /> D. expires(网页到期时间) 说明:用于设定网页的到期时间,过期后网页必须到服务器上重新传输。举例: <meta http-equiv="expires" content="Sunday 26 October 2016 01:00 GMT" /> E. refresh(自动刷新并指向某页面) 说明:网页将在设定的时间内,自动刷新并调向设定的网址。举例: <meta http-equiv="refresh" content="2;URL=http://www.lxxyx.win/"> //意思是2秒后跳转向我的博客 F. Set-Cookie(cookie设定) 说明:如果网页过期。那么这个网页存在本地的cookies也会被自动删除。 <meta http-equiv="Set-Cookie" content="name, date"> //格式 <meta http-equiv="Set-Cookie" content="User=Lxxyx; path=/; expires=Sunday, 10-Jan-16 10:00:00 GMT"> //具体范例 最后 暂时总结的就这么多了,meta标签的自定义属性实在太多了。所以只去找了常用的一些,还有像Window-target这样已经基本被废弃的属性,我也没有添加。一开始以为一两个小时就能学习完毕,结果没想到竟然花了五六个小时,各处查资料,推敲文字。敲击文字的时候,也感觉自己学习了非常多。比如基本的SEO,HTTP协议的再次理解等。 因为经验尚浅,所以如果有出错的地方,希望各位能帮忙指正。最后附上本人博客地址和原文链接,希望能与各位多多交流。

查看详情
img
  • 2025-09-02
  • 彭文光

HTML table、form表单标签的介绍

1. <table> 标签 1.1 说明 在HTML 中定义表格布局。 1.2格式 <table>   <caption></caption>   <tr> <th></th></tr>   <tbody>     <tr><td></td></tr>     <tr><td></td><tr>   <tbody> </table>   1.3 包含的元素 <caption></caption>:表头信息。 <tr></tr> :定义一个表格行; <th></th> :定义一个表格头;若是纯文字,默认会以粗体的样式表现。 <tbody></tbody> :可以理解为表格的内容区域,在Chrome、FF浏览器通过DOM进行表格动态插入行的时候,要使用这个。如果不进行DOM操作,可不用添加。 <td></td> :定义一个单元格;   1.4 属性 table 属性:   border :定义表格的边框宽度,默认为0,即无边框。   title :表格的提示信息,当鼠标移到表格上方时,所提示的信息。 th、td 属性:   colspan : 表示横向合并单元格 ( )   rowspan :表示纵向合并单元格 (  )   1.5 示例 <table border=0 title="测试"> <caption> 表格标题</caption> <tr> <th>姓名</th> <th>年龄</th> </tr> <tr> <td>张三</td> <td>22</td> </tr> <tr> <td><input type=text /></td> <td><input type=text /></td> </tr> </table>   2. <form> 标签 2.1 说明 <form> 标签用于创建 HTML 表单。 表单能够包含 input 元素,比如文本字段、复选框、单选框、提交按钮等等。 表单还可以包含 menus、textarea、fieldset和 label 元素 等。   2.2 属性 action {URL}:一个URL地址;指定form表单向何处发送数据。 enctype {string}:规定在发送表单数据之前,如何对表单数据进行编码。 指定的值有:application/x-www-form-urlencoded :在发送前编码所有字符(默认为此方式);       multipart/form-data :不对字符编码。使用包含文件上传控件的表单时,必须使用该值 method {get/post}:指定表单以何种方式发送到指定的页面。 指定的值有:get :form表单里所填的值,附加在action指定的URL后面,做为URL链接而传递。       post :form表单里所填的值,附加在HTML Headers上。   2.3 示例   <form enctype="multipart/form-data" action="ashx/login.ashx" method="post"> <table> <tr> <td><label for="txtname">账号:</label></td> <td><input type="text" id="txtname" name="login_username" /></td> </tr> <tr> <td><label for="txtpswd">密码:</label></td> <td><input type="password" id="txtpswd" name="login_pswd" /></td> </tr> <tr> <td colspan=2> <input type="reset" /> <input type="submit" /> </td> </tr> </table> </form>      2.4 应用场景 表单主要用于向服务器传输数据;如常见的登录、注册页面。   3. form 表单提交方式 3.1 get 方式 3.1.1 说明 form表单里所填的值,附加在action指定的URL后面,做为URL链接而传递。 3.1.2 示例 在上面的form代码中输入如下: 账号:admin  密码:123456 点击提交后:URL变为: http://localhost:4778/ashx/login.ashx?login_username=admin&login_pswd=123456 变量提交的样式为:html元素的name属性=提交的值。多个变量,以 & 符号隔开。   3.2 post 方式 3.2.1 说明 form表单里所填的值,附加在HTML Headers上。 3.2.2 示例 同上面get方式一样。 账号:admin  密码:123456 点击提交后:URL变为 http://localhost:4778/ashx/login.ashx 可看到只是action指定的URL,参数并没有附加在URL后面。 通过Fiddler软件,可以查看到HTML Header区域:有个名为WebKitFormBoundary2T7xmZEtMRQeAhNh 的对象  查看【Raw】区域,可看见里面包含了提交的变量   3.3 get 与 post 的区别   ①数据的查询:比如浏览论坛时,URL一般包含了分类、页码数、每页记录数等信息。 get 方式,能一目了然的看到所要查询的信息(条件)。 post 因为隐藏掉了这些信息,不方便进行检验查询条件。   ②敏感数据的提交(安全性):对一项记录,进行更改、添加操作时,比如注册用户、更改用户资料等。get 方式附加在URL上,会泄露掉敏感的消息。 post 方式,能隐藏掉敏感的信息。   ③大数据文本传递:get 虽然方便查询,但由于是附加在URL上,各浏览器对URL也有个长度限制。IE :2048字符。Chrome、FF 好像是 8182字符。post 好像没此限制。  

查看详情
img
  • 2025-08-30
  • 邹曾琪

CSS奇思妙想 -- 使用 CSS 创造艺术

中心布局 本文的所有技巧都会围绕这个布局展开,属于一类技巧。 首先,我们需要这样一种中心布局。简单的 HTML 结构如下: ?1234567<div class="g-container">    <div class="g-box"></div>    <div class="g-box"></div>    <div class="g-box"></div>    <div class="g-box"></div>    <div class="g-box"></div></div> ?123456789101112131415.g-container {    position: relative;    width: 300px;    height: 300px;}.g-box {    position: absolute;    top:50%;    left: 50%;    margin-left: -150px;    margin-top: -150px;    width: 100%;    height: 100%;    border: 1px solid #000;} 利用绝对定位和 margin,将元素全部水平垂直居中叠在一起(因为后面会用到 transform,所以选取了这种水平垂直居中的方式),结果如下: 好吧,看着平平无奇,但是基于这种布局,我们可以衍生出非常多有意思的图案。   改变元素大小 最简单的,就是我们可以改变元素的大小。 CSS 代码写着太累,所以我们简单的借助 pug HTML 模板引擎和 SASS。 ?123div.g-container    -for(var i=0; i<10; i++)        div.g-box ?12345678910$count: 10;@for $i from 1 to $count + 1 {    .g-box:nth-child(#{$i}) {        --width: #{$i * 30}px;        width: var(--width);        height: var(--width);        margin-left: calc(var(--width) / -2);        margin-top: calc(var(--width) / -2);    }} 容器下包含 10 个子元素,每个子元素大小逐渐递增,很容易得到如下结果:   改变元素颜色 接着,我们继续改变元素的颜色,让它呈现渐变颜色逐级递进,可以是边框颜色: ?1234567891011@for $i from 1 to $count + 1 {    .g-box:nth-child(#{$i}) {         ...         border-color: hsla(            calc(#{$i * 25}),            50%,            65%,            1        );    }} 得到这样的效果: 也可以是改变背景 background 的颜色: ?123456789101112@for $i from 1 to $count + 1{    .g-box:nth-child(#{$i}) {        ...        background: hsla(            calc(#{$i * 25}),            50%,            65%,            1        );        z-index: #{$count - $i};    }}   改变元素角度 好,接下来,就可以开始变换角度了,我们利用 transform,将元素旋转不同的角度: ?123456@for $i from 1 to $count + 1{    .g-box:nth-child(#{$i}) {        ....        transform: rotate(#{$i * 7}deg);    }} 效果如下: CodePen Demo -- CSS Pattern OK,到这里,基本的一些概念就引入的差不多了,总而言之,利用多元素居中布局,改变元素的大小、颜色、透明度、角度、阴影、滤镜、混合模式等等等等,只要你能想到的,都可以。 接下来,我们再引入本文的另外一个主角 -- CSS-doodle 。 CSS-doodle 是一个基于 Web-Component 的库。允许我们快速的创建基于 CSS Grid 布局的页面,以实现各种 CSS 效果(或许可以称之为 CSS 艺术)。 其最终效果的代码,本质是都还是 CSS。具体的一些概念可以点击主页看看,一看就懂。   使用 CSS-doole 实现多元素水平垂直居中布局 我们将上面的布局利用 CSS-doodle 再实现一次,要实现 50个元素的居中对齐,只需要如下简单的声明即可: ?123456<css-doodle>    :doodle {        @grid: 1x50 / 100vmin;    }    @place-cell: center;</css-doodle> 上面的意思大概是,在 100vmin x 100vmin 大小的容器下,声明一个 1x50 的 grid 网格布局,利用 @place-cell: center 将它们全部水平垂直居中,也就是会叠加在一起。 这样可能看不出效果,我们再给每个元素设置不同的大小,给它们都加上一个简单的 border: ?12345678<css-doodle>    :doodle {        @grid: 1x50 / 100vmin;    }    @place-cell: center;    @size: calc(100% - @calc(@index() - 1) * 2%);    border: 1px solid #000;</css-doodle> @size: calc(100% - @calc(@index() - 1) * 2%) 表示每个子元素宽高的大小(也可以单独设置高宽),@index 是个变量,表示当前元素的序号,从 1 - 50,表示没个元素分别为容器的 2% 高宽、4% 高宽一直到 100% 高宽。 border: 1px solid #000 就是正常的 CSS 代码,里面没有变量,作用于每一个元素 效果如下: Oh No,眼睛开始花了。这样,我们就快速的实现了前面铺垫时候利用 HTML 代码和繁琐的 CSS 生成的图形效果。   CSS 艺术 接下来,就开始美妙的 CSS 艺术。   改变元素的旋转角度及边框颜色 我们利用上述代码继续往下,为了更好的展示效果,首先整体容器的底色改为黑色,接着改变元素的旋转角度。每个元素旋转 30deg x @index。 代码非常的短,大概是这样: ?123456789<css-doodle>    :doodle {        @grid: 1x100 / 100vmin;    }    @place-cell: center;    @size: calc(100% - @calc(@index() - 1) * 1%);    transform: rotate(calc(@index() * 30deg));    border: 1px solid #fff;</css-doodle> 不太好看,接着,我们试着给每个元素,渐进的设置不同的 border 颜色,并且透明度 opacity 逐渐降低,,这里我们会用到 hsla 颜色表示法: ?1234567891011121314<css-doodle>    :doodle {        @grid: 1x100 / 100vmin;    }    @place-cell: center;    @size: calc(100% - @calc(@index() - 1) * 1%);    transform: rotate(calc(@index() * 30deg));    border: 1px solid hsla(        calc(calc(100 - @index()) * 2.55),         calc(@index() * 1%),         50%,        calc(@index() / 100)    );</css-doodle> 再看看效果: Wow,第一幅看上去还不错的作品出现了。 当然,每一个不同的角度,都能产生不一样的效果,通过 CSS-doodle,可以快速生成不同随机值,随机产生不同的效果。我们稍微改变一下上述代码,将 transform 那一行改一下,引入了一个随机值: ?123456<css-doodle>    :doodle {        --rotate: @r(0, 360)deg;    }    transform: rotate(calc(@index() * var(--rotate)));</css-doodle> 利用 @r(0, 360)deg,能随机生成一个介于 0 到 360 之间的随机数,后面可以直接跟上单位,也就变成了一个随机角度值 transform: rotate(calc(@index() * var(--rotate))),利用 calc 规则引入随机生成的 CSS 变量,当然,再不刷新页面的前提下,每一次这个值都是固定的 这样,我们每次刷新页面,就可以得到不同的效果了(当然,CSS-doodle 做了优化,添加短短几行代码就可以通过点击页面刷新效果),改造后的效果,我们每次点击都可以得到一个新的效果: CodePen Demo -- CSS Doodle - CSS Magic Pattern 强烈建议你点进 Demo,自己点点鼠标感受一下 :)   background 颜色奇偶不同 好,我们再换个思路,这次不改变 border 的颜色,而是通过选择器控制奇数序号的元素和偶数序号的元素,分别给予它们不一样的背景色: ?12345678910111213<css-doodle>    :doodle {        @grid: 1x100 / 100vmin;    }    @place-cell: center;    @size: calc(100% - @calc(@index() - 1) * 1%);    transform: rotate(calc(@index() * 60deg));     background: rgba(0, 0, 0, calc((@index * 0.01)));    @even {        background: rgba(255, 255, 255, calc((@index * 0.01)));    }</css-doodle> 利用 @even {} 可以快速选中偶数序号的元素,然后给他赋予白色底色,而奇数元素则赋予黑色底色,看看效果: 还是一样的思路,我们可以将随机值赋予 transform 的旋转角度,利用黑白叠加,看看再不同角度下,都会有什么效果: CodePen Demo -- CSS Doodle - CSS Magic Pattern 当然,在随机的过程中,你也可以选取自己喜欢的,将它们保留下来。 CSS-doodle 支持多种方式的引入,在一页中展示多个图形,不在话下,像是这样: CodePen Demo -- CSS-doodle Pure CSS Pattern   规律总结 小小总结一下,想要生成不同的图案,其实只需要找到能够生成不同线条,或者造型图案图形,将它们按照不同的大小,不同的旋转角度,不同颜色及透明度叠加在一起即可。 这样的话,一些可能的 idea: 只利用单向的 border 会是怎么样的呢? 出现的 border 都是 solid,如果换成是虚线 dashed 呢?或许可以再加上 border-radius text-decoration 也支持一些各式的下划线,我们也可以利用它们试试 OK,将上述想法付诸实践,我们就可以得到利用各式线条绘制出来的各式图形。它们可能是这样: 当然,每次的效果都可以做到随机,只要我们合理利用好随机的参数即可,你可以戳进下面的 Demo 感受一下: CodePen Demo -- CSS-doodle Pure CSS Pattern   Clip-path 与 drop-shadow 嘿,说到创造不同的线条与图案,就不得不提 CSS 里另外两个有意思是属性。Clip-path 与 fitler: drop-shadow()。 嗯哼?什么意思呢。我们来个简单的 Demo,利用 Clip-path ,我们可以裁剪出不同的元素造型。譬如实现一个简单的多边形: ?123456div {    width: 300px;    height: 300px;    clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 0% 60%, 10% 20%);    background: #333;} 效果如下: 那么利用这个思路,我们就可以尝试利用 clip-path 裁剪出各种不同的造型进行叠加。 在 CSS-doodle Shapes 中,内置了非常多的 clip-path 图形供我们选择: 我们随机选取一个: 套用上述的规则,尝试着实现一个图形: ?1234567891011121314151617181920<css-doodle>    :doodle {        @grid: 1x100 / 100vmin;    }    @place-cell: center;    @size: calc(100% - @calc(@index() - 1) * 1%);    background: hsla(        calc(calc(100 - @index()) * 2.55),         calc(@index() * 1%),         65%,        calc(@index() / 100)    );    clip-path: @shape(        fill-rule: evenodd;        split: 200;        scale: .45;        x: cos(2t) + cos(π - 5t);        y: sin(2t) + sin(π - 5t);    );</css-doodle> 这次没有旋转不同的角度,只是给每一层赋予不同的背景底色,能够得到这样的效果: CodePen Demo -- CSS Doodle - CSS Magic Pattern   Clip-path 与 drop-shadow 创造不同线条 OK,上述是利用 Clip-path 创造了不同的图案,那不同的线条怎么得来呢? 别急。这就需要请出我们另外一个属性 drop-shadow,利用 drop-shadow,可以给 Clip-path 裁剪出来的图形创造不同的阴影,当然有一些结构上的限制,大概的伪代码如下: ?1234567891011121314151617div {    position: relative;    width: 300px;    height: 300px;    filter: drop-shadow(0px 0px 1px black);     &::after {        content: "";        position: absolute;        width: 100%;        height: 100%;        left: 0;        right: 0;        background: #fff;        clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 0% 60%, 10% 20%);    }} 我们需要将 filter: drop-shadow(0px 0px 2px black) 作用在利用了 clip-path 的元素的父元素之上,并且,利用了 clip-path: 的元素必须带有 background,才能给裁剪元素附上阴影效果。 上述的代码如下: OK,完美,这样一来,我们就极大极大的丰富了我们的线条库,再运用会上述的线条规则,一大波新的图案应运而生。 CodePen Demo -- CSS-doodle Pure CSS Pattern - clip-path - drop-shadow OK,限于篇幅,就不一一展开了,感兴趣可以点进上述 Demo Fork 一份自己尝试。还有非常多有意思的图案等待挖掘生成。 最后,再来欣赏一下 CSS-doodle 作者,袁川袁老师利用上述技巧的作品: CodePen Demo -- css doodle art

查看详情
img
  • 2025-08-30
  • 梁嘉颖

奇妙的 CSS MASK

本文将介绍 CSS 中一个非常有意思的属性 mask 。 顾名思义,mask 译为遮罩。在 CSS 中,mask 属性允许使用者通过遮罩或者裁切特定区域的图片的方式来隐藏一个元素的部分或者全部可见区域。 其实 mask 的出现已经有一段时间了,只是没有特别多实用的场景,在实战中使用的非常少,本文将罗列一些使用 mask 创造出来的有意思的场景。 语法 最基本,使用 mask 的方式是借助图片,类似这样: ?12345{    /* Image values */    mask: url(mask.png);                       /* 使用位图来做遮罩 */    mask: url(masks.svg#star);                 /* 使用 SVG 图形中的形状来做遮罩 */} 当然,使用图片的方式后文会再讲。借助图片的方式其实比较繁琐,因为我们首先还得准备相应的图片素材,除了图片,mask 还可以接受一个类似 background 的参数,也就是渐变。 类似如下使用方法: ?123{    mask: linear-gradient(#000, transparent)   /* 使用渐变来做遮罩 */} 那该具体怎么使用呢?一个非常简单的例子,上述我们创造了一个从黑色到透明渐变色,我们将它运用到实际中,代码类似这样: 下面这样一张图片,叠加上一个从透明到黑色的渐变, ?1234{    background: url(image.png) ;    mask: linear-gradient(90deg, transparent, #fff);} 应用了 mask 之后,就会变成这样: 这个 DEMO,可以先简单了解到 mask 的基本用法。 这里得到了使用 mask 最重要结论:图片与 mask 生成的渐变的 transparent 的重叠部分,将会变得透明。 值得注意的是,上面的渐变使用的是 linear-gradient(90deg, transparent, #fff),这里的 #fff 纯色部分其实换成任意颜色都可以,不影响效果。 CodePen Demo -- 使用 MASK 的基本使用   使用 MASK 进行图片裁切 利用上述简单的运用,我们可以使用 mask 实现简单的图片裁剪。 使用 mask 实现图片切角遮罩 使用线性渐变,我们实现一个简单的切角图形: ?123456789101112131415.notching{    width: 200px;    height: 120px;    background:    linear-gradient(135deg, transparent 15px, deeppink 0)    top left,    linear-gradient(-135deg, transparent 15px, deeppink 0)    top right,    linear-gradient(-45deg, transparent 15px, deeppink 0)    bottom right,    linear-gradient(45deg, transparent 15px, deeppink 0)    bottom left;    background-size: 50% 50%;    background-repeat: no-repeat;} 像是这样: 我们将上述渐变运用到 mask 之上,而 background 替换成一张图片,就可以得到运用了切角效果的图片: ?123456789101112background: url(image.png);mask:    linear-gradient(135deg, transparent 15px, #fff 0)    top left,    linear-gradient(-135deg, transparent 15px, #fff 0)    top right,    linear-gradient(-45deg, transparent 15px, #fff 0)    bottom right,    linear-gradient(45deg, transparent 15px, #fff 0)    bottom left;mask-size: 50% 50%;mask-repeat: no-repeat; 得到的效果如下: CodePen Demo -- 使用 MASK 实现图片切角遮罩 当然,实现上述效果还有其他很多种方式,譬如 clip-path,这里的 mask 也是一种方式。   多张图片下使用 mask 上述是单张图片使用 mask 的效果。下面我们看看多张图片下,使用 mask 能碰撞出什么样的火花。 假设我们有两张图片,使用 mask,可以很好将他们叠加在一起进行展示。最常见的一个用法: ?123456789101112div {    position: relative;    background: url(image1.jpg);     &::before {        position: absolute;        content: "";        top: 0;left: 0; right: 0;bottom: 0;        background: url(image2.jpg);        mask: linear-gradient(45deg, #000 50%, transparent 50%);    }} 两张图片,一张完全重叠在另外一张之上,然后使用 mask: linear-gradient(45deg, #000 50%, transparent 50%) 分割两张图片: CodePen Demo -- MASK 的基本使用,多张图片下的基本用法 当然,注意上面我们使用的 mask 的渐变,是完全的实色变化,没有过度效果。 我们稍微修改一下 mask 内的渐变: ?1234{- mask: linear-gradient(45deg, #000 50%, transparent 50%)+ mask: linear-gradient(45deg, #000 40%, transparent 60%)} 即可得到图片1向图片2过渡切换的效果: CodePen Demo -- MASK 的基本使用,多张图片下的基本用法2   使用 MASK 进行转场动画 有了上面的铺垫。运用上面的介绍的一些方法,我们就可以使用 mask 来进行一些图片切换间的转场动画。   使用线性渐变 mask:linear-gradient() 进行切换 还是上面的 Demo,我们通过动态的去改变 mask 的值来实现图片的显示/转场效果。 代码可能是这样: ?1234div {    background: url(image1.jpg);    animation: maskMove 2s linear;} ?123456789101112@keyframes {  0% {    mask: linear-gradient(45deg, #000 0%, transparent 5%, transparent 5%);  }  1% {    mask: linear-gradient(45deg, #000 1%, transparent 6%, transparent 6%);  }  ...  100% {    mask: linear-gradient(45deg, #000 100%, transparent 105%, transparent 105%);  }} 当然,像上面那样一个一个写,会比较费力,通常我们会借助 SASS/LESS 等预处理器进行操作。像是这样: ?1234567891011121314151617181920div {    position: relative;    background: url(image2.jpg) no-repeat;     &::before {        position: absolute;        content: "";        top: 0;left: 0; right: 0;bottom: 0;        background: url(image1.jpg);        animation: maskRotate 1.2s ease-in-out;    }} @keyframes maskRotate {    @for $i from 0 through 100 {         #{$i}% {            mask: linear-gradient(45deg, #000 #{$i + '%'}, transparent #{$i + 5 + '%'}, transparent 1%);        }    }} 可以得到下面这样的效果(单张图片的显隐及两张图片下的切换): CodePen Demo -- MASK linear-gradient 转场   使用角向渐变 mask: conic-gradient() 进行切换 当然,除了 mask: linear-gradient(),使用径向渐变或者角向渐变也都是可以的。使用角向渐变的原理也是一样的: ?1234567@keyframes maskRotate {    @for $i from 0 through 100 {         #{$i}% {            mask: conic-gradient(#000 #{$i - 10 + '%'}, transparent #{$i + '%'}, transparent);        }    }} 可以实现图片的角向渐显/切换: CodePen Demo -- MASK conic-gradient 转场 这个技巧,在张鑫旭的这篇文章里,有更多丰富的例子,可以移步阅读: 你用的那些CSS转场动画可以换一换了 运用这个技巧,我们就可以实现很多有意思的图片效果。像是这样:   mask 碰撞滤镜与混合模式 继续下一环节。CSS 中很多有意思的属性,和滤镜和混合模式一结合,会碰撞出更多火花。   mask & 滤镜 filter: contrast() 首先,我们利用多重径向渐变,实现这样一张图。 ?1234{  background: radial-gradient(#000, transparent);  background-size: 20px 20px;} 看着没什么特别,我们利用 filter: contrast() 对比度滤镜,改造一下。代码大概是这样: ?1234567891011121314151617181920html,body {    width: 100%;    height: 100%;    filter: contrast(5);} div {    position: relative;    width: 100%;    height: 100%;    background: #fff;         &::before {        content: "";        position: absolute;        top: 0; right: 0; bottom: 0; left: 0;        background: radial-gradient(#000, transparent);        background-size: 20px 20px;    }}  即可得到这样的图形,利用对比度滤镜,将图形变得非常的锐化。 这个时候,我们再叠加上不同的 mask 遮罩。即可得到各种有意思的图形效果。 ?1234567891011121314body {    filter: contrast(5);} div {    position: relative;    background: #fff;         &::before {        background: radial-gradient(#000, transparent);        background-size: 20px 20px;      + mask: linear-gradient(-180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, .5));    }} CodePen Demo -- 使用 mask 搭配滤镜 contrast 我们叠加了一个线性渐变的 mask linear-gradient(-180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, .5)),注意,两个渐变颜色都是带透明度的。 或者换一个径向渐变: ?123{    mask: repeating-radial-gradient(circle at 35% 65%, #000, rgba(0, 0, 0, .5), #000 25%);} CodePen Demo -- 使用 mask 搭配滤镜 contrast 好的,下一步,与上文类似,我们添加上动画。 ?123456789101112131415161718div {    ...         &::before {        background: radial-gradient(#000, transparent);        background-size: 20px 20px;        mask: repeating-radial-gradient(circle at 35% 65%, #000, rgba(0, 0, 0, .5), #000 25%);        animation: maskMove 15s infinite linear;    }} @keyframes maskMove {    @for $i from 0 through 100 {         #{$i}% {            mask: repeating-radial-gradient(circle at 35% 65%, #000, rgba(0, 0, 0, .5), #000 #{$i + 10 +  '%'});        }    }} 看看,可以得到了非常酷炫的动画效果: CodePen Demo -- 使用 mask 搭配滤镜 contrast 及动画 还记得使用 filter: hue-rotate() 色相滤镜吗。再加上它,我们可以让颜色也变化起来。 CodePen Demo -- 使用 mask 搭配滤镜 contrast 及动画2   mask & 滤镜 filter: contrast() & 混合模式 接下来我们再叠加上混合模式。 注意到上面,其实我们的容器背景色是白色 #fff。 我们可以通过多嵌套一层层级,再增加一个容器背景色,再叠加上混合模式,产生不一样的效果。 先不添加使用 mask,重新构造一下结构,最终的伪代码带个是这样: ?123<div class="wrap">    <div class="inner"></div></div> ?1234567891011121314151617181920.wrap {    position: relative;    height: 100%;    background: linear-gradient(45deg, #f44336, #ff9800, #ffeb3b, #8bc34a, #00bcd4, #673ab7);} .inner {    height: 100%;    background: #000;    filter: contrast(700%);    mix-blend-mode: multiply;         &::before {        content: "";        position: absolute;        top: 0; right: 0; bottom: 0; left: 0;        background: radial-gradient(#fff, transparent);        background-size: 12px 12px;    }} 原理示例图如下: 我们就可以得到如下的效果: OK,到这一步,mask 还没有运用上,我们再添加上 mask。 ?123456789101112131415.wrap {    background: linear-gradient(45deg, #f44336, #ff9800, #ffeb3b, #8bc34a, #00bcd4, #673ab7);} .inner {    ...    filter: contrast(700%);    mix-blend-mode: multiply;         &::before {        background: radial-gradient(#fff, transparent);        background-size: 12px 12px;      + mask: linear-gradient(#000, rgba(0, 0, 0, .5));    }} CodePen Demo -- mask & filter & blend-mode 实际效果比截图好很多,可以点击 Demo 去看看。 当然,这里叠加的是 mix-blend-mode: multiply ,可以尝试其他混合模式,得到其他不一样的效果。 譬如,叠加 mix-blend-mode: difference,等等等等: 更多有意思的叠加,感兴趣的同学需要自己多加尝试。   mask 与图片 当然,mask 最本质的作用应该还是作用于图片。上面得到的重要结论: 图片与 mask 生成的渐变的 transparent 的重叠部分,将会变得透明。 也可以作用于 mask 属性传入的图片。也就是说,mask 是可以传入图片素材的,并且遵循 background-image 与 mask 图片的透明重叠部分,将会变得透明。 运用这个技巧,可以制作非常酷炫的转场动画: 这里其实主要是在 mask 中运用了这样一张图片: 然后,使用了逐帧动画,快速切换每一帧的 mask : ?12345678910111213141516171819202122.img1 {    background: url(image1.jpg) no-repeat left top;} .img2 {    mask: url(https://i.imgur.com/AYJuRke.png);    mask-size: 3000% 100%;    animation: maskMove 2s steps(29) infinite;} .img2::before {    background: url(image2.jpg) no-repeat left top;} @keyframes maskMove {    from {        mask-position: 0 0;    }    to {        mask-position: 100% 0;    }} CodePen Demo -- mask 制作转场动画 当然,这个也是可以加上各种动画的。上面已经演示了很多次了,感兴趣的同学可以自己尝试尝试。   最后 说了这么多,mask 其实还是属于一个比较冷门的属性。在日常业务中能运用上的机会不多。 而且兼容性不算特别好,打开 MDN,可以看到,除了 mask 本身,还有很多与 mask 相关的属性,只是目前大部分还属于实验室阶段。

查看详情
img
  • 2025-09-03
  • 乔乐康

深入浅出 CSS 动画

本文将比较全面细致的梳理一下 CSS 动画的方方面面,针对每个属性用法的讲解及进阶用法的示意,希望能成为一个比较好的从入门到进阶的教程。 CSS 动画介绍及语法 首先,我们来简单介绍一下 CSS 动画。 最新版本的 CSS 动画由规范 -- CSS Animations Level 1 定义。 CSS 动画用于实现元素从一个 CSS 样式配置转换到另一个 CSS 样式配置。 动画包括两个部分: 描述动画的样式规则和用于指定动画开始、结束以及中间点样式的关键帧。 简单来说,看下面的例子: div { animation: change 3s; } @keyframes change { 0% { color: #f00; } 100% { color: #000; } } animation: move 1s 部分就是动画的第一部分,用于描述动画的各个规则; @keyframes move {} 部分就是动画的第二部分,用于指定动画开始、结束以及中间点样式的关键帧; 一个 CSS 动画一定要由上述两部分组成。 CSS 动画的语法 接下来,我们简单看看 CSS 动画的语法。 创建动画序列,需要使用 animation 属性或其子属性,该属性允许配置动画时间、时长以及其他动画细节,但该属性不能配置动画的实际表现,动画的实际表现是由 @keyframes 规则实现。 animation 的子属性有: animation-name:指定由 @keyframes 描述的关键帧名称。 animation-duration:设置动画一个周期的时长。 animation-delay:设置延时,即从元素加载完成之后到动画序列开始执行的这段时间。 animation-direction:设置动画在每次运行完后是反向运行还是重新回到开始位置重复运行。 animation-iteration-count:设置动画重复次数, 可以指定 infinite 无限次重复动画 animation-play-state:允许暂停和恢复动画。 animation-timing-function:设置动画速度, 即通过建立加速度曲线,设置动画在关键帧之间是如何变化。 animation-fill-mode:指定动画执行前后如何为目标元素应用样式 @keyframes 规则,当然,一个动画想要运行,还应该包括 @keyframes 规则,在内部设定动画关键帧 其中,对于一个动画: 必须项:animation-name、animation-duration 和 @keyframes规则 非必须项:animation-delay、animation-direction、animation-iteration-count、animation-play-state、animation-timing-function、animation-fill-mode,当然不是说它们不重要,只是不设置时,它们都有默认值 上面已经给了一个简单的 DEMO, 就用上述的 DEMO,看看结果: 这就是一个最基本的 CSS 动画,本文将从 animation 的各个子属性入手,探究 CSS 动画的方方面面。 animation-name / animation-duration 详解 整体而言,单个的 animation-name 和 animation-duration 没有太多的技巧,非常好理解,放在一起。 首先介绍一下 animation-name,通过 animation-name,CSS 引擎将会找到对应的 @keyframes 规则。 当然,它和 CSS 规则命名一样,也存在一些骚操作。譬如,他是支持 emoji 表情的,所以代码中的 animation-name 命名也可以这样写: div { animation: ✅ 3s; } @keyframes ✅ { 0% { color: #f00; } 100% { color: #000; } } 而 animation-duration 设置动画一个周期的时长,上述 DEMO 中,就是设定动画整体持续 3s,这个也非常好理解。 animation-delay 详解 animation-delay 就比较有意思了,它可以设置动画延时,即从元素加载完成之后到动画序列开始执行的这段时间。 简单的一个 DEMO: <div></div> <div></div> div { width: 100px; height: 100px; background: #000; animation-name: move; animation-duration: 2s; } div:nth-child(2) { animation-delay: 1s; } @keyframes move { 0% { transform: translate(0); } 100% { transform: translate(200px); } } 比较下列两个动画,一个添加了 animation-delay,一个没有,非常直观: 上述第二个 div,关于 animation 属性,也可以简写为 animation: move 2s 1s,第一个时间值表示持续时间,第二个时间值表示延迟时间。 animation-delay 可以为负值 关于 animation-delay,最有意思的技巧在于,它可以是负数。也就是说,虽然属性名是动画延迟时间,但是运用了负数之后,动画可以提前进行。 假设我们要实现这样一个 loading 动画效果: 有几种思路: 初始 3 个球的位置就是间隔 120°,同时开始旋转,但是这样代码量会稍微多一点 另外一种思路,同一个动画,3 个元素的其中两个延迟整个动画的 1/3,2/3 时间出发 方案 2 的核心伪代码如下: .item:nth-child(1) { animation: rotate 3s infinite linear; } .item:nth-child(2) { animation: rotate 3s infinite 1s linear; } .item:nth-child(3) { animation: rotate 3s infinite 2s linear; } 但是,在动画的前 2s,另外两个元素是不会动的,只有 2s 过后,整个动画才是我们想要的: 此时,我们可以让第 2、3 个元素的延迟时间,改为负值,这样可以让动画延迟进行 -1s、-2s,也就是提前进行 1s、2s: .item:nth-child(1) { animation: rotate 3s infinite linear; } .item:nth-child(2) { animation: rotate 3s infinite -1s linear; } .item:nth-child(3) { animation: rotate 3s infinite -2s linear; } 这样,每个元素都无需等待,直接就是运动状态中的,并且元素间隔位置是我们想要的结果: 利用 animation-duration 和 animation-delay 构建随机效果 还有一个有意思的小技巧。 同一个动画,我们利用一定范围内随机的 animation-duration 和一定范围内随机的 animation-delay,可以有效的构建更为随机的动画效果,让动画更加的自然。 我在下述两个纯 CSS 动画中,都使用了这样的技巧: 纯 CSS 实现华为充电动画: 纯 CSS 实现火焰动画: 以纯 CSS 实现华为充电动画为例子,简单讲解一下。 仔细观察这一部分,上升的一个一个圆球,抛去这里的一些融合效果,只关注不断上升的圆球,看着像是没有什么规律可言: 我们来模拟一下,如果是使用 10 个 animation-duration 和 animation-delay 都一致的圆的话,核心伪代码: <ul> <li></li> <!--共 10 个...--> <li></li> </ul> ul { display: flex; flex-wrap: nowrap; gap: 5px; } li { background: #000; animation: move 3s infinite 1s linear; } @keyframes move { 0% { transform: translate(0, 0); } 100% { transform: translate(0, -100px); } } 这样,小球的运动会是这样的整齐划一: 要让小球的运动显得非常的随机,只需要让 animation-duration 和 animation-delay 都在一定范围内浮动即可,改造下 CSS: @for $i from 1 to 11 { li:nth-child(#{$i}) { animation-duration: #{random(2000)/1000 + 2}s; animation-delay: #{random(1000)/1000 + 1}s; } } 我们利用 SASS 的循环和 random() 函数,让 animation-duration 在 2-4 秒范围内随机,让 animation-delay 在 1-2 秒范围内随机,这样,我们就可以得到非常自然且不同的上升动画效果,基本不会出现重复的画面,很好的模拟了随机效果: CodePen Demo -- 利用范围随机 animation-duration 和 animation-delay 实现随机动画效果 animation-timing-function 缓动函数 缓动函数在动画中非常重要,它定义了动画在每一动画周期中执行的节奏。 缓动主要分为两类: cubic-bezier-timing-function 三次贝塞尔曲线缓动函数 step-timing-function 步骤缓动函数(这个翻译是我自己翻的,可能有点奇怪) 三次贝塞尔曲线缓动函数 首先先看看三次贝塞尔曲线缓动函数。在 CSS 中,支持一些缓动函数关键字。 /* Keyword values */ animation-timing-function: ease; // 动画以低速开始,然后加快,在结束前变慢 animation-timing-function: ease-in; // 动画以低速开始 animation-timing-function: ease-out; // 动画以低速结束 animation-timing-function: ease-in-out; // 动画以低速开始和结束 animation-timing-function: linear; // 匀速,动画从头到尾的速度是相同的 关于它们之间的效果对比: 除了 CSS 支持的这 5 个关键字,我们还可以使用 cubic-bezier() 方法自定义三次贝塞尔曲线: animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1); 这里有个非常好用的网站 -- cubic-bezier 用于创建和调试生成不同的贝塞尔曲线参数。 三次贝塞尔曲线缓动对动画的影响 关于缓动函数对动画的影响,这里有一个非常好的示例。这里我们使用了纯 CSS 实现了一个钟的效果,对于其中的动画的运动,如果是 animation-timing-function: linear,效果如下: 而如果我们我把缓动函数替换一下,变成 animation-timing-function: cubic-bezier(1,-0.21,.85,1.29),它的曲线对应如下: 整个钟的动画律动效果将变成这样,完全不一样的感觉: CodePen Demo - 缓动不同效果不同 对于许多精益求精的动画,在设计中其实都考虑到了缓动函数。我很久之前看到过一篇《基于物理学的动画用户体验设计》,可惜如今已经无法找到原文。其中传达出的一些概念是,动画的设计依据实际在生活中的表现去考量。 譬如 linear 这个缓动,实际应用于某些动画中会显得很不自然,因为由于空气阻力的存在,程序模拟的匀速直线运动在现实生活中是很难实现的。因此对于这样一个用户平时很少感知到的运动是很难建立信任感的。这样的匀速直线运动也是我们在进行动效设计时需要极力避免的。 步骤缓动函数 接下来再讲讲步骤缓动函数。在 CSS 的 animation-timing-function 中,它有如下几种表现形态: { /* Keyword values */ animation-timing-function: step-start; animation-timing-function: step-end; /* Function values */ animation-timing-function: steps(6, start) animation-timing-function: steps(4, end); } 在 CSS 中,使用步骤缓动函数最多的,就是利用其来实现逐帧动画。假设我们有这样一张图(图片大小为 1536 x 256,图片来源于网络): 可以发现它其实是一个人物行进过程中的 6 种状态,或者可以为 6 帧,我们利用 animation-timing-function: steps(6) 可以将其用一个 CSS 动画串联起来,代码非常的简单: <div class="box"></div> .box { width: 256px; height: 256px; background: url('https://github.com/iamalperen/playground/blob/main/SpriteSheetAnimation/sprite.png?raw=true'); animation: sprite .6s steps(6, end) infinite; } @keyframes sprite { 0% { background-position: 0 0; } 100% { background-position: -1536px 0; } } 简单解释一下上述代码,首先要知道,刚好 256 x 6 = 1536,所以上述图片其实可以刚好均分为 6 段: 我们设定了一个大小都为 256px 的 div,给这个 div 赋予了一个 animation: sprite .6s steps(6) infinite 动画; 其中 steps(6) 的意思就是将设定的 @keyframes 动画分为 6 次(6帧)执行,而整体的动画时间是 0.6s,所以每一帧的停顿时长为 0.1s; 动画效果是由 background-position: 0 0 到 background-position: -1536px 0,由于上述的 CSS 代码没有设置 background-repeat,所以其实 background-position: 0 0 是等价于 background-position: -1536px 0,就是图片在整个动画过程中推进了一轮,只不过每一帧停在了特点的地方,一共 6 帧; 将上述 1、2、3,3 个步骤画在图上简单示意: 从上图可知,其实在动画过程中,background-position 的取值其实只有 background-position: 0 0,background-position: -256px 0,background-position: -512px 0 依次类推一直到 background-position: -1536px 0,由于背景的 repeat 的特性,其实刚好回到原点,由此又重新开始新一轮同样的动画。 所以,整个动画就会是这样,每一帧停留 0.1s 后切换到下一帧(注意这里是个无限循环动画),: 完整的代码你可以戳这里 -- CodePen Demo -- Sprite Animation with steps() animation-duration 动画长短对动画的影响 在这里再插入一个小章节,animation-duration 动画长短对动画的影响也是非常明显的。 在上述代码的基础上,我们再修改 animation-duration,缩短每一帧的时间就可以让步行的效果变成跑步的效果,同理,也可以增加每一帧的停留时间。让每一步变得缓慢,就像是在步行一样。 需要提出的是,上文说的每一帧,和浏览器渲染过程中的 FPS 的每一帧不是同一个概念。 看看效果,设置不同的 animation-duration 的效果(这里是 0.6s -> 0.2s),GIF 录屏丢失了一些关键帧,实际效果会更好点: 当然,在 steps() 中,还有 steps(6, start) 和 steps(6, end) 的差异,也就是其中关键字 start 和 end 的差异。对于上述的无限动画而言,其实基本是可以忽略不计的,它主要是控制动画第一帧的开始和持续时长,比较小的一个知识点但是想讲明白需要比较长的篇幅,限于本文的内容,在这里不做展开,读者可以自行了解。 同个动画效果的补间动画和逐帧动画演绎对比 上述的三次贝塞尔曲线缓动和步骤缓动,其实就是对应的补间动画和逐帧动画。 对于同个动画而言,有的时候两种缓动都是适用的。我们在具体使用的时候需要具体分析选取。 假设我们用 CSS 实现了这样一个图形: 现在想利用这个图形制作一个 Loading 效果,如果利用补间动画,也就是三次贝塞尔曲线缓动的话,让它旋转起来,得到的效果非常的一般: .g-container{ animation: rotate 2s linear infinite; } @keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } 动画效果如下: 但是如果这里,我们将补间动画换成逐帧动画,因为有 20 个点,所以设置成 steps(20),再看看效果,会得到完全不一样的感觉: .g-container{ animation: rotate 2s steps(20) infinite; } @keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } 动画效果如下: 整个 loading 的圈圈看上去好像也在旋转,实际上只是 20 帧关键帧在切换,整体的效果感觉更适合 Loading 的效果。 因此,两种动画效果都是很有必要掌握的,在实际使用的时候灵活尝试,选择更适合的。 上述 DEMO 效果完整的代码:CodePen Demo -- Scale Loading steps vs linear animation-play-state 接下来,我们讲讲 animation-play-state,顾名思义,它可以控制动画的状态 -- 运行或者暂停。类似于视频播放器的开始和暂停。是 CSS 动画中有限的控制动画状态的手段之一。 它的取值只有两个(默认为 running): { animation-play-state: paused | running; } 使用起来也非常简单,看下面这个例子,我们在 hover 按钮的时候,实现动画的暂停: <div class="btn stop">stop</div> <div class="animation"></div> .animation { width: 100px; height: 100px; background: deeppink; animation: move 2s linear infinite alternate; } @keyframes move { 100% { transform: translate(100px, 0); } } .stop:hover ~ .animation { animation-play-state: paused; } 一个简单的 CSS 动画,但是当我们 hover 按钮的时候,给动画元素添加上 animation-play-state: paused: animation-play-state 小技巧,默认暂停,点击运行 正常而言,按照正常思路使用 animation-play-state: paused 是非常简单的。 但是,如果我们想创造一些有意思的 CSS 动画效果,不如反其道而行之。 我们都知道,正常情况下,动画应该是运行状态,那如果我们将一些动画的默认状态设置为暂停,只有当鼠标点击或者 hover 的时候,才设置其 animation-play-state: running,这样就可以得到很多有趣的 CSS 效果。 看个倒酒的例子,这是一个纯 CSS 动画,但是默认状态下,动画处于 animation-play-state: paused,也就是暂停状态,只有当鼠标点击杯子的时,才设置 animation-play-state: running,让酒倒下,利用 animation-play-state 实现了一个非常有意思的交互效果: 完整的 DEMO 你可以戳这里:CodePen Demo -- CSS Beer! 在非常多 Web 创意交互动画我们都可以看到这个技巧的身影。 页面 render 后,无任何操作,动画不会开始。只有当鼠标对元素进行 click ,通过触发元素的 :active 伪类效果的时候,赋予动画 animation-play-state: running,动画才开始进行; 动画进行到任意时刻,鼠标停止点击,伪类消失,则动画停止; animation-fill-mode 控制元素在各个阶段的状态 下一个属性 animation-fill-mode,很多人会误认为它只是用于控制元素在动画结束后是否复位。这个其实是不准确的,不全面的。 看看它的取值: { // 默认值,当动画未执行时,动画将不会将任何样式应用于目标,而是使用赋予给该元素的 CSS 规则来显示该元素的状态 animation-fill-mode: none; // 动画将在应用于目标时立即应用第一个关键帧中定义的值,并在 `animation-delay` 期间保留此值, animation-fill-mode: backwards; // 目标将保留由执行期间遇到的最后一个关键帧计算值。 最后一个关键帧取决于 `animation-direction` 和 `animation-iteration-count` animation-fill-mode: forwards; // 动画将遵循 `forwards` 和 `backwards` 的规则,从而在两个方向上扩展动画属性 animation-fill-mode: both; } 对于 animation-fill-mode 的解读,我在 Segment Fault 上的一个问答中(SF - 如何理解 animation-fill-mode)看到了 4 副很好的解读图,这里借用一下: 假设 HTML 如下: <div class="box"></div> CSS如下: .box{ transform: translateY(0); } .box.on{ animation: move 1s; } @keyframes move{ from{transform: translateY(-50px)} to {transform: translateY( 50px)} } 使用图片来表示 translateY 的值与 时间 的关系: 横轴为表示 时间,为 0 时表示动画开始的时间,也就是向 box 加上 on 类名的时间,横轴一格表示 0.5s 纵轴表示 translateY 的值,为 0 时表示 translateY 的值为 0,纵轴一格表示 50px animation-fill-mode: none 表现如图: 一句话总结,元素在动画时间之外,样式只受到它的 CSS 规则限制,与 @keyframes 内的关键帧定义无关。 animation-fill-mode: backwards 表现如图: 一句话总结,元素在动画开始之前(包含未触发动画阶段及 animation-delay 期间)的样式为动画运行时的第一帧,而动画结束后的样式则恢复为 CSS 规则设定的样式。 animation-fill-mode: forwards 表现如图: 一句话总结,元素在动画开始之前的样式为 CSS 规则设定的样式,而动画结束后的样式则表现为由执行期间遇到的最后一个关键帧计算值(也就是停在最后一帧)。 animation-fill-mode: both 表现如图: 一句话总结,综合了 animation-fill-mode: backwards 和 animation-fill-mode: forwards 的设定。动画开始前的样式为动画运行时的第一帧,动画结束后停在最后一帧。 animation-iteration-count/animation-direction 动画循环次数和方向 讲到了 animation-fill-mode,我们就可以顺带讲讲这个两个比较好理解的属性 -- animation-iteration-count 和 animation-direction animation-iteration-count 控制动画运行的次数,可以是数字或者 infinite,注意,数字可以是小数 animation-direction 控制动画的方向,正向、反向、正向交替与反向交替 在上面讲述 animation-fill-mode 时,我使用了动画运行时的第一帧替代了@keyframes 中定义的第一帧这种说法,因为动画运行的第一帧和最后一帧的实际状态还会受到动画运行方向 animation-direction 和 animation-iteration-count 的影响。 在 CSS 动画中,由 animation-iteration-count 和 animation-direction 共同决定动画运行时的第一帧和最后一帧的状态。 动画运行的第一帧由 animation-direction 决定 动画运行的最后一帧由 animation-iteration-count 和 animation-direction 决定 动画的最后一帧,也就是动画运行的最终状态,并且我们可以利用 animation-fill-mode: forwards 让动画在结束后停留在这一帧,这个还是比较好理解的,但是 animation-fill-mode: backwards 和 animation-direction 的关系很容易弄不清楚,这里简答讲解下。 设置一个 100px x 100px 的滑块,在一个 400px x 100px 的容器中,其代码如下: <div class="g-father"> <div class="g-box"></div> </div> .g-father { width: 400px; height: 100px; border: 1px solid #000; } .g-box { width: 100px; height: 100px; background: #333; } 表现如下: 那么,加入 animation 之后,在不同的 animation-iteration-count 和 animation-direction 作用下,动画的初始和结束状态都不一样。 如果设置了 animation-fill-mode: backwards,则元素在动画未开始前的状态由 animation-direction 决定: .g-box { ... animation: move 4s linear; animation-play-state: paused; transform: translate(0, 0); } @keyframes move { 0% { transform: translate(100px, 0); } 100% { transform: translate(300px, 0); } } 注意这里 CSS 规则中,元素没有设置位移 transform: translate(0, 0),而在动画中,第一个关键帧和最后一个关键的 translateX 分别是 100px、300px,配合不同的 animation-direction 初始状态如下。 下图假设我们设置了动画默认是暂停的 -- animation-play-state: paused,那么动画在开始前的状态为: 动画的分治与复用 讲完了每一个属性,我们再来看看一些动画使用过程中的细节。 看这样一个动画: <div></div> div { width: 100px; height: 100px; background: #000; animation: combine 2s; } @keyframes combine { 100% { transform: translate(0, 150px); opacity: 0; } } 这里我们实现了一个 div 块下落动画,下落的同时产生透明度的变化: 对于这样一个多个属性变化的动画,它其实等价于: div { animation: falldown 2s, fadeIn 2s; } @keyframes falldown { 100% { transform: translate(0, 150px); } } @keyframes fadeIn { 100% { opacity: 0; } } 在 CSS 动画规则中,animation 是可以接收多个动画的,这样做的目的不仅仅只是为了复用,同时也是为了分治,我们对每一个属性层面的动画能够有着更为精确的控制。 keyframes 规则的设定 我们经常能够在各种不同的 CSS 代码见到如下两种 CSS @keyframes 的设定: 使用百分比 @keyframes fadeIn { 0% { opacity: 1; } 100% { opacity: 0; } } 使用 from 及 to @keyframes fadeIn { from { opacity: 1; } to { opacity: 0; } } 在 CSS 动画 @keyframes 的定义中,from 等同于 0%,而 to 等同于 100%。 当然,当我们的关键帧不止 2 帧的时,更推荐使用百分比定义的方式。 除此之外,当动画的起始帧等同于 CSS 规则中赋予的值并且没有设定 animation-fill-mode,0% 和 from 这一帧是可以删除的。 动画状态的高优先级性 我曾经在这篇文章中 -- 深入理解 CSS(Cascading Style Sheets)中的层叠(Cascading) 讲过一个很有意思的 CSS 现象。 这也是很多人对 CSS 优先级的一个认知误区,在 CSS 中,优先级还需要考虑选择器的层叠(级联)顺序。 只有在层叠顺序相等时,使用哪个值才取决于样式的优先级。 那什么是层叠顺序呢? 根据 CSS Cascading 4 最新标准: CSS Cascading and Inheritance Level 5(Current Work) 定义的当前规范下申明的层叠顺序优先级如下(越往下的优先级越高,下面的规则按升序排列): Normal user agent declarations Normal user declarations Normal author declarations Animation declarations Important author declarations Important user declarations Important user agent declarations Transition declarations 简单翻译一下: 按照上述算法,大概是这样: 过渡动画过程中每一帧的样式 > 用户代理、用户、页面作者设置的!important样式 > 动画过程中每一帧的样式优先级 > 页面作者、用户、用户代理普通样式。 然而,经过多个浏览器的测试,实际上并不是这样。(尴尬了) 举个例子,我们可以通过这个特性,覆盖掉行内样式中的 !important 样式: <p class="txt" style="color:red!important">123456789</p> .txt { animation: colorGreen 2s infinite; } @keyframes colorGreen { 0%, 100% { color: green; } } 在 Safari 浏览器下,上述 DEMO 文本的颜色为绿色,也就是说,处于动画状态中的样式,能够覆盖掉行内样式中的 !important 样式,属于最最高优先级的一种样式,我们可以通过无限动画、或者 animation-fill-mode: forwards,利用这个技巧,覆盖掉本来应该是优先级非常非常高的行内样式中的 !important 样式。 我在早两年的 Chrome 中也能得到同样的结果,但是到今天(2022-01-10),最新版的 Chrome 已经不支持动画过程中关键帧样式优先级覆盖行内样式 !important 的特性。 对于不同浏览器,感兴趣的同学可以利用我这个 DEMO 自行尝试,CodePen Demo - the priority of CSS Animation CSS 动画的优化 这也是非常多人非常关心的一个重点。 我的 CSS 动画很卡,我应该如何去优化它? 动画元素生成独立的 GraphicsLayer,强制开始 GPU 加速 CSS 动画很卡,其实是一个现象描述,它的本质其实是在动画过程中,浏览器刷新渲染页面的帧率过低。通常而言,目前大多数浏览器刷新率为 60 次/秒,所以通常来讲 FPS 为 60 frame/s 时动画效果较好,也就是每帧的消耗时间为 16.67ms。 页面处于动画变化时,当帧率低于一定数值时,我们就感觉到页面的卡顿。 而造成帧率低的原因就是浏览器在一帧之间处理的事情太多了,超过了 16.67ms,要优化每一帧的时间,又需要完整地知道浏览器在每一帧干了什么,这个就又涉及到了老生常谈的浏览器渲染页面。 到今天,虽然不同浏览器的渲染过程不完全相同,但是基本上大同小异,基本上都是: 简化一下也就是这个图: 这两张图,你可以在非常多不同的文章中看到。 回归本文的重点,Web 动画很大一部分开销在于层的重绘,以层为基础的复合模型对渲染性能有着深远的影响。当不需要绘制时,复合操作的开销可以忽略不计,因此在试着调试渲染性能问题时,首要目标就是要避免层的重绘。那么这就给动画的性能优化提供了方向,减少元素的重绘与回流。 这其中,如何减少页面的回流与重绘呢,这里就会运用到我们常说的** GPU 加速**。 GPU 加速的本质其实是减少浏览器渲染页面每一帧过程中的 reflow 和 repaint,其根本,就是让需要进行动画的元素,生成自己的 GraphicsLayer。 浏览器渲染一个页面时,它使用了许多没有暴露给开发者的中间表现形式,其中最重要的结构便是层(layer)。 在 Chrome 中,存在有不同类型的层: RenderLayer(负责 DOM 子树),GraphicsLayer(负责 RenderLayer 的子树)。 GraphicsLayer ,它对于我们的 Web 动画而言非常重要,通常,Chrome 会将一个层的内容在作为纹理上传到 GPU 前先绘制(paint)进一个位图中。如果内容不会改变,那么就没有必要重绘(repaint)层。 而当元素生成了自己的 GraphicsLayer 之后,在动画过程中,Chrome 并不会始终重绘整个层,它会尝试智能地去重绘 DOM 中失效的部分,也就是发生动画的部分,在 Composite 之前,页面是处于一种分层状态,借助 GPU,浏览器仅仅在每一帧对生成了自己独立 GraphicsLayer 元素层进行重绘,如此,大大的降低了整个页面重排重绘的开销,提升了页面渲染的效率。 因此,CSS 动画(Web 动画同理)优化的第一条准则就是让需要动画的元素生成了自己独立的 GraphicsLayer,强制开始 GPU 加速,而我们需要知道是,GPU 加速的本质是利用让元素生成了自己独立的 GraphicsLayer,降低了页面在渲染过程中重绘重排的开销。 当然,生成自己的独立的 GraphicsLayer,不仅仅只有 transform3d api,还有非常多的方式。对于上述一大段非常绕的内容,你可以再看看这几篇文章: 【Web动画】CSS3 3D 行星运转 && 浏览器渲染原理 Accelerated Rendering in Chrome 除了上述准则之外,还有一些提升 CSS 动画性能的建议: 减少使用耗性能样式 不同样式在消耗性能方面是不同的,改变一些属性的开销比改变其他属性要多,因此更可能使动画卡顿。 例如,与改变元素的文本颜色相比,改变元素的 box-shadow 将需要开销大很多的绘图操作。box-shadow 属性,从渲染角度来讲十分耗性能,原因就是与其他样式相比,它们的绘制代码执行时间过长。这就是说,如果一个耗性能严重的样式经常需要重绘,那么你就会遇到性能问题。 类似的还有 CSS 3D 变换、mix-blend-mode、filter,这些样式相比其他一些简单的操作,会更加的消耗性能。我们应该尽可能的在动画过程中降低其使用的频率或者寻找替代方案。 当然,没有不变的事情,在今天性能很差的样式,可能明天就被优化,并且浏览器之间也存在差异。 因此关键在于,我们需要针对每一起卡顿的例子,借助开发工具来分辨出性能瓶颈所在,然后设法减少浏览器的工作量。学会 Chrome 开发者工具的 Performance 面板及其他渲染相关的面板非常重要,当然这不是本文的重点。大家可以自行探索。 使用 will-change 提高页面滚动、动画等渲染性能 will-change 为 Web 开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。 值得注意的是,用好这个属性并不是很容易: 不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。 有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。 不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。 will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。 给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。 有人说 will-change 是良药,也有人说是毒药,在具体使用的时候,可以多测试一下。

查看详情