15前端学习之WebAPI(五):网页特效的三大系列(offset、client、scroll),动画函数封装,轮播图

一、网页特效:

1. 元素偏移量 offset 系列:

1.1 offset 概述:

offset: 偏移量.使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等;

  • 获得元素距离带有定位父元素的位置;
  • 获得元素自身的大小;
  • 注意: 返回的数值都不带单位;
offset系利属性 作用
element.offsetParent 返回作为该元素带有定位的父级元素(如果父级元素都没有定位, 则返回body)
element.offsetTop 返回该元素相对带有定位的父元素上方的偏移
element.offsetLeft 返回该元素相对于带有定位的父元素左边框的偏移
element.offsetWidth 返回自身包括padding, 边框,内容区的宽度(返回数值不带单位)
element.offsetHeight 返回自身包括padding, 边框,内容区的高度(返回数值不带单位)

在这里插入图片描述

1.2 offset 与 style 区别

offset

  • offset 可以得到任意样式表中的样式值;
  • offset 系列获得的数值是没有单位的;
  • offsetWidth 包含padding+border+width;
  • offsetWidth 等属性是只读属性,只能获取不能赋值;
  • 所以,想要获取元素大小位置,用offset更合适;

style

  • style 只能得到行内样式表中的样式值;
  • style.width 获得的是带有单位的字符串;
  • style.width 获得不包含padding和border 的值;
  • style.width 是可读写属性,可以获取也可以赋值;
  • 所以,想要给元素更改值,则需要用style改变;

1.3 案例:获取鼠标在盒子内的座标:

案例分析:

  • 在盒子内点击,想要得到鼠标距离盒子左右的距离。
  • 首先得到鼠标在页面中的座标(e.pageX, e.pageY)
  • 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
  • 用鼠标距离页面的座标减去盒子在页面中的距离,得到 鼠标在盒子内的座标
  • 如果想要移动一下鼠标,就要获取最新的座标,使用鼠标移动

实现:

<head>
    <title>鼠标在盒子内部的座标</title>
    <style>
        .box {
            position: relative;
            width: 280px;
            height: 280px;
            margin: 100px auto;
            background-color: pink;
            padding: 10px;
        }
    </style>
    <script>
        window.onload = function() {
            var box = document.querySelector('.box');
            box.addEventListener('mousemove', function(e) {
                var x = e.pageX - this.offsetLeft;
                var y = e.pageY - this.offsetTop;
                this.innerHTML = 'Y: ' + x + ', Y: ' + y
            })
        }
    </script>
</head>
<body>
    <div class="box">123</div>
</body>

在这里插入图片描述

1.4 案例:模态框拖拽:

功能需求:
弹出框,也称为模态框;

  • 点击弹出层,会弹出模态框, 并且显示灰色半透明的遮挡层;
  • 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层;
  • 鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动;
  • 鼠标松开,可以停止拖动模态框移动;

案例分析:

  • 点击弹出层, 模态框和遮挡层就会显示出来 display:block; 点击关闭按钮,模态框和遮挡层就会隐藏起来
  • display:none; 在页面中拖拽的原理:鼠标按下并且移动, 之后松开鼠标
  • 触发事件是鼠标按下mousedown,鼠标移动mousemove 鼠标松开 mouseup 拖拽过程:
  • 鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了 鼠标按下触发的事件源是最上面一行,就是 id
  • 为 title 鼠标的座标减去 鼠标在盒子内的座标, 才是模态框真正的位置。 鼠标按下,要得到鼠标在盒子的座标。
  • 鼠标移动,就让模态框的座标 设置为 :鼠标座标 减去盒子座标即可,注意移动事件写到按下事件里面。
  • 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除

实现:

<head>
    <title>拖拽的模态框</title>
    <style>
        .login-header {
            width: 100%;
            text-align: center;
            height: 30px;
            font-size: 24px;
            line-height: 30px;
        }
        div,
        p,
        span,
        a {
            padding: 0px;
            margin: 0px;
        }
        a {
            text-decoration: none;
            color: #000000;
        }
        .login {
            display: none;
            width: 512px;
            height: 280px;
            position: fixed;
            border: #ebebeb solid 1px;
            left: 50%;
            top: 50%;
            background: #fff;
            box-shadow: 0px 0px 20px #ddd;
            z-index: 9999;
            transform: translate(-50%, -50%);
        }
        .login-title {
            position: relative;
            width: 100%;
            margin: 10px 0px 0px 0px;
            text-align: center;
            line-height: 40px;
            height: 40px;
            font-size: 18px;
            cursor:move;
        }
        .login-input-content {
            margin-top: 20px;
        }
        .login-button {
            width: 50%;
            margin: 30px auto 0 auto;
            line-height: 40px;
            font-size: 14px;
            border: #ebebeb solid 1px;
            text-align: center;
        }
        .login-bg {
            display: none;
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0px;
            left: 0px;
            background: rgba(0, 0, 0, .3);
        }
        .login-button a{
            display: block;
        }
        .login-input input.list-input {
            float: left;
            line-height: 35px;
            height: 35px;
            width: 350px;
            border: #ebebeb solid 1px;
            text-indent: 5px;
        }
        .login-input {
            overflow: hidden;
            margin: 0px 0px 20px 0px;
        }
        .login-input label {
            float: left;
            width: 90px;
            padding-right: 10px;
            text-align: right;
            line-height: 35px;
            height: 35px;
            font-size: 14px;
        }
        .login-title span {
            position: absolute;
            font-size: 12px;
            right: -20px;
            top: -30px;
            background: #fff;
            border: #ebebeb solid 1px;
            width: 40px;
            height: 40px;
            border-radius: 20px;
        }
    </style>
    <script>
        window.onload = function() {
            var login = document.querySelector('.login');
            var bg = document.querySelector(".login-bg");
            var link = document.querySelector('#link');
            var closeBtn = document.querySelector('.login-button');

            var title = document.querySelector('.login-title');

            // 点击链接,弹出login与遮罩
            link.addEventListener('click', function() {
                login.style.display = 'block';
                bg.style.display = 'block';
            })

            // 点击关闭,隐藏
            closeBtn.addEventListener('click', function() {
                login.style.display = 'none';
                bg.style.display = 'none';
            })

            // 拖拽效果
            title.addEventListener('mousedown', function(e) {
                // 获取鼠标在盒子中的位置
                var X = e.pageX - login.offsetLeft;
                var Y = e.pageY - login.offsetTop;

                //鼠标移动
                document.addEventListener('mousemove', move)
                function move(e) {
                    login.style.left = e.pageX - X + "px";
                    login.style.top = e.pageY - Y + "px";
                }

                // 鼠标弹起, 移除移动事件
                document.addEventListener('mouseup', function(e) {
                    document.removeEventListener('mousemove', move);
                })
            })
        }
    </script>
</head>
<body>
    <div class="login-header">
        <a href="javascript:;" id="link">点击, 弹出登录框</a>
    </div>

    <div id="login" class="login">
        <div id="title" class="login-title">
            会员登录
            <span>
                <a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a>
            </span>
        </div>
        <div class="login-input-content">
            <div class="login-input">
                <label for="username">用户名</label>
                <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
            </div>
            <div class="login-input">
                <label for="password">密码</label>
            <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
            </div>
        </div>
        <div id="loginBtn" class="login-button">
            <a href="javascript:void(0);" id="login-button-submit">登录会员</a>
        </div>
    </div>
    <!-- 遮罩层 -->
    <div id="bg" class="login-bg"></div>
</body>

在这里插入图片描述

1.4 案例:仿京东放大镜:

需求:

  • 整个案例可以分为三个功能模块
  • 鼠标经过小图片盒子, 黄色的遮挡层 和 大图片盒子显示,离开隐藏2个盒子功能
  • 黄色的遮挡层跟随鼠标功能。
  • 移动黄色遮挡层,大图片跟随移动功能

案例分析:

  • 黄色的遮挡层跟随鼠标功能;
  • 把鼠标座标给遮挡层不合适。因为遮挡层座标以父盒子为准;
  • 首先是获得鼠标在盒子的座标;
  • 之后把数值给遮挡层做为left 和top值;
  • 此时用到鼠标移动事件,但是还是在小图片盒子内移动;
    发现,遮挡层位置不对,需要再减去盒子自身高度和宽度的一半;
  • 遮挡层不能超出小图片盒子范围;
  • 如果小于零,就把座标设置为0;
  • 如果大于遮挡层最大的移动距离,就把座标设置为最大的移动距离;
  • 遮挡层的最大移动距离:小图片盒子宽度 减去 遮挡层盒子宽度;
    实现:
window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的座标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是mask 的最终 left 和top值了
        // (3) mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4) 如果x 座标小于了0 就让他停在0 的位置
        // 遮挡层的最大移动距离
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        // 大图
        var bigIMg = document.querySelector('.bigImg');
        // 大图片最大移动距离
        var bigMax = bigIMg.offsetWidth - big.offsetWidth;
        // 大图片的移动距离 X Y
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        bigIMg.style.left = -bigX + 'px';
        bigIMg.style.top = -bigY + 'px';
    })
})

2. 元素可视区 client 系列:

2.1 client概述

client 翻译过来就是客户端, 使用 client 系列的相关属性来获取元素可视区的相关信息. 通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等.

client系列属性 说明
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括padding, 内容区的宽度, 不含边框,返回数值不带单位
element.clientHeight 返回自身包括padding, 内容区的高度, 不含边框,返回数值不带单位

在这里插入图片描述

2.2. 淘宝 flexible.js 源码分析

(function flexible(window, document) {
    // 获取的html 的根元素
    var docEl = document.documentElement
        // dpr 物理像素比
    var dpr = window.devicePixelRatio || 1

    // adjust body font size  设置body 的字体大小
    function setBodyFontSize() {
        // 如果页面中有body 这个元素 就设置body的字体大小
        if (document.body) {
            document.body.style.fontSize = (12 * dpr) + 'px'
        } else {
            // 如果页面中没有body 这个元素,则等着 页面主要的DOM元素加载完毕再去设置body
            // 的字体大小
            document.addEventListener('DOMContentLoaded', setBodyFontSize)
        }
    }
    setBodyFontSize();

    // set 1rem = viewWidth / 10    设置html 元素的文字大小
    function setRemUnit() {
        var rem = docEl.clientWidth / 10
        docEl.style.fontSize = rem + 'px'
    }

    setRemUnit()

    // reset rem unit on page resize  当页面尺寸大小发生变化的时候,要重新设置下rem 的大小
    window.addEventListener('resize', setRemUnit)
        // pageshow 是重新加载页面触发的事件
    window.addEventListener('pageshow', function(e) {
        // e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
        if (e.persisted) {
            setRemUnit()
        }
    })

    // detect 0.5px supports  有些移动端的浏览器不支持0.5像素的写法
    if (dpr >= 2) {
        var fakeBody = document.createElement('body')
        var testElement = document.createElement('div')
        testElement.style.border = '.5px solid transparent'
        fakeBody.appendChild(testElement)
        docEl.appendChild(fakeBody)
        if (testElement.offsetHeight === 1) {
            docEl.classList.add('hairlines')
        }
        docEl.removeChild(fakeBody)
    }
}(window, document))
  • 立即执行函数 (function(){})() 或者 (function(){}())
    • 主要作用: 创建一个独立的作用域。 避免了命名冲突问题
  • 下面三种情况都会刷新页面都会触发 load 事件。
    • a标签的超链接
    • F5或者刷新按钮(强制刷新)
    • 前进后退按钮

但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。

所以此时后退按钮不能刷新页面。

此时可以使用 pageshow事件来触发。,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件

注意: 这个事件给window添加

3.元素滚动 scroll 系列

3.1. scroll 概述

scroll 翻译过来就是滚动的, 使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等

scroll系列属性 作用
element.scrollTop 返回被卷去的上侧距离
element.scrollLeft 返回被卷去的左侧距离
lement.scrollWidth 返回自身实际的宽度, 不含边框, 返回数值不带单位
element.scrolHeight 返回自身实际的高度,不含边框, 返回数值不带单位

在这里插入图片描述

3.2. 页面被卷去的头部

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条. 当滚动条向下滚动时,页面上面被隐藏掉的高度,就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件.

3.3.案例:仿淘宝固定右侧侧边栏

  • 原先侧边栏是绝对定位
  • 当页面滚动到一定位置,侧边栏改为固定定位
  • 页面继续滚动,会让 返回顶部显示出来

案例分析:

  • 需要用到页面滚动事件 scroll 因为是页面滚动,所以事件源是document;
  • 滚动到某个位置,就是判断页面被卷去的上部值;
  • 页面被卷去的头部:可以通过window.pageYOffset 获得 如果是被卷去的左侧window.pageXOffset;
  • 注意,元素被卷去的头部是element.scrollTop , 如果是页面被卷去的头部 则是 window.pageYOffset;
  • 其实这个值 可以通过盒子的 offsetTop可以得到,如果大于等于这个值,就可以让盒子固定定位了;

实现:

<head>
    <title>仿淘宝固定侧边栏</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: pink;
        }
        .w {
            width: 1200px;
            margin:  10px auto;
        }
        .header {
            height: 150px;
            background-color: purple;
        }
        .banner {
            height: 250px;
            background-color: skyblue;
        }
        .main {
            height: 1000px;
            background-color: yellowgreen;
        }
        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
    <script>
        window.onload = function() {
            // 1. 获取元素
            var sliderbar = document.querySelector('.slider-bar');
            var banner = document.querySelector('.header');
            // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
            var bannerTop = banner.offsetTop
            // 当侧边栏固定定位之后应该变化的数据
            var sliderbarTop = sliderbar.offsetTop - bannerTop;
            // 获取mian主体元素
            var main = document.querySelector('.main');
            var goBack = document.querySelector('.goBack');
            var mainTop = main.offsetTop;
            // 2. 页面滚动事件 scroll
            document.addEventListener('scroll', function(e) {
                // window.pageYOffset 页面被卷去的头部
                // console.log(window.pageYOffset);
                // 3. 当页面被卷去的头部大于bannerTop时, 侧边栏要改为固定定位
                if(window.pageYOffset >= bannerTop) {
                    sliderbar.style.position = 'fixed';
                    sliderbar.style.top = sliderbarTop + 'px';
                } else {
                    sliderbar.style.position = 'absolute';
                    sliderbar.style.top = '300px';
                }
                
                // 4. 当页面滚动到main盒子, 就显示goback模块
                if (window.pageYOffset >= mainTop) {
                    goBack.style.display = 'block';
                } else {
                    goBack.style.display = 'none';
                }
            })
        }
    </script>
</head>
<body>
    <div class="slider-bar">
        <span class="goBack">返回顶部</span>
    </div>
    <div class="header w">头部</div>
    <div class="banner w">banner区域</div>
    <div class="main w">主体区域</div>
</body>

在这里插入图片描述

3.4页面被卷去的头部兼容性解决方案

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

  • 声明了 DTD,使用 document.documentElement.scrollTop
  • 未声明 DTD,使用 document.body.scrollTop
  • 新方法 window.pageYOffset和 window.pageXOffset,IE9 开始支持
function getScroll() {
    return {
      left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
      top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    };
 } 
使用的时候  getScroll().left

4. 三大系列总结:

三大系列大小对比 作用
element.offsetWidth 返回自身包括padding, 边框,内容区的宽度; 返回数值不带单位
element.clientWidth 返回自身包括padding, 内容区的宽度; 返回数值不带单位
element.scrollWidth 返回自身实际的宽度, 不含边框; 返回数值不带单位

他们主要用法:

  • 1.offset系列 经常用于获得元素位置 offsetLeft offsetTop;
  • 2.client经常用于获取元素大小 clientWidth clientHeight;
  • 3.scroll 经常用于获取滚动距离 scrollTop scrollLeft;
  • 4.注意页面滚动的距离通过 window.pageXOffset 获得;

5. mouseenter 和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter 事件;
  • 类似 mouseover,它们两者之间的差别是;
  • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发;
  • 之所以这样,就是因为mouseenter不会冒泡;
  • 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡;

二、 动画函数封装:

1. 动画实现原理:

核心原理:通过定时器 setInterval() 不断移动盒子位置;

实现步骤:

  • 获得盒子当前位置;
  • 让盒子在当前位置加上1个移动距离;
  • 利用定时器不断重复这个操作;
  • 加一个结束定时器的条件;
  • 注意此元素需要添加定位(absolute | relative),才能使用element.style.left;

2. 动画函数给不同元素记录不同定时器

如果多个元素都使用这个动画函数, 每次都要var 声明定时器. 可以给不同的元素使用不同的定时器(自己专门用自己的定时器)

核心原理:利用 JS 是一门动态语, 可以很方便的给当前对象添加属性;

 function animate(obj, target) {
  	// 当不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
    // 解决方案就是 让元素只有一个定时器执行
    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        if (obj.offsetLeft >= target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
        }
        obj.style.left = obj.offsetLeft + 1 + 'px';

    }, 30);
}

3. 缓动动画:

3.1 缓动效果原理:

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来

思路:

  • 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来;
  • 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离步长;
  • 停止的条件是: 让当前盒子位置等于目标位置就停止定时器;
  • 注意步长值需要取整;

3.2 动画函数多个目标值之间移动

可以让动画函数从 800 移动到 500。
当点击按钮时候,判断步长是正值还是负值:

  • .如果是正值,则步长往大了取整;
  • 如果是负值,则步长 向小了取整;

3.3 动函数添加回调函数

回调函数原理: 函数可以作为一个参数. 将这个函数作为参数传到另一个函数里面, 当那个函数执行完之后, 再执行传进去的这个函数,这个过程就叫做回调;

回调函数写的位置:定时器结束的位置;

动画完整版代码:

function animate(obj, target, callback) {
    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
    }, 15);
}

3.4 动画函数封装到js文件中:

**案例: 滑出 **
在这里插入图片描述在这里插入图片描述

<head>
    <title>外部引用动画函数</title>
    <style>
        .sliderbar {
            position: fixed;    /* 固定定位 */
            right: 0;
            top: 200px;
            width: 40px;
            height: 40px;
            text-align: center;
            line-height: 40px;
            cursor:pointer;
            color: #fff;
        }
        .con {
            position: absolute;
            left: 0;
            top: 0;
            width: 200px;
            height: 40px;
            background-color: purple;
            z-index: -1;
        }
    </style>
    <script src="04_animate.js"></script>
    <script>
        window.onload = function() {
            var sliderbar = document.querySelector(".sliderbar");
            var con = document.querySelector(".con");

            sliderbar.addEventListener("mouseenter", function() {
                animate(con, -160, function() {
                    sliderbar.children[0].innerHTML = "→";
                })
            });
            sliderbar.addEventListener("mouseleave", function() {
                animate(con, 0, function() {
                    sliderbar.children[0].innerHTML = "←";
                })
            })
        }
    </script>
</head>
<body>
    <div class="sliderbar">
        <span></span>
        <div class="con">问题反馈</div>
    </div>
</body>

3.5 案例: js实现动态轮播图:

在这里插入图片描述
轮播图也称为焦点图,是网页中比较常见的网页特效;

  • 1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮;
  • 2.点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理;
  • 3.图片播放的同时,下面小圆圈模块跟随一起变化;
  • 4.点击小圆圈,可以播放相应图片;
  • 5.鼠标不经过轮播图,轮播图会自动播放图片.

js/lunbo.js

//  1. 获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');

var focusWidth = focus.offsetWidth;
// 2. 鼠标经过focus就显示隐藏左右按钮
focus.addEventListener('mouseenter', function() {
    arrow_l.style.display = 'block';
    arrow_r.style.display = 'block';
    clearInterval(timer);
    timer = null;
});
focus.addEventListener('mouseleave', function() {
    arrow_l.style.display = 'none';
    arrow_r.style.display = 'none';
    timer = setInterval(function() {
        //手动调用点击事件
        arrow_r.click();
    }, 2000);
});

// 3. 动态生成圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');

for(var i = 0; i < ul.children.length; i++) {
    var li = document.createElement('li');
    // 设置索引号
    li.setAttribute('index', i);
    ol.appendChild(li);

    // 4. 给圆圈设置排他
    li.addEventListener('click', function() {
        for(var i = 0; i < ol.children.length; i++){
            ol.children[i].className = '';
        }
        this.className = 'current';
        // 5. 点击小圆圈,移动图片 当然移动的是 ul
        var index = this.getAttribute('index');
        // 定位index
        num = circle = index;
        animate(ul, -index * focusWidth);
    })
}
// 默认选中第一个
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到ul 最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);

// 7. 点击按钮, 图片滚动一张
var num = 0;
var circle = 0;

// 节流阀
var flag = true;
arrow_r.addEventListener('click', function() {
    if (flag) {
        flag = false;
        // // 如果走到了最后复制的一张图片,此时的ul 要快速复原 left 改为 0
        if(num == ul.children.length - 1) {
            ul.style.left = 0;
            num = 0;
        }
        num ++;
        animate(ul, -num * focusWidth, function() {
            flag = true;
        });
        circle ++;
        if(circle == ol.children.length){
            circle = 0;
        }
        // 调用函数
        circleChange();
    }
})
arrow_l.addEventListener('click', function() {
    if (flag) {
        flag = false;
        // // 如果走到了最后复制的一张图片,此时的ul 要快速复原 left 改为 0
        if(num == 0) {
            num = ul.children.length - 1;
            ul.style.left = -num * focusWidth + 'px';
        }
        num --;
        animate(ul, -num * focusWidth, function() {
            flag = true;
        });
        circle --;
        circle = circle < 0 ? ol.children.length - 1 : circle;
        // 调用函数
        circleChange();
    }
})
function circleChange() {
    // 先清除其余小圆圈的current类名
    for(var i = 0; i < ol.children.length; i++){
        ol.children[i].className = '';
    }
    // 留下当前的小圆圈的current类名
    ol.children[circle].className = 'current';
}
// 10. 自动播放轮播图
var timer = setInterval(function() {
    //手动调用点击事件
    arrow_r.click();
}, 2000);

css/base.css

/*清除元素默认的内外边距  */
* {
    margin: 0;
    padding: 0
}
/*让所有斜体 不倾斜*/
em,
i {
    font-style: normal;
}
/*去掉列表前面的小点*/
li {
    list-style: none;
}
/*图片没有边框   去掉图片底侧的空白缝隙*/
img {
    border: 0;  /*ie6*/
    vertical-align: middle;
}
/*让button 按钮 变成小手*/
button {
    cursor: pointer;
}
/*取消链接的下划线*/
a {
    color: #666;
    text-decoration: none;
}

a:hover {
    color: #e33333;
}

button,
input {
    font-family: 'Microsoft YaHei', 'Heiti SC', tahoma, arial, 'Hiragino Sans GB', \\5B8B\4F53, sans-serif;
    /*取消轮廓线 蓝色的*/
    outline: none;
}

body {
    background-color: #fff;
    font: 12px/1.5 'Microsoft YaHei', 'Heiti SC', tahoma, arial, 'Hiragino Sans GB', \\5B8B\4F53, sans-serif;
    color: #666
}

.hide,
.none {
    display: none;
}
/*清除浮动*/
.clearfix:after {
    visibility: hidden;
    clear: both;
    display: block;
    content: ".";
    height: 0
}
.clearfix {
    *zoom: 1
}

css/lunbo.css

.focus {
    position: relative;
    width: 721px;
    height: 455px;
    background-color: purple;
    overflow: hidden;

    margin: 100px auto;
}
.focus ul {
    position: absolute;
    top: 0;
    left: 0;
    width: 600%;
}
.focus ul li {
    float: left;
}
.arrow-l,
.arrow-r{
    display: none;
    position: absolute;
    top:50%;
    width: 24px;
    height: 40px;
    margin-top: -20px;
    line-height: 40px;
    text-align: center;
    font-size: 28px;
    color: #ffffff;
    background-color: rgba(0, 0, 0, .3);
    z-index: 2;
}
.arrow-l {
    left: 5px;
}
.arrow-r {
    right: 5px;
}
.circle {
    position: absolute;
    bottom: 10px;
    left: 50%;
}
.circle li {
    float: left;
    width: 8px;
    height: 8px;
    border: 2px solid rgba(255, 255, 255, .5);
    margin: 0 3px;
    border-radius: 50%; 
    cursor: pointer;
}
.current {
    background-color: #fff;
}

index.html

<head>
    <title>05_轮播图</title>
    <link rel="stylesheet" href="./css/base.css">
    <link rel="stylesheet" href="./css/lunbo.css">
    <script src="./js/animate.js"></script>
    <script src="./js/lunbo.js"></script>
</head>
<body>
    <div class="focus">
        <a href="javascript:;"  class="arrow-l">&lt;</a>
        <a href="javascript:;"  class="arrow-r">&gt;</a>
        <ul>
            <li>
                <a href="#">
                    <img src="image/focus.jpg" alt="">
                </a>
            </li>
            <li>
                <a href="#">
                    <img src="image/focus1.jpg" alt="">
                </a>
            </li>
            <li>
                <a href="#">
                    <img src="image/focus2.jpg" alt="">
                </a>
            </li>
            <li>
                <a href="#">
                    <img src="image/focus3.jpg" alt="">
                </a>
            </li>
        </ul>
        <!-- 小圆圈 -->
        <ol class="circle">

        </ol>
    </div>
</body>

3.6 节流阀

防止轮播图按钮连续点击造成播放过快;
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发;
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数;

开始设置一个变量var flag= true;
If(flag){flag = false; do something}       关闭水龙头
利用回调函数动画执行完毕, flag = true     打开水龙头
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章