IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> JavaScript Offset Client Scroll系列以及动画函数 -> 正文阅读

[游戏开发]JavaScript Offset Client Scroll系列以及动画函数

一、元素偏移量offset系列

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

  • 获得元素距离带有定位的父元素的位置
  • 获得元素自身大小(宽度和高度)
  • 返回的数值都不带单位

offset系列常用属性:

offset系列属性作用
e.offsetParent返回作为该元素带有定位的父级元素,如果父级没有定位则返回body
e.offsetTop返回元素相对带有定位父元素上方的偏移
e.offsetLeft返回元素相对带有定位父元素左边框的偏移
e.offsetWidth返回自身包括padding、border、content的宽度(返回值不带单位)
e.offsetHeight返回自身包括padding、border、content的高度(返回值不带单位)
1、得到元素带有定位的父元素
console.log(son.offsetParent);
console.log(son.parentNode);
  • offsetParent返回的是带有定位的祖先元素
  • parentNode返回的是最近一级的父元素,不管父元素是否有定位
2、得到元素的偏移位置

返回值不带单位,是相对于带定位的父元素的偏移。

console.log(father.offsetTop);
console.log(father.offsetLeft);
3、得到元素的大小

返回值不带单位,且包含元素的content+padding+border

console.log(w.offsetWidth);
console.log(w.offsetHeight);
4、offset和style的区别
(1)offset
  • offset可以得到任意样式表中的样式值
  • 获取的数组没有单位
  • offsetWidth包含padding+border+content
  • offsetWidth等属性是只读属性,只能获取不能赋值(修改)
  • 所以,如果想要获取元素的大小和位置,使用offset更合适
(2)style
  • style只能得到行内样式表中的样式值
  • style.width获得的是带有单位的字符串
  • style.width不包含padding和border
  • style.width是可读写属性,可以获取也可以赋值
  • 所以,想要修改元素,则需要style改变
5、获取鼠标在盒子中的坐标demo

无法直接获取鼠标在盒子中的坐标,但是可以先获取到鼠标在页面中的坐标,以及盒子相对于页面的位置。用鼠标在页面中的坐标减掉盒子相对于body的位置就可以得到鼠标在盒子中的坐标了。

var box = document.querySelector('.box');
// 获取盒子相对body的坐标位置
var bx = box.offsetLeft;
var by = box.offsetTop;
box.addEventListener('mousemove', function (e) {
    // 获取鼠标在页面中的坐标
    var px = e.pageX;
    var py = e.pageY;
    // 计算得到鼠标在盒子中的坐标
    var x = px - bx;
    var y = py - by;
    console.log(x, y);
})
6、模态框拖拽demo

弹出框,也称为,模态框。

在页面中拖拽的原理:鼠标按下并移动,之后松开鼠标。触发事件是鼠标按下mousedown,鼠标移动mousemove和鼠标松开mouseup。在鼠标移动过程中,获得新的值赋给模态框的left和top,这样模态框就可以跟着鼠标走了。

模态框的真正位置就是 鼠标在页面中的坐标-鼠标在盒子中的坐标

// 1、当鼠标按下,获取鼠标在盒子内的坐标
title.addEventListener('mousedown', function (e) {
    var x = e.pageX - login.offsetLeft;
    var y = e.pageY - login.offsetTop;
    // 2、鼠标移动,鼠标按下才可以进行移动,模态框的left和top值就是鼠标在页面中的坐标 - 鼠标在盒子中的坐标
    // 注意这里鼠标移动事件可以发生在整个页面,所以是给document注册事件
    document.addEventListener('mousemove', move)
    function move(e) {
    	login.style.left = e.pageX - x + 'px';  //别忘了加上px,因为offset系列获取的值是没有单位的!!
    	login.style.top = e.pageY - y + 'px';
    }
    // 3、鼠标弹起,就让鼠标移动事件移除
    document.addEventListener('mouseup', function (e) {
    	document.removeEventListener('mousemove', move);
    })
})

二、元素可视区client系列

1、client系列

client客户端,client系列相关属性可以获取元素可视区的相关信息,通过client系列的相关属性可以动态得到该元素的边框大小、元素大小等。

client系列常用属性:

client系列属性作用
e.clientTop返回元素的上边框大小
e.clientLeft返回元素左边框大小
e.clientWidth返回元素content+padding宽度,返回值不带单位
e.clientHeight返回元素content+padding高度,返回值不带单位

与offset系列最大的区别就是client返回的宽高不包含border

2、flexible.js源码分析
(1)立即执行函数

不需要进行调用就可以执行的函数。

// 写法1:(function() {})();
        // 可以传递参数,第二个小括号可以看做是调用函数
        (function (a, b) {
            console.log(a + b);
        })(1, 2);
// 写法2:(function() {} ());
        (function (a, b) {
            console.log(a + b);
        }(2, 3));
        // 可以写匿名函数也可以写命名函数
        (function sum(a, b) {
            console.log(a + b);
        }(2, 3));

立即执行函数最大的作用就是创建了一个独立的作用域。里面所有的变量都是局部变量,不存在变量命名冲突问题。

(2)flexible.js传参
(function flexible(window, document) {
    js code...;
} (window, document))

传入两个参数window和document

(3)load事件和pageshow事件

以下三种情况都会刷新页面,触发load事件,也就是说会重新设置字体大小。

  1. a标签的超链接
  2. F5或者刷新按钮(强制刷新)
  3. 前进或后退按钮

但是火狐中,有个特点,“往返缓存”。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。所以此时后退按钮不能刷新页面,那么就可以使用pageshow事件来触发。

该事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load触发之后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件。注意该事件要给window添加

(4)flexible.js源码分析
(function flexible(window, document) {
    // 获取html根元素
    var docEl = document.documentElement
    // dpr 物理像素比 = 当前设备的物理像素比 或 1
    var dpr = window.devicePixelRatio || 1

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

    // 设置html元素的文字大小 1rem = viewWidth / 10,将可视区划分为10份
    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) {
        // 如果该页面是从缓存中取过来的页面,也需要重新计算rem大小(为了照顾火狐浏览器)
        if (e.persisted) {
            setRemUnit()
        }
    })

    // 为了让有些不支持0.5像素的 移动端浏览器支持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系列

1、scroll系列

scroll滚动,使用scroll系列的相关属性可以动态获取元素大小、滚动距离等。

scroll系列常用属性:

SCROLL系列属性作用
e.scrollTop返回被卷去的上侧距离
e.scrollLeft返回被卷去的左侧距离
e.scrollWidth返回自身实际内容的宽度,不包含border
e.scrollHeight返回自身实际内容的高度,不包含border

注意返回值没有单位。

scrollWidth和scrollHeight都是返回实际内容的大小,如果内容超出盒子的宽高,会按照内容的实际宽高返回;如果没超出,跟clientWidth和clientHeight差不多,都是返回content+padding的。

2、页面被卷去的头部

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

仿淘宝固定右侧侧边栏demo
  • 页面被卷去的头部可以通过window.pageYOffset获取,被卷去的左侧window.pageXOffset
  • 元素被卷去的头部是element.scrollTop
		var sliderBar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var bannerTop = banner.offsetTop;
        var sliderBarTop = sliderBar.offsetTop;
        var mainTop = main.offsetTop;
        document.addEventListener('scroll', function () {
            // console.log(window.pageYOffset);
            // 1.当页面头部区域被卷去时 将侧边栏改为固定定位
            if (window.pageYOffset >= bannerTop) {
                sliderBar.style.position = 'fixed';
                sliderBar.style.top = sliderBarTop - bannerTop + 'px';
            } else {
                sliderBar.style.position = 'absolute';
                sliderBar.style.top = sliderBarTop + 'px';
            }
            // 2.当页面滚动到main区域时,就显示goBack模块
            if (window.pageYOffset >= mainTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }
        })

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

  • 声明了DTD,使用document.documentElement.scrollTop
  • 未声明DTD,使用document.body.scrollTop
  • 新方法:window.pageYOffsetwindow.pageXOffset,IE9开始支持
3、offset、client、scroll三大系列总结

(1)offset系列常用于获取元素位置 offsetTop offsetLeft
(2)client系列常用于获取元素大小 clientWidth clientHeight
(3)scroll系列常用于获取滚动距离 scrollTop scrollLeft

4、mouseenter和mouseover区别
  • mouseover鼠标经过自身盒子会触发事件,经过子盒子还会触发
  • mouseenter只有经过自身盒子才会触发事件
	<div class="father">
        <div class="son"></div>
    </div>
    <script>
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // father.addEventListener('mouseover', function () {
        //     console.log(11);
        // })
        father.addEventListener('mouseenter', function () {
            console.log(11);
        })
    </script>

这是因为mouseenter事件不会冒泡。而对于mouseover来说,经过父盒子会触发,鼠标经过子盒子的时候,从子盒子冒泡到父盒子,又触发事件。鼠标离开mouseleave同样也不会冒泡~

四、动画

1、动画实现原理

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

实现步骤

1、获得盒子当前位置
2、让盒子在当前位置基础上,向右移动5px
3、利用定时器不断重复这个操作
4、加一个结束定时器的条件
5、注意此元素需要添加定位,才能使用element.style.left改变位置

2、动画函数简单封装
(1)封装

注意函数需要传递两个参数,动画对象移动到的距离

// 动画函数简单封装 obj目标对象 target目标位置
        function animate(obj, target) {
            var timer = setInterval(function () {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(timer);
                }
                obj.style.left = obj.offsetLeft + 5 + 'px';
            }, 30);
        }

不同元素,要使用动画时调用函数即可。

(2)优化

每次调用函数都会声明一个timer变量,开辟一个内存空间。而且,多个不同元素的定时器都叫timer,容易产生冲突。所以,可以将timer添加给obj对象作为obj的属性。注意:停止动画时,也要清除的是obj.timer

		function animate(obj, target) {
            // 给不同对象指定不同的定时器
            obj.timer = setInterval(function () {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + 5 + 'px';
            }, 30);
        }
(3)修改bug

如果添加一个按钮,点击按钮时,才执行动画函数。当我们不断点击按钮的时候,元素会越走越快。

这是因为每次调用都会添加一个新的定时器,为了解决这个问题,需要先清除之前的定时器,只保留当前一个定时器。

        function animate(obj, target) {
// 解决方案:让元素只有一个定时器执行;先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            // 给不同对象指定不同的定时器
            obj.timer = setInterval(function () {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + 5 + 'px';
            }, 30);
        }
        var div = document.querySelector('div');
        var span = document.querySelector('span');
        var btn = document.querySelector('button');
        animate(div, 400);
        // 当我们不断点击按钮,span元素的速度会越来越快,而且会走很远。这是因为开启了太多的计时器
        // 解决方案:让元素只有一个定时器执行
        btn.addEventListener('click', function () {
            animate(span, 300);
        })
3、缓动动画原理

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

思路:

1.让盒子每次移动的距离慢慢变小,速度就会慢慢降下来。
2.核心算法:每次移动的步长 = (目标值 - 现在的位置) / 10
3.停止的条件:当前位置 = 目标位置,就停止定时器

匀速动画:盒子位置 = 盒子当前位置 + 固定值;
缓动动画:盒子位置 = 盒子当前位置 + 变化的步长[(目标值 - 现在的位置) / 10]

		function animate(obj, target) {
            clearInterval(obj.timer);
            obj.timer = setInterval(function () {
                // 步长值
                var step = (target - obj.offsetLeft) / 10;
                if (obj.offsetLeft == target) {
                    clearInterval(obj.timer);
                }
                // 修改步长值 = (目标值 - 当前位置) / 10
                obj.style.left = obj.offsetLeft + step + 'px';
            }, 30);
        }

但盒子无法达到准确位置(有小数),所以我们将步长值修改为整数:
var step = Math.ceil((target - obj.offsetLeft) / 10);

要注意判断步长的正负(因为有可能倒着走):

// 考虑后退的问题
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
4、动画函数添加回调函数

回调函数原理:函数可以作为一个参数,将这个函数作为参数传入另一个函数里面,当那个函数执行完之后,再执行传入的函数,这个过程称为回调。

回调函数写到定时器结束之后:

		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.left = obj.offsetLeft + step + 'px';
            }, 30);
        }

在函数调用的时候,传递实参:

		btn500.addEventListener('click', function () {
            animate(span, 500, function () {
                // alert('你好吗');
                span.style.backgroundColor = 'red';
            });
        })
5、动画函数封装到js文件中

因为要经常使用这个动画函数,可以单独封装到一个js文件中,使用时直接引用这个js文件。
使用方法:

1.新建一个js文件,存放animate函数
2.在html文件中引入animate.js文件,并调用animate函数

引入animate.js<script src="animate.js"></script>

	<div class="sliderbar">
        <span></span>
        <div class="con">问题反馈</div>
    </div>
    <script>
        var sliderbar = document.querySelector('.sliderbar');
        var con = document.querySelector('.con');
        var span = document.querySelector('span');
        // 鼠标经过sliderbar 就让con盒子滑动到左侧
        sliderbar.addEventListener('mouseenter', function () {
            animate(con, -160, function () {
                // 当用户执行完毕,则将左箭头改为右箭头
                span.innerHTML = '→';
            });
        })
        // 鼠标离开sliderbar 就让con盒子滑动到右侧
        sliderbar.addEventListener('mouseleave', function () {
            animate(con, 0, function () {
                span.innerHTML = '←';
            });
        })
    </script>
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-09-12 20:45:34  更:2021-09-12 20:46:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 10:48:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码