5.3 作用域、闭包和this

let 声明的变量只存在于其所在的代码块中

由于 JS 是基于词法(静态)作用域的语言,词法作用域的含义是在函数定义时就确定了作用域,而不是函数执行时再确定

calculate 函数,所带的闭包,scope =10;

第六章

Microdata 进行SEO优化:

7.3 提高Web动画的性能实战

7.8.1 使用CSS3动画

   

velocity源码解读

阅读(1,878) 评论(0) JS2017-01-01

写在前面

一个号称incredibly fast的动画库,好奇很久了,最近差不多读完了源码

一.结构

动画库基本结构:

  • timer:周期执行的定时器,比如递归requestAnimationFrame/setTimeout

  • tween:补间数据,包括duration, progress, easing, events

  • tick():定时器每次执行的动作,更新补间数据,应用到元素上

有了这些东西,动画就能跑起来了,只是原始一点野蛮一点而已:

var timer = window.requestAnimationFrame || setTimeout;
var tween = {
startTime: 0,
el: document.body,
property: 'marginLeft',
start: 0,
end: 200,
unit: 'px',
duration: 1000,
easing: function(p) {return p;},
begin: function() {console.log('begin')},
progress: function() {console.log('progress')},
complete: function() {console.log('complete')}
};
var tick = function() {
if (!tween.startTime) {
tween.startTime = Date.now();
tween.begin && tween.begin.call(tween.el);
}
var p = Math.min((Date.now() - tween.startTime) / tween.duration, 1);
var delta = tween.end - tween.start;
if (p < 1) {
tween.el.style[tween.property] = tween.start + delta * tween.easing(p) + tween.unit;
tween.progress && tween.progress.call(tween.el);
timer(tick);
}
else {
tween.el.style[tween.property] = tween.end + tween.unit;
tween.complete && tween.complete.call(tween.el);
}
}
// use
tick();

效果是body在1秒内向右匀速移动200px,看起来傻傻的,我们可能想要一些增强效果:

  • 多个元素按顺序动/同时动

  • 稍复杂的easinglinear太无趣了)

  • 循环(有限次/无限次)、暂停、停止

为了支持这些,还需要一些扩展结构:

  • 动画队列:控制动画序列

  • easing包:缓动效果、物理效果、step效果等等

  • 控制命令:reversepause/resumestop等等

当然,基础结构也不够健壮,至少应该有:

  • CSS工具包:负责校验/存取CSS属性,包括属性前缀检测、单位转换、硬件加速、子属性整合

  • 数据缓存:用来存放动画队列、已知的属性前缀、不频繁变化的DOM属性值

到这里,一个动画库的结构基本完整了,我们可能还想要一些高级功能:

  • 快进

  • 重播

  • 跳过

这些强大的特性能给我们带来惊喜,事实上Velocity支持快进(mock),而读源码就是为了添上重播和跳过功能

二.设计理念

1.缓存所有能缓存的东西

通过缓存数据,来尽可能地减少DOM查询,一点一点提升性能

源码从来不解释为什么缓存,只偶尔提到为什么不做缓存:

/* Note: Unlike other properties in Velocity, the browser's scroll position is never cached since it so frequently changes
(due to the user's natural interaction with the page). */
// 当前scroll位置,起点
//! scroll不缓存,每次都从node取(el.scrollLeft/Top),因为频繁变化
scrollPositionCurrent = opts.container["scroll" + scrollDirection]; /* GET */

除了缓存,另一个提升性能的技巧是整合操作,只在最后写一次DOM,例如transformCache

2.把动画逻辑收敛进来

除了必需的动画事件,Velocity提供了非常人性化的display/visibility设计:none/visibility值在动画结束时应用,非none/visibility值在动画开始时就用

类似的还有属性值可以是function,根据元素在集合中的位置来生成初始值,例如:

$("div").velocity({
translateX: function(i, total) {
// i is equal to the current element's index in the total set of divs.
// Successively increase the translateX value of each element.
return (i * 20);
}
}, 2000);

这样做是为了把动画逻辑收敛在Velocity中,动画相关逻辑应该交给动画库控制,保证业务代码干净

三.技巧

1.正则环视的用法

不匹配内容,但是强制检查,比如场景:

// 去掉rgb的小数部分,保留a的小数部分(通过肯定正则环视来搞定的)
'rgba(1.5, 1.4, 1.1, 0.3)'.replace(/\.(\d)+(?=,)/g, "")
"rgba(1, 1, 1, 0.3)"

2.异步throw

//!!! 技巧,异步throw,不会影响逻辑流程
setTimeout(function() {
throw error;
}, 1);

例如:

/* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */
try {
opts.complete.call(elements, elements);
} catch (error) {
setTimeout(function() {
throw error;
}, 1);
}

3.循环动画的实现

利用reverse巧妙实现循环,有限次循环:

// 需要reverse的次数
//! 第一次是正向的,后续的2n-1次都是reverse,例如:正-反-反反-反反反
//! 只是调换起点终点,所以可以这么干
var reverseCallsCount = (opts.loop * 2) - 1;

无限循环:

// 通过reverse + loop: true来实现无限循环(第一次单程结束时调换起点终点)
Velocity(element, "reverse", {loop: true, delay: opts.delay});

4.jQuery队列的’inprogress’哨兵

自动dequeue时,如果发现队首元素为inprogress的话,不dequeue。而每次dequeue时,都会往队列里unshift('inprogress')

这样保证了第一次自动dequeue,后续的必须手动dequeue。用标识变量也行,但得挂在queue数组上,用数组首元可能是为了避免往数组上添属性

P.S.自动dequeue在源码processElement()尾部,比较隐蔽

四.黑科技及注意事项

Velocity的文档常年不更新,且只介绍基本用法,这里介绍一些从源码中发现的好东西

黑科技

1.查看补间插值

如果动画属性名为tween,表示测试补间插值。progress回调的第5个参数为补间插值,其它时候为null。例如:

$el.velocity({
tween: 500
}, {
easing: 'easeIn',
delay: 300,
duration: 600,
progress: function() {
console.log(arguments[4]);
}
});`

可以输出每个tick0变化到500的具体值,有些场景下,这些补间值很有用

2.停止所有动画

有没有办法直接停掉tick loop?有的。

$.Velocity.State.isTicking = false

tick loop的开关,直接停掉rAF递归,next tick生效

但这个是破坏性的,不可恢复,因为停之前没有处理ctx(当前值,当前时间等等)

而且,这样直接停掉tick loop,在性能上不是最优的,因为缺少收尾处理:移除动画元素身上多余的3D变换(主要指硬件加速hack),减少复合层数

3.操作缓存数据

一般不需要手动修改缓存值,但在解决一些闪烁的问题时很有用:

// 设置velocity缓存值
Velocity.Utilities.data(node, "velocity").transformCache = {
'scaleX': '0.8',
'scaleY': '0.8'
};

同样,在重置动画时单纯抹掉style没有用,下次动画仍然取缓存值,必须要清除属性值缓存:

// 清除velocity缓存值
$.Velocity.Utilities.removeData(node);

注意事项

1.调试性能时注意硬件加速

mobileHA选项会被修正,Chrome调试必须开模拟移动设备才能看到真实的层数:

// 硬件加速
// 传入mobileHA=true不算,设备支持才行(移动设备,且不是安卓2.3)
opts.mobileHA = (opts.mobileHA && Velocity.State.isMobile && !Velocity.State.isGingerbread);

2.无限360度旋转

每个属性可以有不同的easing,例如:

$el.velocity({
translateX: [100, 'easeInOut', 0]
}, 1000);

但无限360度旋转的动画不能这样传入easing,否则每转一圈会有停顿,例如:

// 第一圈正常,之后每圈结束有停顿
$el.velocity({
rotateZ: [360, 'linear', 0]
}, {
duration: 1000,
loop: true
});

从现象上看是有停顿,其实原因来自reverse的内部实现:

/* Easing is the only option that embeds into the individual tween data (since it can be defined on a per-property basis).
Accordingly, every property's easing value must be updated when an options object is passed in with a reverse call.
The side effect of this extensibility is that all per-property easing values are forcefully reset to the new value. */
// 如果传入了非空opt,每个属性的easing统一用opt的easing
//! 因为easing可以是属性级的
//! 如果传入的opt有easing,就用该值统一覆盖掉上一个call中各个属性的
if (!Type.isEmptyObject(options)) {
lastTweensContainer[lastTween].easing = opts.easing;
}

算是实现上的bug,因为非空opt不代表opt.easing非空,所以停顿的原因是:

第一圈正常:linear
第二圈:reverse中把linear改成swing(默认easing)了
第n圈:都是reverse,所以都是swing

swing效果是两头慢,中间快,这样就出现了诡异的停顿。当然,紧急修复方案是把easing写在opt里,例如:

// 第一圈正常,之后每圈结束有停顿
$el.velocity({
// 这里的easing要与下面的一致,或者干脆去掉
rotateZ: [360, 'linear', 0]
}, {
easing: 'linear',
duration: 1000,
loop: true
});

3.background-position无限循环动画有问题

通过源码很容易发现逻辑漏洞:

//!!! 这里有bug,如果startValue不是0,这里会给强制0
// 例如backgroundPositionX: ['100%', -100],这里会露出前100像素,强制从0到100%
if (/^backgroundPosition/.test(propertyName) && parseFloat(tweenContainer.endValue) === 100 && tweenContainer.unitType === "%") {
tweenContainer.endValue = 0;
tweenContainer.startValue = 100;
}

应该是像rotate一样,交换起点终点,这里强制为0就有问题了,例如:

$el.css({
background: 'url(ruler.jpeg) no-repeat top left',
backgroundSize: 'auto 100%',
backgroundPosition: '-100px 0'
})
.velocity({
backgroundPositionX: ['100%', -100]
}, {
duration: 2000,
loop: true
});

background-position相关动画中需要注意这一点,或者手动修复它

五.源码分析

Git地址:https://github.com/ayqy/velocity-1.4.1

P.S.源码4600行,读完手动注释版本5400行,足够详细

 

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方案。vw的兼容方案可以参阅《如何在Vue项目中使用vw实现移动端适配》一文。

Compass is no longer actively maintained. Compass is a Stylesheet Authoring Environment that makes your website design simpler to implement and easier to maintain. http://compass-style.org
 
 
Sass makes CSS fun! https://sass-lang.com
 

PostCSS plugin to parse CSS and add vendor prefixes to CSS rules using values from Can I Use. It is recommended by Google and used in Twitter and Alibaba.

 

Sass是一种"CSS预处理器",可以让CSS的开发变得简单和可维护。但是,只有搭配Compass,它才能显出真正的威力。

本文介绍Compass的用法。毫不夸张地说,学会了Compass,你的CSS开发效率会上一个台阶。

一、Compass是什么?

简单说,Compass是Sass的工具库(toolkit)。

Sass本身只是一个编译器,Compass在它的基础上,封装了一系列有用的模块和模板,补充Sass的功能。它们之间的关系,有点像Javascript和jQuery、Ruby和Rails、python和Django的关系。

二、安装

Compass是用Ruby语言开发的,所以安装它之前,必须安装Ruby。

Compass 提供了大量的混合宏 Mixins 用来处理类似添加浏览器前缀这样的工作

一种更优雅的处理浏览器前缀的方式是使用 CSS 的后处理程序,比如 PostCSS 最流行的 Autoprefixer 插件

第8章 前端工程化实战

CSS将从入口脚本文件中抽离出来,作为一个单独的入口样式文件,并在入口页面文件里直接引用

DefinePlugin插件用来定义一些全局变量,这些变量可以在模块当中直接使用,比如通常定义一个变量来标识当前是开发环境还是生产环境;

缓存控制:

模块引用:

相对路径、绝对路径和模块路径

当前路径 --> --> 如果文件不存在,继续往上层目录查找,直至磁盘根目录

后缀名自动补全

异步模块加载

Source Map 提供了源文件代码到目标文件代码的对应关系






《移动WEB前端高级开发实践@www.java1234.com.pdf》——2的更多相关文章

  1. 《从零开始做一个MEAN全栈项目》(1)

    欢迎关注本人的微信公众号"前端小填填",专注前端技术的基础和项目开发的学习. 在本系列的开篇,我打算讲一下全栈项目开发的优势,以及MEAN项目各个模块的概览. 为什么选择全栈开发? ...

  2. 《从零开始做一个MEAN全栈项目》(2)

    欢迎关注本人的微信公众号"前端小填填",专注前端技术的基础和项目开发的学习.   上一节简单介绍了什么是MEAN全栈项目,这一节将简要介绍三个内容:(1)一个通用的MEAN项目的技 ...

  3. 《从零开始做一个MEAN全栈项目》(3)

    欢迎关注本人的微信公众号"前端小填填",专注前端技术的基础和项目开发的学习. 上一篇文章给大家讲了一下本项目的开发计划,这一章将会开始着手搭建一个MEAN项目.千里之行,始于足下, ...

  4. 《从零开始做一个MEAN全栈项目》(4)

    欢迎关注本人的微信公众号"前端小填填",专注前端技术的基础和项目开发的学习. 在上一篇中,我们讲了如何去构建第一个Express项目,总结起来就是使用两个核心工具,express和 ...

  5. 一个关于vue+mysql+express的全栈项目(一)

    最近学了mysql数据库,寻思着能不能构思一个小的全栈项目,思来想去,于是就有了下面的项目: 先上几张效果图吧       目前暂时前端只有这几个页面,后端开发方面,有登录,注册,完善用户信息,获取用 ...

  6. Vue、Nuxt服务端渲染,NodeJS全栈项目,面试小白的博客系统~~

    Holle,大家好,我是李白!! 一时兴起的开源项目,到这儿就告一段落了. 这是一个入门全栈之路的小项目,从设计.前端.后端.服务端,一路狂飙的学习,发量正在欣喜若狂~~ 接触过WordPress,H ...

  7. Vue、Node全栈项目~面向小白的博客系统~

    个人博客系统 前言 ❝ 代码质量问题轻点喷(去年才学的前端),有啥建议欢迎联系我,联系方式见最下方,感谢! 页面有啥bug也可以反馈给我,感谢! 这是一套包含前后端代码的个人博客系统,欢迎各位提出建议 ...

  8. SpringBoot 整合 Elastic Stack 最新版本(7.14.1)分布式日志解决方案,开源微服务全栈项目【有来商城】的日志落地实践

    一. 前言 日志对于一个程序的重要程度不用过多的言语修饰,本篇将以实战的方式讲述开源微服务全栈项目 有来商城 是如何整合当下主流日志解决方案 ELK +Filebeat . 话不多说,先看实现的效果图 ...

  9. 全栈项目|小书架|服务器端-NodeJS+Koa2实现首页图书列表接口

    通过上篇文章 全栈项目|小书架|微信小程序-首页水平轮播实现 我们实现了前端(小程序)效果图的展示,这篇文章来介绍服务器端的实现. 首页书籍信息 先来回顾一下首页书籍都有哪些信息: 从下面的图片可以看 ...

  10. 全栈项目|小书架|服务器开发-NodeJS 使用 JWT 实现登录认证

    通过这篇 全栈项目|小书架|服务器开发-JWT 详解 文章我们对JWT有了深入的了解,那么接下来介绍JWT如何在项目中使用. 安装 $ npm install jsonwebtoken 生成 Toke ...

随机推荐

  1. 【ZooKeeper系列】1.ZooKeeper单机版、伪集群和集群环境搭建

    ZooKeeper安装模式主要有3种: 单机版(Standalone模式)模式:仅有一个ZooKeeper服务 伪集群模式:单机多个ZooKeeper服务 集群模式:多机多ZooKeeper服务 1 ...

  2. 使用iCamera 测试MT9M001 130w高分辨率摄像头说明

    该摄像头默认分辨率为1280*1024,即不设置任何寄存器参数,只要给该模块提供时钟,就可以输出. 在这里 我们可以通过右侧寄存器栏动态调整各寄存器 观察效果. 0x09寄存器可以调整曝光值,可以根据 ...

  3. 《JavaScript 模式》知识点小抄本(下)

    介绍 最近开始给自己每周订个学习任务,学习结果反馈为一篇文章的输出,做好学习记录. 这一周(02.25-03.03)我定的目标是<JavaScript 模式>的第七章学习一遍,学习结果的反 ...

  4. java基础-类成员访问权限控制

    一 前言 这篇文章是很基础的一文,没多大深度,对于开发人员必然是熟练于心.本篇文章的主题是为什么java要设置类成员访问级别?其原因也很简单,就是为了面向对象的封装特性:将类成员使用不同的访问级别控制 ...

  5. springboot-eureka交流机制

    一 前言 这篇文章参考了官方文档,主要是描述了eureka client 和 eureka server 之间的交流机制: peer to peer 的 对等复制交流机制: eureka 官方文档 二 ...

  6. 《Java基础知识》Java字符串详解

    本文内容: String类的概述 String类的使用步骤 String类的常用方法 本文目的: 能够使用String类的构造方法创建字符串对象 能够明确String类的构造方法创建对象,和直接赋值创 ...

  7. 超级详细Mysql安装步骤图解

    数据库忘记装了,然后今天才装上.刚开始有点蒙蔽,进入mysql官网一堆英文,小声逼逼没有学号英语的我.废话不都说,直接上图 1.输入网址 https://www.mysql.com/downloads ...

  8. 松软科技Web课堂:JavaScript 类型转换

    Number() 转换数值,String() 转换字符串,Boolean() 转换布尔值. JavaScript 数据类型 JavaScript 中有五种可包含值的数据类型: 字符串(string) ...

  9. 手摸手教你编写你人生中第一个HTML页面

    本文是<HTML5与CSS3基础语法自学教程>的第二篇,首发于[前端课湛]微信公众号. 导读:本小节主要讲解 HTML 的基础语法内容,将通过编写第一个 HTML 页面来学习 HTML 的 ...

  10. Android4.4 RIL短信接收流程分析

    最近有客户反馈Android接收不到短信,于是一头扎进RIL里面找原因.最后发现不是RIL的问题,而是BC72上报短信的格式不对,AT+CNMA=1无作用等几个小问题导致的.尽管问题不在RIL,但总算 ...