最近给 nzoo 折腾官网,拿 angular2.0 + webpack 实现SPA,然后觉得最终打包后的出口文件有点大,用户首次访问会有一个时间较长的白屏等候界面,感觉体验不太好。

于是希望在用户下载整个 bundle 时能够先看到一个“加载中”的UI做过度,鉴于 nzoo 的LOGO也较简洁,便舍弃笨重的雪碧图+step动画的形式,转以 mask-image + transition动画来实现。

整体最终交互如下(模拟的是 2G 网速):

虽然界面简单,但整个动画仅仅使用了一张3.5kb大小的图片(戳我查看,注意是全白的会跟背景混一体):

如交互截图所示,我们希望在用户刚进入页面时,开始从底部给 logo 填色,持续10秒的 easeout 动画然后停在距离顶部还有一小部分未填色的地方。

接着在用户下载完 bundle 后,用 300ms 时间填完整个logo再执行 angular 应用启动脚本。

什么是 mask-image

擅长捣弄 Flash 甚至 AE 的朋友相信对“遮罩层”的概念会很清楚,都是指定某层的元件的轮廓/alpha通道来作为自己剪影的依据。在 Flash 中遮罩层只支持矢量,而AE则支持多种形式的遮罩(毕竟人家用来后期的嘛)。

另外FW和PS都支持alpha遮罩(PS中称为“蒙版”)。而今天要聊的css3中的 mask-image 则是以指定图片的透明度作为剪影依据的。

介个怎么理解呢?我们来张简单的示意图:

相信玩 flash 的童鞋会不屑地一笑,觉得是个好简单的事儿—— 底部搞个填满色的DOM由下往上运动,顶部固定放个轮廓层(png)剪影整个动画就行了嘛。

然而现实比较骨感—— mask-image 所指定的遮罩图会死死地固定在被遮罩元素上,可以理解为若元素动了那么遮罩图也会随着动。也就是说 flash 的那一套不适用于css3上。

此路不通换条道走—— 把动画改为 transition + background-position 来实现,而不靠元素本身瞎运动了。

我们现在手头有个 nzoo 的剪影,先看看填满整个logo 颜色需要怎么做,一共就俩步:

⑴ 放个DOM,给它配上 -webkit-mask-image 的样式指定遮罩图片;

⑵ 给 DOM上渐变色(得多次微调让渐变的角度、位置到位)。

于是初始化样式是这样的:

<style>
mask-bg{
mask-image: url(src/image/common/mask.png);
-webkit-mask-image: url(src/image/common/mask.png);
position: absolute;
width:409px;height:158px;
background-image: linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -webkit-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -moz-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
}
</style> <mask-bg></mask-bg>

鉴于 firefox 还不支持在样式中配置 mask-image 特性,所以代码中我们没写 -moz-mask-image。(firefox的兼容后面说)

总之与 mask-image 样式结合前后的是酱子的:

留意 nzoo 的字样是有倾斜角度的,所以我们在 liner-gradient 中加了个 353deg 用于线性倾斜填充,这里填充的角度以及位置,均是后期微调得出的数据。

接着我们在其顶部安放另一个DOM(<mask-top>),用作完全未填色的 logo (底色为#EEE):

<style>
mask-bg, mask-top{
position: absolute;
width:409px;height:158px;
mask-image: url(src/image/common/mask.png);
-webkit-mask-image: url(src/image/common/mask.png);
}
mask-bg{
background-image: linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -webkit-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -moz-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
}
mask-top{
background-image: linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -webkit-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -moz-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
}
</style> <mask-bg></mask-bg>
<mask-top></mask-top>

为实现动画再加上 background-position、background-size 和 transition 定义:

<style>
mask-bg, mask-top{
position: absolute;
width:409px;height:158px;
mask-image: url(src/image/common/mask.png);
-webkit-mask-image: url(src/image/common/mask.png);
}
mask-bg{
background-image: linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -webkit-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -moz-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
}
mask-top{
background-image: linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -webkit-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -moz-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-size: auto 300% ;
-webkit-background-size: auto 300% ;
-moz-background-size: auto 300% ;
background-position: 0 -50%;
transition: all 10s cubic-bezier(0, 0, 0.28, 1) 1s;
}
</style> <mask-bg></mask-bg>
<mask-top></mask-top>

其中 background-size 的配置用于拉长线性填充的渐进线,并初始化 background-position 的垂直距离为 -50%(即刚好整个剪影区域都是有填满#EEE的,剪影底部以下才则为rgba(0,0,0,0)的透明填充)。

所以后续我们通过动态改变 background-position 的垂直定位,把<mask-top>的渐进性由底部往上平移,从而逐步展示出其下方的<mask-bg>元素的内容,就能实现整个加载动画界面。

为了方便理解 <mask-top> 原理,我做了个效果图:

另外要留意的是我们给 transition 动画加了个1秒延迟,主要是为了方便客户端先下载遮罩图片再执行动画。

至于如何触发 transition 就不废话了,还是按老套数给父层元素动态加个 class 来实现:

        app.loading mask-top{
background-position: 0 -8%;
}
app.loading-done mask-top{
background-position: 0 0;
transition: all 0.3s;
}
    setTimeout(function(){
document.querySelector('app').className='loading';
},10);

注意这里的 app 是我给 <mask-top> 和 <mask-top> 外部过了一层自定义DOM <app></app>,原本只是用作后续挂载 angular 组件,现在咱把它用于存放挂载组件前先执行的加载交互元素。

在用户下载好 bundle 脚本之后(这时说明一切都loading好了),我们给 <app> 换上名为“loading-done”的类触发“把logo全部填满色”的 300ms 动画,也顺道延迟 300ms 再启动angular:

import {App} from '../component/App';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {bootstrap} from 'angular2/platform/browser'; document.querySelector('app').className='loading-done';
setTimeout(bootstrap.bind(this, App, [ROUTER_PROVIDERS]), 300);

于是在 webkit 浏览器中一切就如同一开始的交互动画一样顺利运行。

Firefox 和 Edge

Firefox 与 chrome 不同,对 mask-image 有另一套标准,需要 svg 加持,我们看下示例

  <!-- SVG begins -->
<svg> <!-- Definition of a mask begins -->
<defs>
<mask id="mask" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse">
<image width="400px" height="300px" xlink:href="mouse.png"></image>
</mask>
</defs>
<!-- Definition of a mask ends --> <foreignObject width="400px" height="300px" style="mask: url(#mask);"> <!-- HTML begins -->
<div class="element">
<p>Lorem ipsum dolor sit … amet.</p>
</div>
<!-- HTML ends --> </foreignObject>
</svg>
<!-- SVG ends -->

说白了就是往 svg 里嵌入 XHTML 来实现,细心看看其实也不复杂。我们可以稍微改下代码(主要是DOM结构)来做兼容:

    <style>
app>div.loading-mask{
position: absolute;
top: 150px;
left: 50%; margin-left: -204px;
width:409px;height:158px;
overflow: hidden;
}
.loading-mask mask-bg,.loading-mask mask-top{
position: absolute;
width:409px;height:158px;
mask-image: url(src/image/common/mask.png);
-webkit-mask-image: url(src/image/common/mask.png);
}
.loading-mask mask-bg{
background-image: linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -webkit-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
background-image: -moz-linear-gradient(353deg,#89C027,#89C027 28%,#E96036 28%,#E96036 49%,#FEF158 49%,#FEF158 72%,#76C5EE 72%);
}
.loading-mask mask-top{
background-image: linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -webkit-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-image: -moz-linear-gradient(bottom,#EEEEEE,#EEEEEE 60%,rgba(0,0,0,0) 60%);
background-size: auto 300% ;
-webkit-background-size: auto 300% ;
-moz-background-size: auto 300% ;
background-position: 0 -50%;
transition: all 10s cubic-bezier(0, 0, 0.28, 1) 1s;
}
app.loading mask-top{
background-position: 0 -8%;
}
app.loading-done mask-top{
background-position: 0 0;
transition: all 0.3s;
}
</style> <app>
<div class="loading-mask">
<svg width="409px" height="158px">
<defs>
<mask id="mask">
<image width="409px" height="158px" xlink:href="src/image/common/mask.png"></image>
</mask>
</defs>
<foreignObject width="409px" height="158px" style="mask: url(#mask);"> <mask-bg></mask-bg>
<mask-top></mask-top> </foreignObject>
</svg> </div>
</app>

这样在 Firefox 中也能正常运行我们的加载动画了。

不过有趣的是,我在 caniuse 看到巨硬 Edge 是不支持 mask-image 的:

而实际用上 Firefox 这套后发现居然能在 Edge 上顺利运行了。

ok 大周末半夜三更的就跟大家唠嗑到这,想来我也许久没写样式软文了,共勉~

巧用 mask-image 实现简单进度加载界面的更多相关文章

  1. 原生JS实战:分享一个首页进度加载动画!

    本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5871134.html 该程序是本人的个人作品,写的不好,可以参考,但未经 ...

  2. 一款基于jquery带百分比的响应式进度加载条

    今天要给大家带来一款基于jquery带百分比的响应式进度加载条.这款加载条非常漂亮,而且带有进度的百度比,且在不同的百分比用的是不同的颜色.而且这款加载条采用了响应式设计,在不同的分辨率的显示器下完美 ...

  3. Canvas制作动态进度加载水球

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. Egret白鹭开发小游戏之自定义load加载界面

    刚接触不久就遇到困难------自定义loading.想和其他获取图片方式一样获取加载界面的图片,结果发现资源还没加载就需要图片,在网上百度了许多,都没有找到正确的方式,通过自己的摸索,终于,,,我成 ...

  5. [UE4]场景加载界面

    就可以这样就可以了,当另外一个场景成功打开后,场景加载界面也会自动消失(因为这是加载界面是添加到当前场景). 加上delay是为了在小场景测试的时候可以方便看到加载场景,避免场景加载过快看不到,不加的 ...

  6. 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)

    正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piec ...

  7. DirectUI界面编程(三)从XML文件中加载界面

    Duilib支持xml界面布局,使得界面设计与逻辑处理相分离,本节介绍如何从xml文件中加载界面元素. 我们需要以下几个步骤: 创建并初始化CPaintManagerUI对象. 创建CDialogBu ...

  8. 使用简单NGUI加载进度条

    1.在Panel上添加Slider,GNUI--Open--Widget Wizard--Slider,设置Empty和Full 2.在Panel上添加Label,GNUI--Open--Widget ...

  9. layui的几个简单使用(简单弹窗,加载效果,移除加载效果)

    1.加载效果和移除加载效果 function layuiLoading(msg){ layui.use(['layer', 'form'], function(){ index = layer.loa ...

随机推荐

  1. 一个粗心的Bug,JSON格式不规范导致AJAX错误

    一.事件回放  今天工作时碰到了一个奇怪的问题,这个问题很早很早以前也碰到过,不过没想到过这么久了竟然又栽在这里. 当时正在联调一个项目,由于后端没有提供数据接口,于是我直接本地建立了一个 json ...

  2. [Java 缓存] Java Cache之 DCache的简单应用.

    前言 上次总结了下本地缓存Guava Cache的简单应用, 这次来继续说下项目中使用的DCache的简单使用. 这里分为几部分进行总结, 1)DCache介绍; 2)DCache配置及使用; 3)使 ...

  3. JavaScript之职责链模式

    一.概述 职责链模式(Chain of responsibility),就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有 ...

  4. 12、Struts2表单重复提交

    什么是表单重复提交 表单的重复提交: 若刷新表单页面, 再提交表单不算重复提交. 在不刷新表单页面的前提下: 多次点击提交按钮 已经提交成功, 按 "回退" 之后, 再点击 &qu ...

  5. 9、 Struts2验证(声明式验证、自定义验证器)

    1. 什么是Struts2 验证器 一个健壮的 web 应用程序必须确保用户输入是合法.有效的. Struts2 的输入验证 基于 XWork Validation Framework 的声明式验证: ...

  6. 深入.NET平台和C#编程总结大全

    对于初学者的你,等到你把这个看完之后就更清楚地认知.NET和C#编程了,好了废话不多说,开始吧!                                                     ...

  7. android_m2repository_rxx.zip下载地址以及MD5

    地址 MD5 https://dl-ssl.google.com/android/repository/android_m2repository_r08.zip 8C8EC4C731B7F55E646 ...

  8. 非技术1-学期总结&ending 2016

    好久好久没写博客了,感觉动力都不足了--12月只发了一篇博客,好惭愧-- 今天是2016年最后一天,怎么能不写点东西呢!! 学期总结 大学中最关键一年的第一个学期,共4个月.前20天在学网络方面的,当 ...

  9. python性能检测工具整理

    python 运行后出现core dump产生core.**文件,可通过gdb来调试 Using GDB with a core dump having found build/python/core ...

  10. 烂泥:jira7.2安装、中文及破解

    . jira的主要配置文件,存放在/opt/atlassian/jira/conf/server.xml文件中,如下: vim /opt/atlassian/jira/conf/server.xml ...