第一章-JavaScript基础进阶part4:PC端常见特效实现原理

本文详细解释了HTML/CSS中的元素偏移量(offset)、可视区(client)和滚动(scroll)属性,以及它们的应用,包括flexible.js的核心原理。同时涵盖了动画函数的封装和缓动动画的实现技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、元素偏移量offset

1.1 offset常用属性

offset 翻译过来就是偏移量,我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等.它可以

  • 获得元素距离带有定位父元素的位置(offsetLeft,offsetTop)
  • 获得元素自身的大小( 宽度高度)(offsetWidth,offsetHeight)
  • 注意:返回的数值都不带单位
    在这里插入图片描述
<!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">
    <style>
        .father{
            width:300px;
            height: 300px;
            margin: 200px;
            background: pink;
            position: relative;
        }
        .child{
            width: 200px;
            height: 200px;
            background: purple;
            margin: 40px;
            padding:10px;
            border: 2px solid red;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="child"></div>
    </div>
    <script>
        var father = document.querySelector(".father")
        var child = document.querySelector(".child")
        // 1.获取相对偏移量。注意:如果父元素没有定位,则默认返回相对body的偏移量
        // 当父元素.father有position:relative属性时,返回40,0;当你元素没有定位时返回240,200
        console.log(child.offsetLeft,child.offsetTop)

        // 2.获取自身大小(包含padding,border,width)
        console.log(child.offsetWidth, child.offsetHeight)  // 224 224

        // 3.获取具有定位的父元素(当父元素没有定位时默认为body)
        console.log(child.offsetParent) // .father有position:relative时,返回.father。
    </script>
</body>
</html>

1.2 offset与style的区别

1、offset

  • offset 可以得到任意样式表中的width,height样式值(内嵌,行内,外链样式)
  • offset 系列获得的数值是没有单位的(数值型)
  • offsetWidth包含padding+border+width
  • offsetWidth 等属性是只读属性,只能获取不能赋值
  • 总结:想要获取元素大小位置,用offset更合适

2、style

  • style 只能得到行内样式表中的样式值
  • style.width获得的是带有单位的字符串
  • style.width获得不包含padding和border的值
  • style.width 是可读写属性,可以获取也可以赋值
  • 想要给元素更改值,则需要用style改变
    在这里插入图片描述
<!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">
    <style>
        .box {
            width: 200px;
            height: 200px;
            background: purple;
            margin: 40px;
            padding: 10px;
            border: 2px solid red;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        var box = document.querySelector(".box")
        let offsetWidth = box.offsetWidth
        let styleWidth = box.style.width
        // 1、因为style只能获取到行内样式的width值,所以此处输出224,空
        // 2、因为offset包含padding,border,width值,所以此处返回为200+10+10+2+2 = 224
        console.log(offsetWidth,styleWidth)
    </script>
</body>
</html>

1.3 案例

1、拖拽元素

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 200px;
        height: 200px;
        background: pink;
        position: absolute;
        left: 0;
        top: 0;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <script>
      var box = document.querySelector(".box");

      // 鼠标按下,获取坐标
      box.addEventListener("mousedown", function (e) {
        box.addEventListener("mousemove", move);
      });
      function move(e) {
        // 示例1、获取鼠标在box内的坐标
        var pageX = e.pageX;
        var pageY = e.pageY;
        var offsetX = this.offsetLeft;
        var offsetY = this.offsetTop;
        var x = pageX - offsetX;
        var y = pageY - offsetY;
        this.innerHTML = "鼠标在盒内坐标(x,y):(" + x + "," + y + ")";

        // 示例2、拖拽实现 —— 绝对定位+动态定位赋值,默认鼠标在盒子居中位置
        var posLeft = pageX - box.offsetWidth / 2;
        var posTop = pageY - box.offsetHeight / 2;
        this.style.left = posLeft + "px";
        this.style.top = posTop + "px";
      }

      box.addEventListener("mouseup", function (e) {
        box.removeEventListener("mousemove", move);
      });
    </script>
  </body>
</html>

二、元素可视区client

2.1 常用client属性

client: 翻译过来是客户端,我们使用client系列的相关属性来获取元素可视区的相关信息。通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
![在这里插入图片描述](https://img-blog.csdnimg.cn/6708420a63764e8d837e9f0bb234ffc5.png

2.2 client应用-flexible.js核心原理

1、立即执行函数
- 立即执行函数最大的作用是独立创建了一个作用域,里面的所有变量都是局部变量,不会有命名冲突的情况
- 创建独立作用域,避免命名冲突

// 1、具名函数直接调用
function fn(){
  console.log("执行了")
}
fn()

// 2、方式2.1 (Fun)()
(function(a,b){
  console.log(a+b)
})(1,2)

// 3、方式2.2 (Fun()())
(function(a,b){
  console.log(a+b)
}(3,4))

2、pageshow事件

  • 与onload事件相比,pageshow事件在页面显示时触发,且无论页面是否来自缓存都会触发(onload在firefox中如果有缓存页面数据,不会触发)。
  • pageshow会在load事件后触发
  • 根据事件对象中的persisted来判断 是否是缓存中的页面触发的。
  • pageshow事件,是给window对象添加的
// 立即执行函数 (function() {})() 或者 (function() {} ())
// 主要作用:创建一个独立的作用域,里面所有的变量都是局部变量,避免了命名冲突问题
// 立即执行函数不需要调用,立马能够自己执行
(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))

三、元素滚动scroll

3.1 scroll系列常用属性

在这里插入图片描述

  • 页面被卷去的上侧距离 : window.pageYOffset
  • 页面被卷去的左侧距离 :window.pageXOffset
  • 滚动事件:window.onscroll 或window.addEventListener(“scroll”,fun)

1、offsetHeight,clientHeight,scrollHeight的区别
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="box" style="width:200px;height:200px;background: pink;border:6px solid blue;padding:10px">
    一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box一段超长文本超出box
  </div>
  <script>
    var box = document.querySelector(".box")
    let oh = box.offsetHeight // padding+border+height
    let ch = box.clientHeight // padding+height
    let sh = box.scrollHeight // padding+contentHeight
    // 输出:232 220 304
    console.log(oh,ch,sh)
  </script>
</body>
</html>

2、scroll的兼容性问题

  • 当页面声明了doctype时,被卷去的头部通过:document.documentElement.scrollTop获取
  • 当页面没声明doctype时,使用document.body.scrollTop
  • 在IE9以后,通过window.pageYOffset和window.pageXOffset获取
function getScroll(){
  return {
    left :window.pageXOffset ||document.documentElement.scrollLeft||document.body.scrollLeft,
    top:window.pageYOffset || document.documentElement.scrollTop||document.body.scrollTop,
  }
}

3、使用场景

  • offset属性:常用于获取元素的位置
  • client属性:获取元素大小
  • scroll属性:用于获取滚动距离

3.2 mouseover和mouseenter的区别

  • 当鼠标移动到元素上时就会触发mouseenter事件
  • mouseover鼠标经过自身盒子会触发,经过子例子还会触发。mouseenter只会经过自身盒子触发(因为mouseenter不会冒泡)
  • mouseenter对应mouseleave
  • mouseover对应mouseout

四、动画函数封装

4.1 动画实现原理

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

  1. 获得例子当前位置
  2. 让例子在当前位置加上1个移动距离
  3. 利用定时器不断重复这个操作
  4. 加一个结束定时器的条件
  5. 注意此元素需要绝对定位,才能使用用element.style.left控制移动
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        background: pink;
        position: absolute;
        left: 0;
        top: 0;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <script>
      var box = document.querySelector(".box");
      var timer = setInterval(() => {
        if(box.offsetLeft>=500){
          clearInterval(timer)
        }
        let left = box.offsetLeft + 1;
        box.style.left = left + "px";
      });
    </script>
  </body>
</html>

4.2 动画函数封装

  1. 设置函数目标对象
  2. 确定目标位置
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .big {
        width: 200px;
        height: 200px;
        background: pink;
        position: absolute;
        left: 0;
        top: 0;
      }
      .small {
        width: 100px;
        height: 100px;
        background: blue;
        position: absolute;
        left: 0;
        top: 220px;
      }
    </style>
  </head>
  <body>
    <div class="big">big</div>
    
    <div class="small"><button>small开始动画</button></div>
    <script>
      // 1、封装动画函数
      //  elObj -> 要实现动画的元素对象, target:停止动画的距离
      function animate(elObj, target) {
        // 如果元素对象有定时器,需要先清理,防止重复创建定时器
        if (elObj.timer) {
          clearInterval(elObj.timer);
        }
        // 持续设置元素左边距
        elObj.timer = setInterval(() => {
          if (elObj.offsetLeft > target) {
            clearInterval(elObj.timer);
          }
          elObj.style.left = elObj.offsetLeft + 1 + "px";
        }, 5);
      }
      var big = document.querySelector(".big");
      var small = document.querySelector(".small");
      var btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        animate(small, 300);
      });
      animate(big, 400);
    </script>
  </body>
</html>

4.3 缓动动画

原理:

  • 缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来
  2. 核心算法:(目标值-现在的位置)/ 10 做为每次移动的距离步长
  3. 停止的条件是:让当前盒子位置等于目标位置就停止定时器
    注意:动画步长需要取整,步长大于0时向上取整,小于0时向下取整
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .big {
        width: 200px;
        height: 200px;
        background: pink;
        position: absolute;
        left: 0;
        top: 0;
      }
      
    </style>
  </head>
  <body>
    <div class="big">big</div>
    <script>
      // 1、缓动动画原理:随时时间增加,移动距离减少
      function animate(elObj, target) {
        // 如果元素对象有定时器,需要先清理,防止重复创建定时器
        if (elObj.timer) {
          clearInterval(elObj.timer);
        }
        // 持续设置元素左边距
        elObj.timer = setInterval(() => {
          // 步长值,用于控制每次移动距离 
          var step = (target - elObj.offsetLeft)/10
          if (elObj.offsetLeft == target) {
            clearInterval(elObj.timer);
          }
          elObj.style.left = elObj.offsetLeft + step + "px";
        }, 15);
      }
      var big = document.querySelector(".big");
    
      animate(big, 1200);
    </script>
  </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sophie_U

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值