JS特效-三大家族-原生封装

一、首先,我们先了解一下三大家族及其区别(offset/scroll/client)

1.offset

1.1.offsetWidth = width+padding+border;

1.2.offsetHeight = Height+padding+border;

1.3.offsetTop:检测距离带有定位的父盒子的上面的距离,返回值不带有px

1.4.offsetLeft:检测距离带有定位的父盒子的左边的距离,返回值不带有px

1.5.offsetParent:检测最近的带有定位的父盒子的节点,返回值是一个节点。(若父盒子都没有定位,则返回body)

2.scroll

2.1.scrollWidth = width+padding;

2.2.scrollHeight = Height+padding;如果文字超出了盒子,高度为超出盒子的内容的高。不超出是盒子本身高度。IE8以下(不包括IE8) 为盒子本身内容的高度。

2.3.scrollTop:特指网页被卷去的头部的高度(这个属性的获取有兼容问题,下文将阐述)

2.4.scrollLeft:特指网页左边被卷去的宽度(一般不用)

3.client

3.1.clientWidth = width+padding;

3.2.clientHeight = Height+padding;

3.3.clientTop:获取盒子的上border的宽度

3.4.clientLeft:获取盒子的左侧border的宽度

3.5.clientX:event调用,鼠标距离浏览器可视区域的左边距离

3.6.clientY:event调用,鼠标距离浏览器可视区域的顶部距离

二、各种函数封装(动画、获取值)

1.匀速动画封装

function animate(ele,target){
        //要用定时器,先清除定时器
        //一个盒子只能有一个定时器,这样儿的话,不会和其他盒子出现定时器冲突
        //而定时器本身讲成为盒子的一个属性
        clearInterval(ele.timer);
        //我们要求盒子既能向前又能向后,那么我们的步长就得有正有负
        //目标值如果大于当前值取正,目标值如果小于当前值取负
        var speed = target>ele.offsetLeft?10:-10;
        ele.timer = setInterval(function () {
            //在执行之前就获取当前值和目标值之差
            var val = target - ele.offsetLeft;
            ele.style.left = ele.offsetLeft + speed + "px";
            //目标值和当前值只差如果小于步长,那么就不能在前进了
            //因为步长有正有负,所有转换成绝对值来比较
            if(Math.abs(val)<Math.abs(speed)){
                ele.style.left = target + "px";
                clearInterval(ele.timer);
            }
        },30)
    }

2.缓动动画封装

function animate(ele,target) {
        //要用定时器,先清定时器
        clearInterval(ele.timer);
        //定义定时器
        ele.timer = setInterval(function () {
            //获取步长
            //步长应该是越来越小的,缓动的算法。
            var step = (target-ele.offsetLeft)/10;
            //对步长进行二次加工(大于0向上取整,小于0向下取整)
            step = step>0?Math.ceil(step):Math.floor(step);
            //动画原理: 目标位置 = 当前位置 + 步长
            ele.style.left = ele.offsetLeft + step + "px";
            //检测缓动动画有没有停止
            //console.log(1);
            if(Math.abs(target-ele.offsetLeft)<=Math.abs(step)){
                //处理小数赋值
                ele.style.left = target + "px";
                clearInterval(ele.timer);
            }
        },30);
    }

3.缓动动画封装(单个属性)(这里有一个获取元素样式的封装函数getStyle,后文将给出)

       //参数变为3个
        function animate(ele,attr,target){
            //先清定时器
            clearInterval(ele.timer);
            ele.timer = setInterval(function () {
                //四部
                var leader = parseInt(getStyle(ele,attr)) || 0;
                //1.获取步长
                var step = (target - leader)/10;
                //2.二次加工步长
                step = step>0?Math.ceil(step):Math.floor(step);
                leader = leader + step;
                //3.赋值
                ele.style[attr] = leader + "px";
                 //4.清除定时器
                if(Math.abs(target-leader)<=Math.abs(step)){
                    ele.style[attr] = target + "px";
                    clearInterval(ele.timer);
                }

            },25);
        }

4.缓动动画封装(多个属性,多个属性将用json来实现)(这里有一个获取元素样式的封装函数getStyle,后文将给出)

function animate(ele,json){
            //先清定时器
            clearInterval(ele.timer);
            ele.timer = setInterval(function () {
                //开闭原则
                var bool = true;


                //遍历属性和值,分别单独处理json
                //attr == k(键)    target == json[k](值)
                for(var k in json){
                    //四部
                    var leader = parseInt(getStyle(ele,k)) || 0;
                    //1.获取步长
                    var step = (json[k] - leader)/10;
                    //2.二次加工步长
                    step = step>0?Math.ceil(step):Math.floor(step);
                    leader = leader + step;
                    //3.赋值
                    ele.style[k] = leader + "px";
                    //4.清除定时器
                    //判断: 目标值和当前值的差大于步长,就不能跳出循环
                    //不考虑小数的情况:目标位置和当前位置不相等,就不能清除定时器。
                    if(json[k] !== leader){
                        bool = false;
                    }
                }

                console.log(1);
                //只有所有的属性都到了指定位置,bool值才不会变成false;
                if(bool){
                    clearInterval(ele.timer);
                }
            },25);
        }

5.缓动动画封装(回调函数)(这里有一个获取元素样式的封装函数getStyle,后文将给出)

        //参数变为3个
        function animate(ele,json,fn){
            //先清定时器
            clearInterval(ele.timer);
            ele.timer = setInterval(function () {
                //开闭原则
                var bool = true;
                //遍历属性和值,分别单独处理json
                //attr == k(键)    target == json[k](值)
                for(var k in json){
                    //四部
                    var leader = parseInt(getStyle(ele,k)) || 0;
                    //1.获取步长
                    var step = (json[k] - leader)/10;
                    //2.二次加工步长
                    step = step>0?Math.ceil(step):Math.floor(step);
                    leader = leader + step;
                    //3.赋值
                    ele.style[k] = leader + "px";
                    //4.清除定时器
                    //判断: 目标值和当前值的差大于步长,就不能跳出循环
                    //不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。
                    if(json[k] !== leader){
                        bool = false;
                    }
                }

                console.log(1);
                //只有所有的属性都到了指定位置,bool值才不会变成false;
                if(bool){
                    clearInterval(ele.timer);
                    //所有程序执行完毕了,现在可以执行回调函数了
                    //只有传递了回调函数,才能执行
                    if(fn){
                        fn();
                    }
                }
            },25);
        }

6.获取元素样式封装

function getStyle(ele,attr){
            if(window.getComputedStyle){
                return window.getComputedStyle(ele,null)[attr];
            }
            return ele.currentStyle[attr];//兼容IE
        }

7.获取顶部或左边被卷去部分的高度或宽度封装(返回值是一个json,可以用.top获取)

function scroll(){       
            //简单封装(实际工作使用)
           return {
              "top": window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
              "left":  window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
            }
        }

8.获取屏幕可视区域的宽高的封装(返回值是一个json,可以用.width获取)

//获取屏幕可视区域的宽高
    function client(){
        if(window.innerHeight !== undefined){
            return {
                "width": window.innerWidth,
                "height": window.innerHeight
            }
        }else if(document.compatMode === "CSS1Compat"){//判断是否有dtd约束
            return {
                "width": document.documentElement.clientWidth,
                "height": document.documentElement.clientHeight
            }
        }else{
            return {
                "width": document.body.clientWidth,
                "height": document.body.clientHeight
            }
        }
    }

三、event对象的属性

1.获取event对象:在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息event对象要在事件的驱动函数里面获取,如:

document.onclick = function (event) {
             //兼容写法,兼容IE浏览器
             event = event || window.event;
}

2.event的属性:

 特别注意pageY/X这两个属性,它是表示鼠标位于整个网页页面的顶部和左侧部分的距离。

但是IE不支持这两个属性,于是就有了pageY和pageX的兼容写法:

var pagey = event.pageY || scroll().top + event.clientY;
var pagex = event.pageX || scroll().left + event.clientX;

//鼠标在页面的位置=被卷去的部分+看得见的部分

四、事件和冒泡/取消冒泡

1.事件和一些特殊属性

1.滚动事件:window.onscroll

2.屏幕滚动到具体位置:window.scrollTo(x,y)//屏幕滚动到座标x,y

3.鼠标按下:onmousedown    鼠标弹起:onmouseup

4.鼠标移动:onmousemove //如果事件是鼠标按下并移动,那这个事件的调用者应该是document(如拖拽一个盒子、模拟滚动条)

5.浏览器大小改变:window.onresize

6.检测屏幕宽度和高度:window.screen.width/window.screen.height

2.冒泡、取消冒泡、捕获、事件委托

1.冒泡就是当子盒子和父盒子都绑定了同一个事件时,如果子盒子的事件被触发,那么父盒子相应的事件会在之后触发。

2.捕获刚好和冒泡相反,子盒子的事件被触发时,父盒子相应的事件会先触发,子盒子的事件再触发。想要实现捕获可以用addEventListenner(参数1,参数2,参数3)这个方法。参数1:事件去掉on ;参数2 :调用的函数;参数3:可有可无。(不写或是false是冒泡,True是捕获。)

3.取消冒泡:

元素.事件= function (event) {
        //阻止冒泡
        event = event || window.event;

        if(event && event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble = true;//兼容IE浏览器
        }
    }

4.事件委托:事件委托就是普通的事件绑定没有办法为新创建的元素绑定事件。所以我们要使用冒泡的特性来实现事件委托。比如我们新创建了一个li标签,我们想给这个标签绑定一个事件,就可以给他的父类ul绑定一个事件,然后用event.target来获取事件触动的时候传递过来的对象,判断之后再进行操作,给出一个样例代码:

//事件委托
    ul.onclick = function (event) {
        //获取事件触动的时候传递过来的值
        event = event || window.event;
        var a = event.target?event.target:event.srcElement;//兼容IE写法
        //判断标签名,如果是li标签就绑定驱动函数
        if(a.tagName === "LI"){
            alert("我是li");
        }
    }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章