0
点赞
收藏
分享

微信扫一扫

JavaScript——PC端网页特效

大明宫 2022-03-12 阅读 219

目录

一、元素偏移量offset系列

1. offset概述

2. 常见属性

3. offset与style区别

案例——鼠标在盒子内坐标

案例——拖动模态框

案例——京东放大镜

二、元素可视区client系列

flexible源码分析

立即执行函数

pageshow事件

三、元素滚动scroll系列

1. 页面被卷去头部

 案例——仿淘宝固定右侧侧边栏

 2. 页面被卷曲头部兼容性方案

四、三大系列总结

五、mouseenter和mouseover事件

六、动画函数封装

1. 动画实现原理

2. 动画函数的简单封装

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

4. 缓动效果动画

5. 动画函数在多个目标之间移动

6. 给动画函数添加回调参数

7. 动画函数封装到单独JS文件里面

案例——右侧盒子滑动


  

一、元素偏移量offset系列

1. offset概述

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

  • 获取元素距离带有定位父元素的位置
  • 获得元素自身大小
  • 注意:返回的值都不带单位

2. 常见属性

	<!-- offset位置 -->
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}

		.father {
			position: relative;
			width: 200px;
			height: 200px;
			background-color: pink;
			margin: 150px;
		}

		.son {
			width: 50px;
			height: 50px;
			background-color: blue;
			margin: 50px;
			padding: 10px;
			border: 2px solid red;
		}
	</style>
	<body>
		<div class="father">
			<div class="son">

			</div>
		</div>
		<script type="text/javascript">
			var father = document.querySelector('.father');
			var son = document.querySelector('.son');

			console.log(father.offsetTop);
			console.log(father.offsetLeft);
			//1.它是以有定位的父亲为准,若无父亲或无定位的父亲 以body为准
			console.log(son.offsetLeft);
			//2.可以得到元素的大小,宽高  包含padding+border+width
			console.log(son.offsetHeight);
			console.log(son.offsetWidth);
			//3. 返回带有定位的父亲,若无返回body
			console.log(son.offsetParent);
			console.log(son.parentNode);	//返回父亲 是最近一级的父亲 不管有没有定位
		</script>
	</body>

3. offset与style区别

案例——鼠标在盒子内坐标

    <!-- 鼠标在盒子内坐标 -->
	<style type="text/css">
		.box {
			width: 200px;
			height: 200px;
			background-color: pink;
			margin: 100px 100px;
		}
	</style>
	<body>
		<div class="box"></div>
		<script type="text/javascript">
			var box = document.querySelector('.box');
			box.addEventListener('mousemove', function(e) {
				// console.log(e.pageX);
				// console.log(e.pageY);
				var x = e.pageX - this.offsetLeft;
				var y = e.pageY - this.offsetTop;
				this.innerHTML = 'x坐标是' + x + 'y坐标是' + y;
			})
		</script>
	</body>

案例——拖动模态框

	<!-- 拖动模态框 -->
	<style type="text/css">
		.login-header {
			text-align: center;
		}

		a {
			margin: 0;
			padding: 0;
		}

		.login {
			display: none;
			width: 512px;
			height: 280px;
			position: fixed;
			border: 1px solid #EBEBEB;
			left: 50%;
			top: 50%;
			background-color: #FFFFFF;
			box-shadow: 0 0 20px #DDDDDD;
			z-index: 9999;
			transform: translate(-50%, -50%);
		}

		.login-title {
			width: 100%;
			margin: 10px 0 0 0;
			text-align: center;
			line-height: 40px;
			height: 40px;
			font-size: 18px;
			position: relative;
			cursor: move;
		}

		.login-input-content {
			margin-top: 20px;
		}

		.login-button {
			width: 50%;
			margin: 30px auto 0 auto;
			line-height: 40px;
			font-size: 14px;
			border: 1px solid #EBEBEB;
			text-align: center;
		}

		.login-bg {
			display: none;
			width: 100%;
			height: 100%;
			position: fixed;
			top: 0px;
			left: 0px;
			background-color: rgba(0, 0, 0, .3);
		}

		a {
			text-decoration: none;
			color: #000000;
		}

		.login-button a {
			display: block;
		}

		.login-input input.list-input {
			float: left;
			line-height: 35px;
			height: 35px;
			width: 350px;
			border: 1px solid #EBEBEB;
			text-indent: 5px;
		}

		.login-input {
			overflow: hidden;
			margin: 0 0 20px 0;
		}

		.login-input label {
			float: left;
			width: 90px;
			padding-right: 10px;
			text-align: center;
			line-height: 35px;
			height: 35px;
			font-size: 14px;
		}

		.login-title span {
			position: absolute;
			font-size: 12px;
			right: -20px;
			top: -30px;
			background-color: #FFFFFF;
			border: #EBEBEB solid 1px;
			width: 40px;
			height: 40px;
			border-radius: 20px;
		}
	</style>
	<body>
		<div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</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>用户名:</label>
					<input type="text" name="info[username]" id="username" placeholder="请输入用户名" class="list-input" />
				</div>
				<div class="login-input">
					<label>登录密码:</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>

		<script>
			//1.获取事件
			var login = document.querySelector('.login');
			var mask = document.querySelector('.login-bg');
			var link = document.querySelector('#link');
			var closeBtn = document.querySelector('#closeBtn');
			var title = document.querySelector('#title');
			//2.点击弹出,显示登录
			link.addEventListener('click', function() {
				mask.style.display = 'block';
				login.style.display = 'block';
			})
			//3.点击关闭
			closeBtn.addEventListener('click', function() {
				mask.style.display = 'none';
				login.style.display = 'none';
			})
			//4.开始拖拽
			//(1)当鼠标按下,获得鼠标在盒子内坐标
			title.addEventListener('mousedown', function(e) {
				var x = e.pageX - login.offsetLeft;
				var y = e.pageY - login.offsetTop;
				//(2)鼠标移动式,把鼠标在页面中的坐标,减去鼠标在盒子内的坐标(不变的)
				function move(e) {
					login.style.left = e.pageX - x + 'px';
					login.style.top = e.pageY - y + 'px';
				}
				document.addEventListener('mousemove', move);
				//(3)鼠标弹起,就让鼠标移动事件移除
				document.addEventListener('mouseup', function() {
					document.removeEventListener('mousemove', move);
				})
			})
		</script>

案例——京东放大镜

	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
		}

		.preview_img {
			position: relative;
			border: 1px solid #CCCCCC;
			height: 400px;
			width: 400px;
		}

		.preview img {}

		.mask {
			display: none;
			position: absolute;
			top: 0;
			left: 0;
			height: 300px;
			width: 300px;
			background-color: #fede4f;
			border: 1px solid #CCCCCC;
			opacity: .5;
			cursor: move;
		}

		.big {
			display: none;
			position: absolute;
			top: 0;
			left: 410px;
			width: 500px;
			height: 500px;
			border: 1px solid #CCCCCC;
			overflow: hidden;
		}

		.big img {
			position: absolute;
			top: 0;
			left: 0;
		}
	</style>
	<body>
		<div class="preview_img">
			<img src="img/load.png" />
			<div class="mask">1</div>
			<div class="big">
				<img src="img/load-big.jpg" class="bigImg">
			</div>
		</div>
	</body>
	<script type="text/javascript">
		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;
			//(2)减去盒子高度的一般,保证鼠标在中间
			var maskX = x - mask.offsetWidth / 2;
			var maskY = y - mask.offsetHeight / 2;
			//(3)黄色盒子不能出小盒子范围。
			//(3).1 如果x坐标小于0就让停在0的位置,大于小盒子宽度减去遮挡层宽度
			//遮挡层最大移动距离(正方形,长宽一样)
			var maskMax = preview_img.offsetWidth - mask.offsetWidth;
			if (maskX < 0) {
				maskX = 0;
			} else if (maskX >= maskMax) {
				maskX = maskMax;
			}
			//(3).2 如果y坐标小于0就让停在0的位置,大于小盒子高度减去遮挡层高度
			if (maskY < 0) {
				maskY = 0;
			} else if (maskY >= maskMax) {
				maskY = maskMax;
			}

			mask.style.left = maskX + 'px';
			mask.style.top = maskY + 'px';

			//大图片移动距离=遮挡层移动距离*大图片最大移动距离 / 遮挡层最大移动距离
			var bigImg = document.querySelector('.bigImg');
			//大图最大移动距离
			var bigMax = bigImg.offsetWidth - big.offsetWidth;
			//大图片移动距离 bigX 和 bigY
			var bigX = maskX * bigMax / maskMax;
			var bigY = maskY * bigMax / maskMax;

			bigImg.style.left = -bigX + 'px';
			bigImg.style.top = -bigY + 'px';
		})
	</script>

二、元素可视区client系列

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

flexible源码分析

(function flexible(window,document){
	// 获取html的根元素
	var docEl=document.documentElement;
	// dpr像素比
	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元素文字大小
	function setRumUnit(){
		var rem=docEl.clientWidth/10;
		docEl.style.fontSize=rem+'px';
	}
	setRumUnit();
	
	// 当我们页面尺寸大小发生变化,要重新设置rem的大小
	window.addEventListener('resize',setRumUnit());
	//pageshow使我们重新加载页面触发的事件
	window.addEventListener('pageshow',function(e){
		// e.persisted返回true,就是说如果这个页面是从缓冲取过来的页面,也需要重新计算rem大小
		if(e.persisted){
			setRumUnit();
		}
	})
	
	// 有些移动端不支持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))

立即执行函数

  1. 立即函数不需要调用,立马自己执行
  2. 也可以传递参数
  3. 立即执行函数最大的作用是:独立创建了一个作用域,里面所有变量都是局部变量,不会有命名冲突情况
//创建方法
<script>
	(function(){})()
	(function(){}())
</script>
//也可以传递参数
<script>
	(function sum(a,b){
		console.log(a+b);
	})(1,2)    //第二个小括号可以看做调用函数,可以传递参数
</script>
//独立创建了一个作用域,里面所有变量都是局部变量,不会有命名冲突情况
<script>
	(function sum(a,b){
		var num=10;
	})();

	(function sum(a,b){
		var num=10;
	}())
</script>

pageshow事件

三、元素滚动scroll系列

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

 

1. 页面被卷去头部

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

 案例——仿淘宝固定右侧侧边栏

  

 

    <style type="text/css">
		.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: lightgrey;
		}

		.banner {
			height: 400px;
			background-color: lightblue;
		}

		.main {
			height: 1000px;
			background-color: yellowgreen;
		}

		span {
			display: none;
			position: absolute;
			bottom: 0;
		}
	</style>
	<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>
	<script type="text/javascript">
		var sliderbar = document.querySelector('.slider-bar');
		var banner = document.querySelector('.banner');
		
        //banner.offsetTop就是卷去的高度,要写在滚动外面
		var bannerTop = banner.offsetTop;
		//当我们侧边栏固定定位后应保持的top值
		var sliderbarTop=sliderbar.offsetTop-bannerTop;
		
        //获取主体元素
		var main=document.querySelector('.main');
		var goBack=document.querySelector('.goBack');
		var mainTop=main.offsetTop;
		
		//2.页面滚动事件
		document.addEventListener('scroll', function() {
			//3. 当我们页面卷去的头部大于 banner.offsetTop时, 侧边栏变成固定定位
			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>

 

 2. 页面被卷曲头部兼容性方案

  

四、三大系列总结

 

 主要用法:

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

 

五、mouseenter和mouseover事件

  • 当鼠标移动到元素上会触发mouseenter和mouseover事件
  • 当mouseover鼠标经过自身盒子会触发,经过子盒子还会触发
  • mouseenter只会经过自身盒子才触发
  • 之所以这样,mouseenter不会冒泡
  • 跟mouseenter搭配 鼠标离开事件 mouseleave 同样不会冒泡

六、动画函数封装

1. 动画实现原理

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

实现步骤:

  1. 获取当前盒子位置
  2. 让盒子在当前位置加上一个像素
  3. 利用定时器不断重复这个操作
  4. 加上结束定时器条件
  5. 注意!此元素要添加定位才行

常见网页特效案例

2. 动画函数的简单封装

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

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

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

    <body>
		<button type="button">go</button>
		<div></div>
		<span></span>
		<script type="text/javascript">
			//简单函数封装,obj目标对象,target目标位置
			//当我们不断点击按钮,这个元素会越来越快,应为开启太多定时器
			function animate(obj, target) {
				obj.timer = setInterval(function() {
					if (obj.offsetLeft >= target) {
						//停止动画,本质停止定时器
						clearInterval(obj.timer);
					}
					obj.style.left = obj.offsetLeft + 2 + 'px';
				}, 30);
			}

			var div = document.querySelector('div');
			var span = document.querySelector('span');
			var btn = document.querySelector('button');
			animate(div, 300);
			btn.addEventListener('click', function() {
				animate(span, 200);
			})
		</script>
	</body>

以上优化有bug:当我们不断点击按钮,这个元素会越来越快,应为开启太多定时器

function animate(obj, target) {
	//先清楚以前定时器,只保留当前的定时器
	clearInterval(obj.timer);
	obj.timer = setInterval(function() {
		if (obj.offsetLeft >= target) {
			//停止动画,本质停止定时器
			clearInterval(obj.timer);
		}
		obj.style.left = obj.offsetLeft + 2 + 'px';
	}, 30);
}

解决方案:先清除以前定时器,只保留当前的定时器

4. 缓动效果动画

缓动动画就是让元素运动有所变化。

思路:

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来
  2. 核心算法:(目标值-现在的位置)/ 10 ,作为每次移动的步长
  3. 停止条件:让当前盒子位置等于目标位置就停止定时器
  4. 注意步长值需要取整
function animate(obj, target) {
	clearInterval(obj.timer);
	obj.timer = setInterval(function() {
		//步长值写定时器里面
		var step=(target-obj.offsetLeft)/10;
		if (obj.offsetLeft >= target) {
			clearInterval(obj.timer);
		}
		//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
		obj.style.left = obj.offsetLeft + step + 'px';
	}, 15);
}

5. 动画函数在多个目标之间移动

可以让动画函数从800移动到500.

当我们点击按钮时,判断步长时正值还是负值

  1. 如果是正值,则步长往大了取整。
  2. 如果是负值,则步长向小了取整。
<body>
<button class="btn500">go500</button>
<button class="btn800">go800</button>
<div></div>
<span></span>
<script type="text/javascript">
	//简单函数封装,obj目标对象,target目标位置
	//当我们不断点击按钮,这个元素会越来越快,应为开启太多定时器
	function animate(obj, target) {
		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);
			}
			//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
			obj.style.left = obj.offsetLeft + step + 'px';
		}, 15);
	}

	var div = document.querySelector('div');
	var span = document.querySelector('span');
	var btn500 = document.querySelector('.btn500');
	var btn800 = document.querySelector('.btn800');
	animate(div, 300);
	btn500.addEventListener('click', function() {
		animate(span, 500);
	})
	btn800.addEventListener('click', function() {
		animate(span, 800);
	})
</script>
</body>

6. 给动画函数添加回调参数

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

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

<body>
<button class="btn500">go500</button>
<button class="btn800">go800</button>
<div></div>
<span></span>
<script type="text/javascript">
	//简单函数封装,obj目标对象,target目标位置
	//当我们不断点击按钮,这个元素会越来越快,应为开启太多定时器
	function animate(obj, target, callback) {
		// console.log(callback);//等价于 callback=function(){}调用的时候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);
				//回调函数写道定时器里面
				if(callback){
					callback();
				}
			}
			//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
			obj.style.left = obj.offsetLeft + step + 'px';
		}, 15);
	}

	var div = document.querySelector('div');
	var span = document.querySelector('span');
	var btn500 = document.querySelector('.btn500');
	var btn800 = document.querySelector('.btn800');
	animate(div, 300);
	btn500.addEventListener('click', function() {
		animate(span, 500);
	})
	btn800.addEventListener('click', function() {
		animate(span, 800, function() {
			// alert('你好吗');
			span.style.backgroundColor='red';//等移动结束了再执行
		});
	})
</script>
</body>

7. 动画函数封装到单独JS文件里面

因为以后经常使用这个动画函数,可以单独封装到一个JS文件里面,使用的时候引用这个JS文件即可。

function animate(obj, target, callback) {
	// console.log(callback);//等价于 callback=function(){}调用的时候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);
			//回调函数写道定时器里面
			if (callback) {
				callback();
			}
		}
		//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
		obj.style.left = obj.offsetLeft + step + 'px';
	}, 15);
}

案例——右侧盒子滑动

<script src="js/animate.js" type="text/javascript" charset="utf-8"></script>

<style type="text/css">
	*{
		margin: 0;
		padding: 0;
	}
	.slider-bar{
		float: right;
		position: relative;
	}
	span{
		position: absolute;
		width: 40px;
		height: 40px;
		background-color: pink;
		right: 0;
		text-align: center;
	}
	.con{
		position: absolute;
		height: 40px;
		width: 200px;
		background-color: #0000FF;
		z-index: -1;
		text-align: left;
		right: -160px;
	}
</style>

<body>
	<div class="slider-bar">
		<span><-</span>
		<div class="con">问题反馈</div>
	</div>
</body>

<script type="text/javascript">
	//1.获取元素
	var sliderbar=document.querySelector('.slider-bar');
	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>

 

七、常见网页特效案例

案例——网页轮播图

轮播图也称焦点图

 

 

举报

相关推荐

0 条评论