zoukankan      html  css  js  c++  java
  • 我在 impress.js 中学到的小套路

    我在 impress.js 中学到的小套路

    写在开篇

    作为了一个自学 JavaScript 才一个月的新手,前几天“妄图”研究 jQuery-3.1.0 源码,结果自然是被虐得死去活来。机缘巧合之下,遇到了 impress.js ,代码量只有 jQ 的十分之一,看起来挺好下手,研究了两天,勉强弄懂了其中的原理。于是写下此文,记录我在 impress.js 中学到的小套路。

    附上 impress.js github 链接。

    impress.js 简介

    套路开始

    如何使用 JS 为某个元素添加多个样式?

    一般情况下,在 JS 中改变元素的样式都是通过元素的 style 属性,比如这样:

    element.style.width = 100 + "px";
    

    添加一个样式还好,如果添加多个呢,比如同时添加 width 和 height ,难道要这样?

    element.style.width = 100 + "px";
    element.style.height = 50 + "px";
    

    非也,同时添加多个样式,可以通过函数来解决,如下:

    /**
     * 为指定的元素添加一组 CSS 样式
     * @param  ele    指定的元素
     * @param  props  一组 CSS 属性和值,JSON 的形式,属性名和属性值都要加引号
     * @return        返回指定的元素
     */
    var css = function(ele, props) {
        var key, pkey;
        for (key in props) {
            if (props.hasOwnProperty(key)) {
                pkey = pfx(key);
                if (pkey !== null) {
                    ele.style[pkey] = props[key];
                }
            }
        }
        return ele;
    }
    

    如何使用 JS 为 CSS 属性添加当前浏览器能够识别的前缀?

    比如 transform ,为了让他在各个浏览器上都支持,在 CSS 中就得写成这样:

    #box {
    	        transform: rotateX(45deg);
    	-webkit-transform: rotateX(45deg);
    	   -moz-transform: rotateX(45deg);
    	    -ms-transform: rotateX(45deg);
    	     -o-transform: rotateX(45deg);
    }
    

    可是这样写真的很麻烦啊,怎么办?用函数自动添加!

    /**
     * 在需要的时候,为 CSS 属性添加当前浏览器能够识别的前缀
     * @param  prop  一定要记住,参数是一个字符串,所以传入的 CSS 属性一定要加引号
     * @return       返回当前浏览器能够识别的 CSS 属性
     */
    var pfx = (function() {
        var prefixes = "Moz Webkit O ms".split(" ");
        var style = document.createElement("dummy").style;
        var memory = {};
    
        return function(prop) {
            var uProp = prop.charAt(0).toUpperCase() + prop.slice(1);
            var props = (prop + " " + prefixes.join(uProp + " ") + uProp).split(" ");
    
            memory[prop] = null;
            for (var i in props) {
                if (style[props[i]] !== undefined) {
                    memory[prop] = props[i];
                    break;
                }
            }
            return memory[prop];
        }
    })();
    

    把上面的 css() 和 pfx() 两个函数结合起来,就可以方便地在 JS 中为元素添加样式啦。

    如何通过 hash 值定位元素?

    首先元素必须得有一个 id ,这个简单,遍历一下,没有 id 的加上 id 就可以了,在此不表。

    有了 id 之后,就可以改变 hash 值啦,如下:

    h3[i].onclick = function(event) {
    	window.location.hash = event.target.id;
    }
    

    改变 hash 值有什么用?当然有用,假如 hash 值是 #h3-3 ,就会自动跳转到 id 为 h3-3 的元素上面,然后转念一想,这不是可以生成目录吗?锚点什么的,out 啦!

    基本思想是这样子:首先,保证每一个标题标签都有一个 id ,然后遍历所有的标签,找到标题标签,提取出来,利用自定义列表生成一个目录;目录下的每一项也都有一个 id ,为 # + 对象标题的 id 。当点击目录上的标题时,将这个标题的 id 赋给 hash 值,就 OK 啦。

    附上本文生成目录的 JS 代码,仅供参考!

    window.onload = function() {
    	addId();
    	createContents();
    	createLink();
    }
    
    // 保证所有的标题标签都有 id ,以 h2-* h3-* 的格式
    var addId = function() {
    	var h3 = document.querySelectorAll("h3");
    	for (var i = 0; i < h3.length; i++) {
    		if( !h3[i].id ) {
    			h3[i].id = "h3-" + (i + 1);
    		}
    	}
    	var h2 = document.querySelectorAll("h2");
    	for (var i = 0; i < h2.length; i++) {
    		if( !h2[i].id ) {
    			h2[i].id = "h2-" + (i + 1);
    		}
    	}
    };
    
    // 创建目录
    var createContents = function() {
    	// 创建 dl 标签
    	var dl = document.createElement("dl");
    	dl.id = "dl";
    	document.body.appendChild(dl);
    
    	// 遍历所有的标签,找到 h2 和 h3 
    	var tags = document.getElementsByTagName("*");
    	for (var i = 0; i < tags.length; i++) {
    		// 如果是 h2 ,则将标题保存在 dt 标签中,并给这个 dt 标签一个 id ,格式为 #h2-*	
    		if(tags[i].nodeName.toLowerCase() === "h2") {
    			var dt = document.createElement("dt");
    			dt.id = "#" + tags[i].id;
    			var dt_text = document.createTextNode(tags[i].firstChild.nodeValue);
    			dt.appendChild(dt_text)
    			dl.appendChild(dt);
    		}
    		// 如果是 h3 ,则将标题保存在 dd 标签中,并给这个 dd 标签一个 id ,格式为 #h3-*	
    		if(tags[i].nodeName.toLowerCase() === "h3") {
    			if(tags[i].firstChild) {
    				var dd = document.createElement("dd");
    				dd.id = "#" + tags[i].id;
    				var dd_text = document.createTextNode(tags[i].firstChild.nodeValue);
    				dd.appendChild(dd_text);
    				dl.appendChild(dd);
    			}
    		}
    	}
    }
    
    // 定义目录与标题的联系
    // 点击目录中的某一项,将这一项的 id 赋给 hash 值,通过 hash 值的改变定位到相应的标题
    function createLink() {
    	var dl = document.getElementById("dl");
    	var tags = dl.getElementsByTagName("*");
    	for (var i = 0; i < tags.length; i++) {
    		tags[i].onclick = function() {
    			window.location.hash = this.id;
    		}
    	}
    }
    

    自定义属性 data 与 dataset 对象

    在 impress.js 中,充分利用了 data 来定义各个 step 的样式。首先在 HTML 中的元素标签上定义 data 属性,然后在 JS 中通过 dataset 对象读取,然后 通过 css() 函数为元素添加这些样式。虽然最终的结果与直接在 CSS 样式表中定义的一样,不过这能通过 JS 对样式进行更多控制。

    举个栗子,下面是 HTML :

    第三幕

    然后在 JS 中通过 el.dataset 获取这些属性,长这样:

    var data = el.dataset = {
    	      x : 0,
    	      z : 1000,
    	rotateY : 45,
    	rotateZ : 80,
    	  scale : 1
    }
    

    然后就可以通过 data 获取具体的属性值,就像这样:

    step = {
        translate: {
            x: toNumber( data.x ),
            y: toNumber( data.y ),
            z: toNumber( data.z )
        },
        rotate: {
            x: toNumber( data.rotateX ),
            y: toNumber( data.rotateY ),
            z: toNumber( data.rotateZ || data.rotate )
        },
        scale: toNumber( data.scale, 1 ),
        el: el
    };
    

    然后调用 css() 函数将这些样式同时添加到元素上。

    css( el, {
        position: "absolute",
        transform: "translate(-50%,-50%)" +
                   translate( step.translate ) +
                   rotate( step.rotate ) +
                   scale( step.scale ),
        transformStyle: "preserve-3d"
    } );
    

    鉴于 step.translate 和 step.rotate 是一个对象,为了解析它们的值,还得需要两个辅助函数,如下:

    /**
     * 设置 3D 转换元素的 translate 值
     * @param  t 位移值,以对象字面量的形式,属性值不需要带单位
     * @return   返回 "translate3d() "
     */
    var translate = function(t) {
        return "translate3d(" + t.x + "px," + t.y + "px," + t.z +"px) ";
    }
    
    /**
     * 设置 3D 转换元素的 rotate 值
     * @param  r 旋转值,以对象字面量的形式,属性值不需要带单位
     * @return   返回 "rotateX() rotateY() rotateZ() " 
     */
    var rotate = function( r, revert ) {
        var rX = " rotateX(" + r.x + "deg) ",
            rY = " rotateY(" + r.y + "deg) ",
            rZ = " rotateZ(" + r.z + "deg) ";
    
        return revert ? rZ + rY + rX : rX + rY + rZ;
    };
    

    自定义事件并立即触发

    在 impress.js 的初始化函数中,函数的最后,是这样的:

    triggerEvent( root, "impress:init", { api: roots[ "impress-root-" + rootId ] } );
    

    调用的 triggerEvent() 函数长这样:

    /**
     * 自定义事件并立即触发
     * @param  el        触发事件的元素
     * @param  eventName 事件名
     * @param  detail    事件信息
     */
    var triggerEvent = function( el, eventName, detail ) {
        var event = document.createEvent( "CustomEvent" );
        // 事件冒泡,并且可以取消冒泡
        event.initCustomEvent( eventName, true, true, detail );
        el.dispatchEvent( event );
    };
    

    我个人理解,在初始化完成之后,自定义了一个 impress:init 事件,并立即触发,然后在 root 元素接收该事件,并以此为依据,执行另外的任务。

    同样在 impress.js 中,每一幕的进入在经过一段时间的延迟之后,都会自定义一个 impress:stepenter 事件并触发,在 root 上接收到这个事件,从而改变 hash 值。

  • 相关阅读:
    博客园
    未释放的已删除文件
    ssh连接缓慢
    剑指 Offer 38. 字符串的排列
    剑指 Offer 37. 序列化二叉树
    剑指 Offer 50. 第一个只出现一次的字符
    剑指 Offer 36. 二叉搜索树与双向链表
    剑指 Offer 35. 复杂链表的复制
    剑指 Offer 34. 二叉树中和为某一值的路径
    剑指 Offer 33. 二叉搜索树的后序遍历序列
  • 原文地址:https://www.cnblogs.com/cc156676/p/5837511.html
Copyright © 2011-2022 走看看