• 《移动WEB前端高级开发实践@www.java1234.com.pdf》【2】


    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 提供了源文件代码到目标文件代码的对应关系


  • 相关阅读:
    The Collections Module内建collections集合模块
    生成器接受和返还功能在执行过程中的详解以及生成器实现协同
    写python中的装饰器
    windows下载Mysql-python
    分别用单线程和多线程运行斐波那契、阶乘和累加和
    TCP客户端和服务器间传输数据遇到的TypeError: a bytes-like object is required, not 'str'问题
    python的property属性
    python的伪私有属性
    使用栈实现中缀表达式转为后缀表达式和后缀表达式的求解
    公众帐号如何向用户发送emoji表情(php版,附emoji编码表)
  • 原文地址:https://www.cnblogs.com/cx2016/p/12063851.html
Copyright © 2020-2023  润新知