37-scroll相关属性和缓动动画

scroll 相关属性

window.onscroll() 方法

当我们用鼠标滚轮,滚动网页的时候,会触发 window.onscroll() 方法。效果如下:(注意看控制台的打印结果)

48-scroll相关属性和缓动动画 - 图1

如果你需要做滚动监听,可以使用这个方法。

我们来看看和 scroll 相关的有哪些属性。

1、ScrollWidth 和 scrollHeight

ScrollWidthscrollHeight:获取元素整个滚动区域的宽、高。包括 width 和 padding,不包括 border和margin。

注意

scrollHeight 的特点是:如果内容超出了盒子,scrollHeight为内容的高(包括超出的内容);如果不超出,scrollHeight为盒子本身的高度。ScrollWidth同理。

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. div {
  8. width: 100px;
  9. height: 100px;
  10. padding: 10px;
  11. margin: 3px;
  12. border: 8px solid red;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <div class="box">
  18. 静,能寒窗苦守;动,能点石成金。
  19. 静,能寒窗苦守;动,能点石成金。
  20. 静,能寒窗苦守;动,能点石成金。
  21. 静,能寒窗苦守;动,能点石成金。
  22. 静,能寒窗苦守;动,能点石成金。
  23. 静,能寒窗苦守;动,能点石成金。
  24. </div>
  25. <script>
  26. var div = document.getElementsByTagName("div")[0];
  27. // `scrollHeight` 的特点是:如果内容超出了盒子,`scrollHeight`为内容的高(包括超出的内容);如果不超出,`scrollHeight`为盒子本身的高度。
  28. //IE8以下(不包括IE8),为盒子本身内容的高度。
  29. console.log(div.scrollWidth);
  30. console.log(div.scrollHeight);
  31. </script>
  32. </body>
  33. </html>

打印结果:

48-scroll相关属性和缓动动画 - 图2

2、scrollTop 和 scrollLeft

  • scrollLeft:获取水平滚动条滚动的距离。

  • scrollTop:获取垂直滚动条滚动的距离。

实战经验

当某个元素满足scrollHeight - scrollTop == clientHeight时,说明垂直滚动条滚动到底了。

当某个元素满足scrollWidth - scrollLeft == clientWidth时,说明水平滚动条滚动到底了。

这个实战经验非常有用,可以用来判断用户是否已经将内容滑动到底了。比如说,有些场景下,希望用户能够看完“长长的活动规则”,才允许触发接下来的表单操作。

scrollTop 的兼容性

如果要获取页面滚动的距离,scrollTop 这个属性的写法要注意兼容性,如下。

(1)如果文档没有 DTD 声明,写法为:

  1. document.body.scrollTop

在没有 DTD 声明的情况下,要求是这种写法,chrome浏览器才能认出来。

(2)如果文档有 DTD 声明,写法为:

  1. document.documentElement.scrollTop

在有 DTD 声明的情况下,要求是这种写法,IE6、7、8才能认出来。

综合上面这两个,就诞生了一种兼容性的写法:

  1. document.body.scrollTop || document.documentElement.scrollTop //方式一
  2. document.body.scrollTop + document.documentElement.scrollTop //方式二

另外还有一种兼容性的写法:window.pageYOffsetwindow.pageXOffset。这种写法无视DTD的声明。这种写法支持的浏览器版本是:火狐/谷歌/ie9+。

综合上面的几种写法,为了兼容,不管有没有DTD,最终版的兼容性写法:

  1. window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;

判断是否已经 DTD 声明

方法如下:

  1. document.compatMode === "CSS1Compat" // 已声明
  2. document.compatMode === "BackCompat" // 未声明

将 scrollTop 和 scrollLeft 进行封装

这里,我们将 scrollTop 和 scrollLeft 封装为一个方法,名叫scroll(),返回值为 一个对象。以后就直接调用scroll().topscroll().left就好。

代码实现:

  1. <html>
  2. <head lang="en">
  3. <meta charset="UTF-8">
  4. <title></title>
  5. <style>
  6. body {
  7. height: 6000px;
  8. width: 5000px;
  9. }
  10. </style>
  11. </head>
  12. <body>
  13. <script>
  14. //需求:封装一个兼容的scroll().返回的是对象,用scroll().top获取scrollTop,用scroll().left获取scrollLeft
  15. window.onscroll = function () {
  16. // var myScroll = scroll();
  17. // myScroll.top;
  18. console.log(scroll().top);
  19. console.log(scroll().left);
  20. }
  21. //函数封装(简单封装,实际工作使用)
  22. function scroll() {
  23. return { //此函数的返回值是对象
  24. left: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
  25. right: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
  26. }
  27. }
  28. </script>
  29. </body>
  30. </html>

上方代码中,函数定义的那部分就是要封装的代码。

另外还有一种比较麻烦的封装方式:(仅供参考)

  1. function scroll() { // 开始封装自己的scrollTop
  2. if(window.pageYOffset !== undefined) { // ie9+ 高版本浏览器
  3. // 因为 window.pageYOffset 默认的是 0 所以这里需要判断
  4. return {
  5. left: window.pageXOffset,
  6. top: window.pageYOffset
  7. }
  8. }
  9. else if(document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTD
  10. return {
  11. left: document.documentElement.scrollLeft,
  12. top: document.documentElement.scrollTop
  13. }
  14. }
  15. return { // 未声明 DTD
  16. left: document.body.scrollLeft,
  17. top: document.body.scrollTop
  18. }
  19. }

获取 html 文档的方法

获取title、body、head、html标签的方法如下:

  • document.title 文档标题;

  • document.head 文档的头标签

  • document.body 文档的body标签;

  • document.documentElement (这个很重要)。

document.documentElement表示文档的html标签。也就是说,基本结构当中的 html 标签而是通过document.documentElement访问的,并不是通过 document.html 去访问的。

scrollTop 举例:固定导航栏

完整版代码实现:

(1)index.html:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. * {
  8. margin: 0;
  9. padding: 0
  10. }
  11. img {
  12. vertical-align: top;
  13. }
  14. .main {
  15. margin: 0 auto;
  16. width: 1000px;
  17. margin-top: 10px;
  18. }
  19. #Q-nav1 {
  20. overflow: hidden;
  21. }
  22. .fixed {
  23. position: fixed;
  24. top: 0;
  25. left: 0;
  26. }
  27. </style>
  28. <!--引入工具js-->
  29. <script src="tools.js"></script>
  30. <script>
  31. window.onload = function () {
  32. //需求1:当我们滚动界面的时候,被卷曲的头部如果超过第二个盒子距离顶部的位置,那么直接给第二个盒子加类名.fixed
  33. //需求2:当我们滚动界面的时候,被卷曲的头部如果小于第二个盒子距离顶部的位置,那么直接给第二个盒子取消类名.fixed
  34. //1.老三步。
  35. var topDiv = document.getElementById("top");
  36. var height = topDiv.offsetHeight;
  37. var middle = document.getElementById("Q-nav1");
  38. var main = document.getElementById("main");
  39. window.onscroll = function () {
  40. //2.判断 ,被卷曲的头部的大小
  41. if (scroll().top > height) {
  42. //3.满足条件添加类,否则删除类
  43. middle.className += " fixed";
  44. //第二个盒子也要占位置,为了避免重叠,我们给第三个盒子一个上padding的空间,把这个空间留给第二个盒子
  45. main.style.paddingTop = middle.offsetHeight + "px";
  46. } else {
  47. middle.className = "";
  48. //清零
  49. main.style.paddingTop = 0;
  50. }
  51. }
  52. }
  53. </script>
  54. </head>
  55. <body>
  56. <div class="top" id="top">
  57. <img src="images/top.png" alt=""/>
  58. </div>
  59. <div id="Q-nav1">
  60. <img src="images/nav.png" alt=""/>
  61. </div>
  62. <div class="main" id="main">
  63. <img src="images/main.png" alt=""/>
  64. </div>
  65. </body>
  66. </html>

上方代码中,有一个技巧:

  1. main.style.paddingTop = middle.offsetHeight + "px";

仔细看注释就好。

(2)tools.js:

  1. /**
  2. * Created by smyhvae on 2018/02/03.
  3. */
  4. function scroll() { // 开始封装自己的scrollTop
  5. if (window.pageYOffset !== undefined) { // ie9+ 高版本浏览器
  6. // 因为 window.pageYOffset 默认的是 0 所以这里需要判断
  7. return {
  8. left: window.pageXOffset,
  9. top: window.pageYOffset
  10. }
  11. }
  12. else if (document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTD
  13. return {
  14. left: document.documentElement.scrollLeft,
  15. top: document.documentElement.scrollTop
  16. }
  17. }
  18. return { // 未声明 DTD
  19. left: document.body.scrollLeft,
  20. top: document.body.scrollTop
  21. }
  22. }

实现效果:

48-scroll相关属性和缓动动画 - 图3

工程文件:

  • 2018-02-03-scrollTop固定导航栏.rar

缓动动画

三个函数

缓慢动画里,我们要用到三个函数,这里先列出来:

  • Math.ceil() 向上取整

  • Math.floor() 向下取整

  • Math.round(); 四舍五入

缓动动画的原理

缓动动画的原理就是:在移动的过程中,步长越来越小。

设置步长为:目标位置和盒子当前位置的十分之一。用公式表达,即:

  1. 盒子位置 = 盒子本身位置 + (目标位置 - 盒子本身位置)/ 10

代码举例:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. div {
  8. width: 100px;
  9. height: 100px;
  10. background-color: pink;
  11. position: absolute;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <button>运动到left = 400px</button>
  17. <div></div>
  18. <script>
  19. var btn = document.getElementsByTagName("button")[0];
  20. var div = document.getElementsByTagName("div")[0];
  21. btn.onclick = function () {
  22. setInterval(function () {
  23. //动画原理:盒子未来的位置 = 盒子当前的位置+步长
  24. div.style.left = div.offsetLeft + (400 - div.offsetLeft) / 10 + "px";
  25. }, 30);
  26. }
  27. </script>
  28. </body>
  29. </html>

效果:

48-scroll相关属性和缓动动画 - 图4

缓慢动画的封装(解决四舍五入的问题)

我们发现一个问题,上图中的盒子最终并没有到达400px的位置,而是只到了396.04px就停住了:

48-scroll相关属性和缓动动画 - 图5

原因是:JS在取整的运算时,进行了四舍五入。

我们把打印396.04px这个left值打印出来看看:

48-scroll相关属性和缓动动画 - 图6

我么发现,通过div.style.left获取的值是精确的,通过div.offsetLeft获取的left值会进行四舍五入。

此时,我们就要用到取整的函数了。

通过对缓动动画进行封装,完整版的代码实现如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. div {
  8. width: 100px;
  9. height: 100px;
  10. background-color: pink;
  11. position: absolute;
  12. left: 0;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <button>运动到200</button>
  18. <button>运动到400</button>
  19. <div></div>
  20. <script>
  21. var btn = document.getElementsByTagName("button");
  22. var div = document.getElementsByTagName("div")[0];
  23. btn[0].onclick = function () {
  24. animate(div, 200);
  25. }
  26. btn[1].onclick = function () {
  27. animate(div, 400);
  28. }
  29. //缓动动画封装
  30. function animate(ele, target) {
  31. //要用定时器,先清定时器
  32. //一个萝卜一个坑儿,一个元素对应一个定时器
  33. clearInterval(ele.timer);
  34. //定义定时器
  35. ele.timer = setInterval(function () {
  36. //获取步长
  37. //步长应该是越来越小的,缓动的算法。
  38. var step = (target - ele.offsetLeft) / 10;
  39. //对步长进行二次加工(大于0向上取整,小于0向下取整)
  40. //达到的效果是:最后10像素的时候都是1像素1像素的向目标位置移动,就能够到达指定位置。
  41. step = step > 0 ? Math.ceil(step) : Math.floor(step);
  42. //动画原理: 目标位置 = 当前位置 + 步长
  43. ele.style.left = ele.offsetLeft + step + "px";
  44. console.log(step);
  45. //检测缓动动画有没有停止
  46. console.log("smyhvae");
  47. if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)) {
  48. //处理小数赋值
  49. ele.style.left = target + "px";
  50. clearInterval(ele.timer);
  51. }
  52. }, 30);
  53. }
  54. </script>
  55. </body>
  56. </html>

实现效果:

48-scroll相关属性和缓动动画 - 图7

window.scrollTo()方法举例:返回到顶部小火箭

(1)index.html:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <style>
  7. img {
  8. position: fixed;
  9. bottom: 100px;
  10. right: 50px;
  11. cursor: pointer;
  12. display: none;
  13. border: 1px solid #000;
  14. }
  15. div {
  16. width: 1210px;
  17. margin: 100px auto;
  18. text-align: center;
  19. font: 500 26px/35px "simsun";
  20. color: red;
  21. }
  22. </style>
  23. <script src="tools.js"></script>
  24. <script>
  25. window.onload = function () {
  26. //需求:被卷去的头部超过100显示小火箭,然后点击小火箭屏幕缓慢移动到最顶端。
  27. //难点:我们以前是移动盒子,现在是移动屏幕,我们没有学过如何移动屏幕。
  28. // 技术点:window.scrollTo(x,y);浏览器显示区域跳转到指定的坐标
  29. //步骤:
  30. //1.老三步
  31. var img = document.getElementsByTagName("img")[0];
  32. window.onscroll = function () {
  33. //被卷去的距离大于200显示小火箭,否则隐藏
  34. //2.显示隐藏小火箭
  35. if (scroll().top > 1000) {
  36. img.style.display = "block";
  37. } else {
  38. img.style.display = "none";
  39. }
  40. //每次移动滚动条的时候都给leader赋值,模拟leader获取距离顶部的距离
  41. leader = scroll().top;
  42. }
  43. //3.缓动跳转到页面最顶端(利用我们的缓动动画)
  44. var timer = null;
  45. var target = 0; //目标位置
  46. var leader = 0; //显示区域自身的位置
  47. img.onclick = function () {
  48. //技术点:window.scrollTo(0,0);
  49. //要用定时器,先清定时器
  50. clearInterval(timer);
  51. timer = setInterval(function () {
  52. //获取步长
  53. var step = (target - leader) / 10;
  54. //二次处理步长
  55. step = step > 0 ? Math.ceil(step) : Math.floor(step);
  56. leader = leader + step; //往上移动的过程中,step是负数。当前位置减去步长,就等于下一步的位置。
  57. //显示区域移动
  58. window.scrollTo(0, leader);
  59. //清除定时器
  60. if (leader === 0) {
  61. clearInterval(timer);
  62. }
  63. }, 25);
  64. }
  65. }
  66. </script>
  67. </head>
  68. <body>
  69. <img src="images/Top.jpg"/>
  70. <div>
  71. 我是最顶端.....<br>
  72. 生命壹号,永不止步.....<br>
  73. 生命壹号,永不止步.....<br>
  74. 生命壹号,永不止步.....<br>
  75. 生命壹号,永不止步.....<br>
  76. 生命壹号,永不止步.....<br>
  77. 生命壹号,永不止步.....<br>
  78. 生命壹号,永不止步.....<br>
  79. 生命壹号,永不止步.....<br>
  80. 生命壹号,永不止步.....<br>
  81. 生命壹号,永不止步.....<br>
  82. 生命壹号,永不止步.....<br>
  83. 生命壹号,永不止步.....<br>
  84. 生命壹号,永不止步.....<br>
  85. 生命壹号,永不止步.....<br>
  86. 生命壹号,永不止步.....<br>
  87. 生命壹号,永不止步.....<br>
  88. 生命壹号,永不止步.....<br>
  89. 生命壹号,永不止步.....<br>
  90. 生命壹号,永不止步.....<br>
  91. 生命壹号,永不止步.....<br>
  92. 生命壹号,永不止步.....<br>
  93. 生命壹号,永不止步.....<br>
  94. 生命壹号,永不止步.....<br>
  95. 生命壹号,永不止步.....<br>
  96. 生命壹号,永不止步.....<br>
  97. 生命壹号,永不止步.....<br>
  98. 生命壹号,永不止步.....<br>
  99. 生命壹号,永不止步.....<br>
  100. 生命壹号,永不止步.....<br>
  101. 生命壹号,永不止步.....<br>
  102. 生命壹号,永不止步.....<br>
  103. 生命壹号,永不止步.....<br>
  104. 生命壹号,永不止步.....<br>
  105. 生命壹号,永不止步.....<br>
  106. 生命壹号,永不止步.....<br>
  107. 生命壹号,永不止步.....<br>
  108. 生命壹号,永不止步.....<br>
  109. 生命壹号,永不止步.....<br>
  110. 生命壹号,永不止步.....<br>
  111. 生命壹号,永不止步.....<br>
  112. 生命壹号,永不止步.....<br>
  113. 生命壹号,永不止步.....<br>
  114. 生命壹号,永不止步.....<br>
  115. 生命壹号,永不止步.....<br>
  116. 生命壹号,永不止步.....<br>
  117. 生命壹号,永不止步.....<br>
  118. 生命壹号,永不止步.....<br>
  119. 生命壹号,永不止步.....<br>
  120. 生命壹号,永不止步.....<br>
  121. 生命壹号,永不止步.....<br>
  122. 生命壹号,永不止步.....<br>
  123. 生命壹号,永不止步.....<br>
  124. 生命壹号,永不止步.....<br>
  125. 生命壹号,永不止步.....<br>
  126. 生命壹号,永不止步.....<br>
  127. 生命壹号,永不止步.....<br>
  128. 生命壹号,永不止步.....<br>
  129. 生命壹号,永不止步.....<br>
  130. 生命壹号,永不止步.....<br>
  131. 生命壹号,永不止步.....<br>
  132. 生命壹号,永不止步.....<br>
  133. 生命壹号,永不止步.....<br>
  134. 生命壹号,永不止步.....<br>
  135. 生命壹号,永不止步.....<br>
  136. 生命壹号,永不止步.....<br>
  137. 生命壹号,永不止步.....<br>
  138. 生命壹号,永不止步.....<br>
  139. 生命壹号,永不止步.....<br>
  140. 生命壹号,永不止步.....<br>
  141. 生命壹号,永不止步.....<br>
  142. 生命壹号,永不止步.....<br>
  143. 生命壹号,永不止步.....<br>
  144. </div>
  145. </body>
  146. </html>

(2)tools.js:

  1. /**
  2. * Created by smyhvae on 2015/12/8.
  3. */
  4. //函数:获取scrollTop和scrollLeft的值
  5. function scroll() { // 开始封装自己的scrollTop
  6. if (window.pageYOffset != null) { // ie9+ 高版本浏览器
  7. // 因为 window.pageYOffset 默认的是 0 所以这里需要判断
  8. return {
  9. left: window.pageXOffset,
  10. top: window.pageYOffset
  11. }
  12. }
  13. else if (document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTD
  14. return {
  15. left: document.documentElement.scrollLeft,
  16. top: document.documentElement.scrollTop
  17. }
  18. }
  19. return { // 未声明 DTD
  20. left: document.body.scrollLeft,
  21. top: document.body.scrollTop
  22. }
  23. }

实现效果:

48-scroll相关属性和缓动动画 - 图8

小火箭的图片资源:

48-scroll相关属性和缓动动画 - 图9

我的公众号

想学习更多技能?不妨关注我的微信公众号:千古壹号(id:qianguyihao)。

扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:

48-scroll相关属性和缓动动画 - 图10