zl程序教程

您现在的位置是:首页 >  后端

当前栏目

JS学习第6天——PC端网页特效(元素偏移量offset、元素可视区client、元素滚动scroll、动画函数封装、节流阀、网页轮播图案例、mouseenter与mouseover区别)

2023-09-11 14:21:26 时间

PC端网页特效

一、元素偏移量offset系列

offset相关属性可以动态的得到该元素的位置(偏移)、大小等

offset系列常用属性作用
element.offsetParent返回该元素带有定位的父级元素,如果父级没定位,则返回body
element.offsetTop返回元素相对带有定位父元素上方的偏移
element.offsetLeft返回元素相对带有定位父元素左边框的偏移
element.offsetWidth返回自身宽度(包括padding、边框)
element.offsetHeight返回自身高度(包括padding、边框)

注意:返回值不带单位

offset与style的区别

offsetstyle
可以得到任意样式表中的样式值只能得到行内样式表中的样式值(行内样式表:写在元素自身的样式)
获得的数值无单位获得带有单位的字符串
包含padding+border+width不包含padding和border
只能读不能赋值能读也能赋值

因此:获取元素的位置,用offset更适合;给元素改值,使用style更合适

二、元素可视区client系列

client系列相关属性可以动态得到元素的边框大小、元素大小等

client系列常用属性作用
element.clientTop返回元素上边框的大小
element.clientLeft返回元素左边框的大小
element.clientWidth返回自身padding、内容区的宽度,不包含边框
element.clientHeight返回自身padding、内容区的高度,不包含边框

立即执行函数

(function() {})() 
// 或者 
(function(){}())

主要作用: 创建一个独立的作用域。 避免了命名冲突问题

三、元素滚动scroll系列

scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等

scroll系列常用属性作用
element.scrollTop返回被卷去的上侧距离
element.scrollLeft返回被卷去的左侧距离
element.clientWidth返回自身的实际宽度、不包含边框
element.clientHeight返回自身的实际高度、不包含边框

1、onscroll事件

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

滚动窗口至文档中的特定位置:window.
注意,里面的x和y 不跟单位,直接写数字

2、仿淘宝固定右侧侧边栏(小案例)

① 需要用到页面滚动事件srcoll,因此事件源为document
页面被卷去的头部:window.pageYOffset 获得,如果是被卷去的左侧window.pageXOffset
滚到窗口到文档的特定位置window.scroll(x, y) ,x和y可以不带单位

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</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: 3000px;
        background-color: yellowgreen;
      }

      span {
        display: none;
        position: absolute;
        bottom: 0;
      }
    </style>
  </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>
    <script>
      var banner = document.querySelector(".banner");
      var sliderBar = document.querySelector(".slider-bar");
      var bannerTop = banner.offsetTop;
      var sliderBarTop = sliderBar.offsetTop;
      var main = document.querySelector(".main");
      var goBack = document.querySelector(".goBack");
      var mainTop = main.offsetTop;
      document.addEventListener("scroll", function (e) {
        if (window.pageYOffset >= bannerTop) {
          sliderBar.style.position = "fixed";
          sliderBar.style.top = sliderBarTop - bannerTop + "px";
        } else {
          sliderBar.style.position = "absolute";
          sliderBar.style.top = "300px";
        }
        if (window.pageYOffset >= mainTop) {
          goBack.style.display = "block";
        } else {
          goBack.style.display = "none";
        }
      });

      goBack.addEventListener("click", function () {
        window.scroll(0, 0);
      });
    </script>
  </body>
</html>

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

① 声明了 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

四、动画函数封装

1、多个目标值之间移动

实现div在500到800之间移动

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      div {
        width: 100px;
        height: 100px;
        background-color: antiquewhite;
      }
    </style>
  </head>

  <body>
    <button class="btn800">点击到800</button>
    <button class="btn500">点击到500</button>
    <div></div>
  </body>
  <script>
    var div = document.querySelector("div");
    var btn500 = document.querySelector(".btn500");
    var btn800 = document.querySelector(".btn800");
    function animate(obj, target) {
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          clearInterval(obj.timer);
        }
        obj.style.marginLeft = obj.offsetLeft + step + "px";
      }, 30);
    }

    btn500.addEventListener("click", function () {
      // 调用函数
      animate(div, 500);
    });
    btn800.addEventListener("click", function () {
      // 调用函数
      animate(div, 800);
    });
  </script>
</html>

2、动画函数添加回调函数

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

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

在上面案例的基础上,当div到达800时变背景色

  <script>
    var div = document.querySelector("div");
    var btn500 = document.querySelector(".btn500");
    var btn800 = document.querySelector(".btn800");
    function animate(obj, target.callback) {
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          clearInterval(obj.timer);
          if(callback){
          callback() // 调用回调函数
          }
        }
        obj.style.marginLeft = obj.offsetLeft + step + "px";
      }, 30);
    }

    btn500.addEventListener("click", function () {
      // 调用函数
      animate(div, 500);
    });
    btn800.addEventListener("click", function () {
      // 调用函数
      animate(div, 800.function(){
      	btn800.style.backgroundColor ="red"
      });
    });
  </script>

五、常见网页特效案例

1、节流阀

目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

// 开始设置一个变量 
var flag = true;
if(flag) {
	flag = false;  // 关闭水龙头
	do something
} 
// 利用回调函数 动画执行完毕
flag = true  // 打开水龙头

用途:防止轮播图按钮连续点击造成播放过快

2、网页轮播图案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .focus {
        position: relative;
        width: 721px;
        height: 455px;
        overflow: hidden;
      }
      .focus ul {
        position: absolute;
        top: 0;
        left: 0;
        width: 600%;
      }
      ul,
      li {
        margin: 0;
        padding: 0;
        list-style: none;
      }
      .arrow-r,
      .arrow-l {
        position: absolute;
        top: 50%;
        margin-top: -20px;
        width: 24px;
        height: 40px;
        background: rgba(0, 0, 0, 0.3);
        text-align: center;
        line-height: 40px;
        color: #fff;
        font-size: 18px;
        z-index: 2;
      }
      .arrow-r {
        right: 0;
      }
      li {
        float: left;
      }
      .circle {
        position: absolute;
        bottom: 10px;
        left: 50px;
      }

      .circle li {
        float: left;
        width: 8px;
        height: 8px;
        /*background-color: #fff;*/
        border: 2px solid rgba(255, 255, 255, 0.5);
        margin: 0 3px;
        border-radius: 50%;
        /*鼠标经过显示小手*/
        cursor: pointer;
      }
      .current {
        background-color: #fff;
      }
    </style>
  </head>
  <body>
    <div class="focus fl">
      <!-- 左侧按钮 -->
      <a href="javascript:;" class="arrow-l"> > </a>
      <!-- 右侧按钮 -->
      <a href="javascript:;" class="arrow-r"> < </a>
      <!-- 核心的滚动区域 -->
      <ul>
        <li >
          <a href="#"><img src="upload/focus.jpg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="upload/focus1.jpg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="upload/focus2.jpg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="upload/focus3.jpg" alt="" /></a>
        </li>
      </ul>
      <!-- 小圆圈 -->
      <ol class="circle"></ol>
    </div>
  </body>
  <script>
    var ul = document.querySelector("ul");
    var lis = document.querySelectorAll("li");
    // 封装一个动画
    function animate(obj, target, callback) {
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          callback && callback();
        }
        obj.style.left = obj.offsetLeft + step + "px";
      }, 15);
    }
    window.addEventListener("load", function () {
      var arrow_l = document.querySelector(".arrow-l");
      var arrow_r = document.querySelector(".arrow-r");
      var focus = document.querySelector(".focus");
      var focusWidth = focus.offsetWidth;
      
      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);
      });
      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);
        // 直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener("click", function () {
          for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = "";
          }
          this.className = "current";
          var index = this.getAttribute("index");
          num = index;
          circle = index;
          animate(ul, -index * focusWidth);
        });
      }
      ol.children[0].className = "current";
      // 克隆第一张图片(li)放到ul 最后面
      var first = ul.children[0].cloneNode(true);
      ul.appendChild(first);

      var num = 0;
      var circle = 0;  // circle 控制小圆圈的播放
      // flag 节流阀
      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;
          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() {
        for (var i = 0; i < ol.children.length; i++) {
          ol.children[i].className = "";
        }
        ol.children[circle].className = "current";
      }
      var timer = setInterval(function () {
        //手动调用点击事件
        arrow_r.click();
      }, 2000);
    });
  </script>
</html>

3、筋头云案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body {
        padding: 0;
        margin: 0;
      }
      ul {
        width: 560px;
        height: 36px;
        padding: 0;
        margin: 0;
        background-color: rgb(235, 247, 247);
        opacity: 0.5;
      }
      li {
        list-style: none;
        float: left;
        width: 80px;
        height: 36px;
        line-height: 36px;
        text-align: center;
      }
      .current {
        z-index: -999;
        position: absolute;
        top: 0;
        left: 0;
        width: 80px;
        height: 36px;
        background-color: pink;
        opacity: 00.9;
      }
    </style>
  </head>

  <body>
    <div>
      <ul>
        <li>首页</li>
        <li>语文</li>
        <li>数学</li>
        <li>英语</li>
        <li>物理</li>
        <li>化学</li>
        <li>体育</li>
      </ul>
      <span class="current"> </span>
    </div>
  </body>
  <script>
    var ul = document.querySelector("ul");
    var lis = document.querySelectorAll("li");
    var current = document.querySelector(".current");

    function animate(obj, target, callback) {
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          clearInterval(obj.timer);
          callback && callback();
        }
        obj.style.left = obj.offsetLeft + step + "px";
      }, 15);
    }

    // console.log(lis.length);
    window.addEventListener("load", function () {
      for (var i = 0; i < lis.length; i++) {
        lis[i].addEventListener("mouseenter", function () {
          animate(current, this.offsetLeft);
        });

        lis[i].addEventListener("mouseleave", function () {
          animate(current, 0);
        });
        lis[i].addEventListener("click", function () {
          current = this.offsetLeft;
        });
      }
    });
  </script>
</html>

六、mouseenter和mouseover的区别

区别:mouseenter不会冒泡
mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发;mouseenter 只会经过自身盒子触发

跟mouseenter搭配的鼠标离开mouseleave 同样不会冒泡