河南手机网站建设公司哪家好网络销售是什么工作内容
PC端网页特效
1.元素偏移量 offset 系列
1.1 offset 概述
offset 翻译过来就是偏移量,我们使用offset系列相关属性可以动态
的得到该元素位置(偏移) 、大小等
- 获得元素距离带有定位父元素的位置
- 获得元素自身的大小(宽度高度)
- 注意:返回的数值都不带单位
offset 系列常用属性:
offset系列属性 | 作用 |
---|---|
element.offsetParent | 返回作为该元素带有定位的父级元素 如果父级都没有定位则返回body |
element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
element.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 |
element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 |
elelment.offsetHeight | 返回自身包括padding、边框、内容区的高度,返回数值不带单位 |
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>offset系列属性</title><style>* {padding: 0;margin: 0;}.father {position: relative;width: 300px;height: 300px;background-color: pink;margin: 200px 100px;}.son {width: 150px;height: 150px;background-color: purple;margin-left: 45px;}.w {/* width: 200px; */height: 200px;background-color: skyblue;margin: 0 auto 200px;padding: 10px;border: 15px solid red; }</style>
</head>
<body><div class="father"><div class="son"></div></div><div class="w"></div><script>// offset 系列var father = document.querySelector('.father');var son = document.querySelector('.son');var w = document.querySelector('.w');// 1. 可以得到元素的偏移 位置 返回的不带单位的数值console.log(father.offsetTop);console.log(father.offsetLeft);// 它以带有定位的父元素为准 如果没有父元素或者父元素没有定位 则以 body 为准console.log(son.offsetLeft);// 2. 可以得到元素的大小 宽度和高度 是包含padding + border + widthconsole.log(w.offsetWidth);console.log(w.offsetHeight);// 3. 返回带有定位的父元素 否则的是bodyconsole.log(son.offsetParent);console.log(son.parentNode); // 返回父元素 是最近一级父元素 不管有没有定位</script>
</body>
</html>
1.2 offset与style区别
offset
- offset可以得到任意样式表中的样式值
- offset系列获得的数值是没有单位的
- offsetWidth包含padding+border+width
- offsetWidth等属性是只读属性,只能获取不能赋值
所以,我们想要获取元素大小位置,有offset更合适
style
- style只能得到行内样式表中的样式
- style.width获得的是带有单位的字符串
- style.width获得不包含padding和border的值
- style.width是可读写属性,可以获取也可以赋值
所以,我们想要给元素更改值,则需要用style改变
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>offset和style的区别</title><style>.box {width: 200px;height: 200px;background-color: pink;padding: 10px;}</style>
</head>
<body><div class="box" style="width: 200px;"></div><script>var box = document.querySelector('.box');console.log(box.offsetWidth);console.log(box.style.width);</script>
</body>
</html>
案例:获取鼠标在盒子内的坐标
案例分析
- 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
- 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
- 其次得到盒子在页面中的距离(box.offsetLeft,box.offsetTop)
- 用鼠标距离页面的坐标减去盒子在页面中的距离,得到鼠标在盒子内的坐标
- 如果想要移动柜一下鼠标,就要获取最新的坐标,使用鼠标移动事件mousemove
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>计算鼠标在盒子内的坐标</title><style>.box {width: 300px;height: 300px;background-color: pink;margin: 200px 200px;}</style>
</head>
<body><div class="box"></div><script>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>
</html>
案例:模态框拖拽
弹出框,我们也称为模态框
- 点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层。
- 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮罩层。
- 鼠标放到模态层最上面一行,可以按住鼠标拖拽模态框在页面中移动。
- 鼠标松开,可以停止拖动模态框移动。
案例分析
- 点击弹出层,模态层和遮罩层就会显示出来display:block;
- 点击关闭按钮,模态框和遮罩层就会隐藏起来display:none;
- 在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标
- 触发事件是鼠标按下mousedown,鼠标移动mousemove鼠标松开mouseup
- 拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的lef和top值,这样模态框就可以跟着鼠标走了
- 鼠标按下触发的事件源是最上面一行,就是id为title
- 鼠标的坐标减去鼠标在盒子内的坐标,才是模态框真正的位置。
鼠标按下
,我们要得到鼠标在盒子的坐标。鼠标移动
,就让模态框的坐标设置为:鼠标坐标减去盒子坐标即可,注意移动事件写到按下事件里面。鼠标松开
,就停止拖拽,就是可以让鼠标移动事件解除
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>模态框拖拽</title><style>* {padding: 0;margin: 0;box-sizing: border-box;}.login-header {width: 100%;text-align: center;font-size: 24px;margin-top: 15px;}input {border: 0;}a {color: #000;text-decoration: none;}.login {display: none;width: 512px;height: 280px;position: fixed;border: #ebebeb solid 1px;left: 50%;top: 50%;background: #ffffff;box-shadow: 0px 0px 20px #ddd;z-index: 9999;transform: translate(-50%, -50%);text-align: center;}.login-title {text-align: center;font-size: 20px;margin: 15px 0 20px 0;}.login-title:hover {cursor: move;}.login-title span {position: absolute;top: -24px;right: -24px;width: 48px;height: 48px;line-height: 45px;text-align: center;font-size: 14px;background-color: #fff;border-radius: 50%;border: 1px solid #ccc;}.login-input {overflow: hidden;}.list-input {float: left;width: 350px;height: 35px;line-height: 35px;border: 1px solid #ebebeb;margin-bottom: 20px;text-indent: 6px; }.login-input label {float: left;width: 90px;padding-right: 10px;text-align: right;line-height: 35px;height: 35px;font-size: 14px;}.login-button {width: 50%;height: 42px;line-height: 42px;margin: 15px auto 0px;font-size: 14px;border: 1px solid #ebebeb;}.login-button a {display: block;}.login-bg {display: none;width: 100%;height: 100%;position: fixed;top: 0;left: 0;background: rgba(0, 0, 0, .3);}</style>
</head>
<body><div class="login-header"><a href="javascript:;" id="link">点击,弹出登录框</a></div><div class="login" id="login"><div class="login-title" id="title">登录会员<span><a href="javascript:void(0);" id="closeBtn" class="close-login">关闭</a></span></div><div class="login-input-content"><div class="login-input"><label for="">用户名: </label><input type="text" id="username" class="list-input" placeholder="请输入用户名" name="info[username]"> </div><div class="login-input"><label for="">登录密码: </label><input type="password" name="info[password]" id="password" class="list-input" placeholder="请输入登录密码"></div></div><div class="login-button" id="loginBtn"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div></div><!-- 遮盖层 --><div class="login-bg" id="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 让mask和login显示出来link.addEventListener('click', function() {mask.style.display = 'block';login.style.display = 'block';})// 3. 点击closeBtn 让mask和login隐藏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) 鼠标移动的时候,把鼠标在页面中的坐标减去鼠标在盒子内的坐标就是模态框的left和top值document.addEventListener('mousemove', move)function move(e) {login.style.left = e.pageX - x + 'px';login.style.top = e.pageY - y + 'px';} // (3) 鼠标弹起就让鼠标移动事件移除document.addEventListener('mouseup', function() {document.removeEventListener('mousemove', move);})})</script>
</body>
</html>
案例:仿京东放大镜
案例分析
- 整个案例可以分为三个功能模块
鼠标经过小图片盒子,黄色的遮挡层和大图片显示,离开隐藏2个盒子功能
- 就是显示与隐藏
黄色的遮挡层跟随鼠标功能
把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。
- 首先是获得鼠标在盒子的坐标
- 之后把数值给遮挡层作为left和top值
- 此时用到鼠标移动事件,但是还是在小图片盒子内移动
- 发现遮挡层位置不对,需要再减去盒子自身高度和宽度的一半。
- 遮挡层不能超出小图片盒子范围
- 如果小于零,就把坐标设置为0
- 遮挡层最大移动距离:小图片盒子宽度减去遮挡层盒子宽度
移动黄色遮挡层,大图片跟随移动功能
求大图片的移动距离公式
- 遮挡层移动距离/遮挡层最大移动距离 = 大图片移动距离/大图片最大移动距离
- 大图片移动距离 = 遮挡层移动距离*大图片最大移动距离/遮挡层最大移动距离
<div class="product_intro clearfix"><!-- 预览区域 --><div class="preview_wrap fl"><div class="preview_img"><img src="upload/s3.png" alt=""><div class="mask"></div><div class="big"><img src="upload/big.jpg" alt="" class="bigImg"></div></div><div class="preview_list"><a href="#" class="arrow_prev"></a><a href="#" class="arrow_next"></a><ul class="list_item"><li><img src="upload/pre.jpg" alt=""></li><li class="current"><img src="upload/pre.jpg" alt=""></li><li><img src="upload/pre.jpg" alt=""></li><li><img src="upload/pre.jpg" alt=""></li><li><img src="upload/pre.jpg" alt=""></li></ul></div></div><!-- 产品详细信息 --><div class="itemInfo_wrap fr"><div class="sku_name">Apple iPhone 6s(A1700)64G玫瑰金色 移动通信电信4G手机</div><div class="news">推荐选择下方[移动优惠购],手机套餐齐搞定,不用换号,每月还有花费返</div><div class="summary"><dl class="summary_price"><dt>价格</dt><dd><i class="price">¥5299.00 </i><a href="#">降价通知</a><div class="remark">累计评价612188</div></dd></dl><dl class="summary_promotion"><dt>促销</dt><dd><em>加购价</em> 满999.00另加20.00元,或满1999.00另加30.00元,或满2999.00另加40.00元,即可在购物车换 购热销商品 详情 》</dd></dl><dl class="summary_support"><dt>支持</dt><dd>以旧换新,闲置手机回收 4G套餐超值抢 礼品购</dd></dl><dl class="choose_color"><dt>选择颜色</dt><dd><a href="javascript:;" class="current">玫瑰金</a><a href="javascript:;">金色</a><a href="javascript:;">白色</a><a href="javascript:;">土豪色</a></dd></dl><dl class="choose_version"><dt>选择版本</dt><dd><a href="javascript:;" class="current">公开版</a><a href="javascript:;">移动4G</a></dd></dl><dl class="choose_type"><dt>购买方式</dt><dd><a href="javascript:;" class="current">官方标配</a><a href="javascript:;">移动优惠购</a><a href="javascript:;">电信优惠购</a></dd></dl><div class="choose_btns"><div class="choose_amount"><input type="text" value="1"><a href="javascript:;" class="add">+</a><a href="javascript:;" class="reduce">-</a></div><a href="#" class="addcar">加入购物车</a></div></div></div></div>
/*详情页的样式文件*/.de_container {margin-top: 20px;
}.crumb_wrap {height: 25px;
}.crumb_wrap a {margin-right: 10px;
}.preview_wrap {width: 400px;height: 590px;
}.preview_img {position: relative;height: 398px;border: 1px solid #ccc;
}.mask {display: none;position: absolute;top: 0;left: 0;width: 300px;height: 300px;background: #FEDE4F;opacity: .5;border: 1px solid #ccc;cursor: move;
}.big {display: none;position: absolute;left: 410px;top: 0;width: 500px;height: 500px;background-color: pink;z-index: 999;border: 1px solid #ccc;overflow: hidden;
}.big img {position: absolute;top: 0;left: 0;
}.preview_list {position: relative;height: 60px;margin-top: 60px;
}.list_item {width: 320px;height: 60px;margin: 0 auto;
}.list_item li {float: left;width: 56px;height: 56px;border: 2px solid transparent;margin: 0 2px;
}.list_item li.current {border-color: #c81623;
}.arrow_prev, .arrow_next {position: absolute;top: 15px;width: 22px;height: 32px;background-color: purple;
}.arrow_prev {left: 0;background: url(../img/arrow-prev.png) no-repeat;
}.arrow_next {right: 0;background: url(../img/arrow-next.png) no-repeat;
}.itemInfo_wrap {width: 718px;
}.sku_name {height: 30px;font-size: 16px;font-weight: 700;
}.news {height: 32px;color: #e12228;
}.summary dl {overflow: hidden;
}.summary dt, .summary dd {float: left;
}.summary dt {width: 60px;padding-left: 10px;line-height: 36px;
}.summary_price, .summary_promotion {position: relative;padding: 10px 0;background-color: #fee9eb;
}.price {font-size: 24px;color: #e12228;
}.summary_price a {color: #c81623;
}.remark {position: absolute;right: 10px;top: 20px;
}.summary_promotion {padding-top: 0;
}.summary_promotion dd {width: 450px;line-height: 36px;
}.summary_promotion em {display: inline-block;width: 40px;height: 22px;background-color: #c81623;text-align: center;line-height: 22px;color: #fff;
}.summary_support dd {line-height: 36px;
}.choose_color a {display: inline-block;width: 80px;height: 41px;background-color: #f7f7f7;border: 1px solid #ededed;text-align: center;line-height: 41px;
}.summary a.current {border-color: #c81623;
}.choose_version {margin: 10px 0;
}.choose_version a, .choose_type a {display: inline-block;height: 32px;padding: 0 12px;background-color: #f7f7f7;border: 1px solid #ededed;text-align: center;line-height: 32px;
}.choose_btns {margin-top: 20px;
}.choose_amount {position: relative;float: left;width: 50px;height: 46px;background-color: pink;
}.choose_amount input {width: 33px;height: 44px;border: 1px solid #ccc;text-align: center;
}.add, .reduce {position: absolute;right: 0;width: 15px;height: 22px;border: 1px solid #ccc;background-color: #f1f1f1;text-align: center;line-height: 22px;
}.add {top: 0;
}.reduce {bottom: 0;/*禁止鼠标样式*/cursor: not-allowed;/* pointer 小手 move 移动 */
}.addcar {float: left;width: 142px;height: 46px;background-color: #c81623;text-align: center;line-height: 46px;font-size: 18px;color: #fff;margin-left: 10px;font-weight: 700;
}.product_detail {margin-bottom: 50px;
}.aside {width: 208px;border: 1px solid #ccc;
}.tab_list {overflow: hidden;height: 34px;
}/*把背景颜色 底边框都给 li*/.tab_list li {float: left;background-color: #f1f1f1;border-bottom: 1px solid #ccc;height: 33px;text-align: center;line-height: 33px;
}/*鼠标单击 li 变化样式 背景变白色 去掉下边框 文字变颜色*/.tab_list .current {background-color: #fff;border-bottom: 0;color: red;
}.first_tab {width: 104px;
}.second_tab {width: 103px;border-left: 1px solid #ccc;
}.tab_con {padding: 0 10px;
}.tab_con li {border-bottom: 1px solid #ccc;
}.tab_con li h5 {/*超出的文字省略号显示*/white-space: nowrap;overflow: hidden;text-overflow: ellipsis;font-weight: 400;
}.aside_price {font-weight: 700;margin: 10px 0;
}.as_addcar {display: block;width: 88px;height: 26px;border: 1px solid #ccc;background-color: #f7f7f7;margin: 10px auto;text-align: center;line-height: 26px;
}.detail {width: 978px;
}.detail_tab_list {height: 39px;border: 1px solid #ccc;background-color: #f1f1f1;
}.detail_tab_list li {float: left;height: 39px;line-height: 39px;padding: 0 20px;text-align: center;cursor: pointer;
}.detail_tab_list .current {background-color: #c81623;color: #fff;
}.item_info {padding: 20px 0 0 20px;
}.item_info li {line-height: 22px;
}.more {float: right;font-weight: 700;font-family: 'icomoon';
}
window.addEventListener('load', function() {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';})preview_img.addEventListener('mousemove', function(e) {// (1). 先计算出鼠标在盒子内的坐标var x = e.pageX - this.offsetLeft;var y = e.pageY - this.offsetTop;// console.log(x, y);// (2) 减去盒子高度300的一半是150 就是我们mask最终的left和top值// (3) mask 移动的距离var maskX = x - mask.offsetWidth / 2;var maskY = y - mask.offsetHeight / 2;var moveWidthX = preview_img.offsetWidth - mask.offsetWidth;var moveWidthY = preview_img.offsetHeight - mask.offsetHeight;// 如果x坐标小于了0,就让他停在0的位置if (maskX <= 0) {maskX = 0;} else if (maskX >= moveWidthX) {maskX = moveWidthX;}if (maskY <= 0) {maskY = 0;} else if (maskY >= moveWidthY) {maskY = moveWidthY;}mask.style.left = maskX + 'px';mask.style.top = maskY + 'px';// 大图片移动距离 = 遮挡层移动距离*大图片最大移动距离/遮挡层最大移动距离// 大图var bigImg = document.querySelector('.bigImg');// 大图片最大移动距离var bigMax = bigImg.offsetWidth - big.offsetWidth;// 大图片移动距离 X Yvar bigX = maskX * bigMax / moveWidthX;var bigY = maskY * bigMax / moveWidthX;bigImg.style.left = -bigX + 'px';bigImg.style.top = -bigY + 'px';})
})
1.3 offsetX,offsetLeft,offsetWidth的区别
offsetX/offsetY
offsetX和offsetY表示(鼠标位置)相对于最近父级元素的坐标(无论父级是否定位)(不管是谁触发)
.big{width: 200px;height: 200px;border: 1px solid red;position: absolute;top: 100px;left: 100px;
}
.box{width: 100px;height: 100px;border: 1px solid green;margin-left: 50px;
}
<div class="big"><div class="box"></div>
</div>
var box = document.getElementsByClassName('box')[0];
var big = document.getElementsByClassName('big')[0];big.onclick = function(e){e = e || window.event;console.log(e.offsetX);//若点击位置在box里面则返回的数据是鼠标相对于box的坐标,若点击位置在big里面则返回的值是鼠标相对于big的坐标,和是谁触发事件的无关。
}
offsetLeft/offsetTop
元素相对于最近定位父级元素的坐标,若在所有的父级上都没有定位,则相对于整个文档
.big{width: 200px;height: 200px;border: 1px solid red;/* position: absolute; */}
.small{width: 100px;height: 100px;border: 1px solid green;
}
<div class="big"><div class="small"></div>
</div>
var small = document.getElementsByClassName('small')[0];
var big = document.getElementsByClassName('big')[0];console.log(small.offsetLeft); //不加position结果是9,加上position结果是0
offsetWidth/offsetHeight
返回元素的视觉尺寸(width+padding+border)
.big{width: 200px;height: 200px;border: 1px solid red;position: absolute;top: 100px;left: 100px;
}
.box{width: 100px;height: 100px;border: 1px solid green;margin-left: 50px;
}
<div class="big"><div class="box"></div>
</div>
var box = document.getElementsByClassName('box')[0];
var big = document.getElementsByClassName('big')[0];console.log(big.offsetWidth);//202
console.log(box.offsetWidth);//102
2.元素可视区 client系列
client
翻译过来就是客户端,我们使用client系列的相关属性来获取元素可视区的相关信息。通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
client系列属性 | 作用 |
---|---|
element.clientTop | 返回元素上边框的大小 |
element.clientLeft | 返回元素左边框的大小 |
element.clientWidth | 返回自身包括padding、内容区的宽度,不含边框,返回数值不带单位 |
element.clientHeight | 返回自身包括padding、内容区的高度,不包边框,返回数值不带单位 |
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>client系列</title><style>div {width: 200px;height: 200px;background-color: pink;border: 10px solid red;padding: 10px;}</style>
</head>
<body><div></div><script>// client 宽度 和 offsetWidth 最大的区别就是 不包含边框var div = document.querySelector('div');console.log(div.clientWidth);</script>
</body>
</html>
案例:淘宝flexible.js源码分析
立即执行函数(function(){})() 或者 (function() {}())
主要作用:创建一个独立的作用域。
下面三种情况都会刷新页面都会触发load事件。
- a标签的超链接
- F5或者刷新按钮(强制刷新)
- 前进后退按钮
但是火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了页面里。
所以此时后退按钮不能刷新页面。
此时可以使用pageshow事件来触发。这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中。pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件,注意这个事件给window添加。
(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 / 10docEl.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))
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>像素比和pageshow事件</title><script src="07-flexible分析.js"></script>
</head>
<body><script>console.log(window.devicePixelRatio);window.addEventListener('load', function () {alert(11);})</script><a href="http://www.itcast.cn">链接</a>
</body>
</html>
3.元素滚动 scroll系列
3.1 元素scroll系列属性
scroll
翻译过来就是滚动的,我们使用scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等。
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element.scrollWidth | 返回自身实际的宽度,不含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度,不含边框,返回数值不带单位 |
3.2 页面被卷去的头部
如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发onscroll事件。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>scroll系列</title><style>div {width: 200px;height: 200px;background-color: pink;border: 10px solid red;padding: 10px;overflow: auto;/* display: -webkit-box;-webkit-line-clamp: 10;-webkit-box-orient: vertical;text-overflow: ellipsis; */}</style>
</head>
<body><div>我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容</div><script>// scroll系列var div = document.querySelector('div');console.log(div.scrollHeight);console.log(div.clientHeight);// scroll滚动事件当滚动条发生变化的时候会触发的事件div.addEventListener('scroll', function() {console.log(div.scrollTop);})</script>
</body>
</html>
案例:仿淘宝固定右侧侧边栏
- 原先侧边栏是绝对定位
- 当页面滚动到一定位置,侧边栏改为固定定位
- 页面继续滚动,会让返回顶部显示出来
案例分析
- 需要用到页面滚动事件scroll因为是页面滚动,所以事件源是document
- 滚动到某个位置,就是判断页面被卷去的上部值
页面被卷去的头部:可以通过window.pageYOffset 获得
如果是被卷去的左侧 window.pageXOffset- 注意,元素被卷去的头部是
element.scrollTop
,如果是页面被卷去的头部则是window.pageXOffset
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
- 声明了DTD使用document.documentElement.scrollTop
- 未声明DTD,使用 document.body.scrollTop
- 新方法window.pageYOffset 和 window.pageXOffset, IE9开始支持
function() {return {left: window.pageXOffset || documentElement.scrollLeft || document.body.scrollLeft || 0,top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0};}
// 使用的时候 getScroll().left
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>仿淘宝固定侧边栏</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: 1000px;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>// 1. 获取元素var sliderbar = document.querySelector('.slider-bar');var banner = document.querySelector('.banner');// 获取 main 主体元素var main = document.querySelector('.main');var goBack = document.querySelector('.goBack');var mainTop = main.offsetTop;// banner.offsetTop 就是被卷去的头部的大小 一定要写到滚动事件的外面var bannerTop = banner.offsetTop;// 当我们侧边栏固定定位之后应该变化的数值var sliderbarTop = sliderbar.offsetTop - bannerTop;// 2. 页面滚动事件 scrolldocument.addEventListener('scroll', function() {// console.log(11);// window.pageYOffset 页面被卷去的头部// console.log(window.pageYOffset);// 3. 当我们页面被卷去的头部大于等于了301 此时 侧边栏就要改为固定定位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>
</body>
</html>
三大系列总结
三大系列大小对比 | 作用 |
---|---|
element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回值不带单位 |
element.clientWidth | 返回自身包括padding、内容区的宽度,不包含边框,返回数值不带单位 |
element.scrollWidth | 返回自身实践的宽度,不含边框,返回数值不带单位 |
他们主要用法:
- offset系列经常用于获得元素位置
offsetLeft offsetTop
- client经常用于获取元素大小
clientWidth clientHeight
- scroll经常用于获取滚动距离
scrollTop scrollLeft
注意页面滚动的距离
通过window.pageXOffset 获得
mouseenter和mouseover的区别
mouseenter 鼠标事件
- 当鼠标移动到元素上时就会触发mouseenter事件
- 类似mouseover,它们两者之间的差别是
- mouseover鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
- 之所以这样,就是因为mouseenter不会冒泡
- 跟mouseenter搭配 鼠标离开 mouseleave 同样不会冒泡
<!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>mouseenter和mouseover的区别</title><style>.father {width: 400px;height: 400px; background-color: pink;margin: 0 auto;}.son {width: 200px;height: 200px;background-color: purple;}</style>
</head>
<body><div class="father"><div class="son"></div></div><script>var father = document.querySelector('.father');var son = document.querySelector('.son');father.addEventListener('mouseenter', function () {console.log(11);})</script>
</body>
</html>
4.动画函数封装
4.1 动画实现原理
核心原理
:通过定时器 setInterval() 不断移动盒子位置。
实现步骤:
- 获得盒子当前位置
- 让盒子在当前位置加上1个移动距离
- 利用定时器不断重复这个操作
- 加一个结束定时器的条件
- 注意此元素需要添加定位,才能使用element.style.left
<!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>动画原理</title><style>div {position: absolute;width: 100px;height: 100px;background: pink;transition: all .3;}</style>
</head><body><div></div><script>// 1. 获得盒子当前位置// 2. 让盒子在当前位置加上1个移动距离// 3. 利用定时器不断重复这个操作// 4. 加一个结束定时器的条件// 5. 注意此元素需要添加定位,才能使用element.style.leftvar div = document.querySelector('div');var timer = setInterval(function() {if (div.offsetLeft >= 400) {// 停止动画 本质是停止定时器clearInterval(timer);} else {div.style.left = div.offsetLeft + 4 + 'px';}}, 30)</script>
</body></html>
4.2 动画函数简单封装
注意函数需要传递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>简单动画函数封装</title><style>div {position: absolute;width: 100px;height: 100px;background: pink;transition: all .3;}span {position: relative;top: 200px;display: block;width: 150px;height: 150px;background-color: purple;}</style>
</head>
<body><div></div><span>夏雨荷</span><script>// 简单动画函数封装 obj目标对象 target目标位置function animate(obj, target) {var timer = setInterval(function() {if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(timer);} else {obj.style.left = obj.offsetLeft + 4 + 'px';}}, 30)}var div = document.querySelector('div');var span = document.querySelector('span');animate(div, 500);animate(span, 300);</script>
</body>
</html>
4.3 动画函数给不同元素记录不同定时器
如果多个元素都使用这个动画函数,每次都要var声明定时器。我们可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。
核心原理:利用js是一门动态语言,可以很方便的给当前对象添加属性。
<!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>给不同对象添加不同定时器</title><style>div {position: absolute;width: 100px;height: 100px;background: pink;transition: all .3;}span {position: relative;top: 200px;display: block;width: 150px;height: 150px;background-color: purple;}</style>
</head>
<body><button>点击之后夏雨荷才走</button><div></div><span>夏雨荷</span><script>// var obj = {};// obj.name = 'andy';// 简单动画函数封装 obj目标对象 target目标位置// 给不同的元素指定了不同的定时器function animate(obj, target) {// 当我们不断地点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器// 解决方案就是让我们元素只有一个定时器执行// 先清除以前的定时器,只保留当前的一个定时器执行clearInterval(obj.timer)obj.timer = setInterval(function() {if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);} else {obj.style.left = obj.offsetLeft + 4 + 'px';}}, 30)}var div = document.querySelector('div');var span = document.querySelector('span');var btn = document.querySelector('button');animate(div, 500);btn.addEventListener('click', function() {animate(span, 300);})</script>
</body>
</html>
4.4 缓动效果原理
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
思路:
- 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
- 核心算法:(目标值-现在的位置)/ 10 作为每次移动的距离 步长
- 停止的条件是:让当前盒子位置等于目标位置就停止定时器
- 注意步长值需要取整
<!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>缓动动画原理</title><style>div {position: absolute;width: 100px;height: 100px;background: pink;transition: all .3;}span {position: relative;top: 200px;display: block;width: 150px;height: 150px;background-color: purple;}</style>
</head><body><button>点击之后夏雨荷才走</button><div></div><span>夏雨荷</span><script>// var obj = {};// obj.name = 'andy';// 简单动画函数封装 obj目标对象 target目标位置// 思路:// 1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。// 2. 核心算法:(目标值 - 现在的位置)/ 10 作为每次移动的距离 步长// 3. 停止的条件是:让当前盒子位置等于目标位置就停止定时器function animate(obj, target) {// 当我们不断地点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器// 解决方案就是让我们元素只有一个定时器执行// 先清除以前的定时器,只保留当前的一个定时器执行clearInterval(obj.timer)obj.timer = setInterval(function () {// 步长值写到定时器里面var step = Math.ceil((target - obj.offsetLeft) / 10)if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);} else {// 把每次加1的这个步长值改为慢慢变小的步长值 步长公式:(目标值 - 现在的位置)/ 10 obj.style.left = obj.offsetLeft + step + 'px';}}, 15)}var div = document.querySelector('div');var span = document.querySelector('span');var btn = document.querySelector('button');animate(div, 800);btn.addEventListener('click', function () {animate(span, 1000);})// 匀速动画 就是 盒子当前的位置加上一个固定值 10// 缓动动画就是盒子当前的位置 + 变化的值(目标值 - 现在的位置)/ 10 </script>
</body></html>
4.5 动画函数多个目标值之间移动
可以让动画函数从800移动到500。
当我们点击按钮时候,判断步长是正值还是负值
- 如果是正值,则步长往大了取整。
- 如果是负值,则步长向小了取整
<!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>缓动动画原理</title><style>span {position: absolute;top: 200px;left: 0;display: block;width: 150px;height: 150px;background-color: purple;}</style></head><body><button class="btn500">点击夏雨荷500</button><button class="btn800">点击夏雨荷800</button><span>夏雨荷</span><script>// var obj = {};// obj.name = 'andy';// 简单动画函数封装 obj目标对象 target目标位置// 思路:// 1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。// 2. 核心算法:(目标值 - 现在的位置)/ 10 作为每次移动的距离 步长// 3. 停止的条件是:让当前盒子位置等于目标位置就停止定时器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);} else {// 把每次加1的这个步长值改为慢慢变小的步长值 步长公式:(目标值 - 现在的位置)/ 10 obj.style.left = obj.offsetLeft + step + 'px';}}, 15)}var span = document.querySelector('span');var btn500 = document.querySelector('.btn500');var btn800 = document.querySelector('.btn800');btn500.addEventListener('click', function () {animate(span, 500);})btn800.addEventListener('click', function () {animate(span, 800);})// 匀速动画 就是 盒子当前的位置加上一个固定值 10// 缓动动画就是盒子当前的位置 + 变化的值(目标值 - 现在的位置)/ 10 </script></body></html>
4.6 动画函数添加回调函数
回调函数原理
:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调
。
回调函数写的位置:定时器结束的位置。
<!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>i缓动动画添加回调函数</title><style>span {position: absolute;top: 200px;left: 0;display: block;width: 150px;height: 150px;background-color: purple;color: #fff;}</style>
</head>
<body><button class="btn500">点击夏雨荷500</button><button class="btn800">点击夏雨荷800</button><span>夏雨荷</span><script>// var obj = {};// obj.name = 'andy';// 简单动画函数封装 obj目标对象 target目标位置// 思路:// 1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。// 2. 核心算法:(目标值 - 现在的位置)/ 10 作为每次移动的距离 步长// 3. 停止的条件是:让当前盒子位置等于目标位置就停止定时器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();}} else {// 把每次加1的这个步长值改为慢慢变小的步长值 步长公式:(目标值 - 现在的位置)/ 10 obj.style.left = obj.offsetLeft + step + 'px';}}, 15)}var span = document.querySelector('span');var btn500 = document.querySelector('.btn500');var btn800 = document.querySelector('.btn800');btn500.addEventListener('click', function () {animate(span, 500, function() {span.style.backgroundColor = 'red';});})btn800.addEventListener('click', function () {animate(span, 800, function () {span.style.background = 'pink';});})// 匀速动画 就是 盒子当前的位置加上一个固定值 10// 缓动动画就是盒子当前的位置 + 变化的值(目标值 - 现在的位置)/ 10 </script>
</body>
</html>
4.7 动画函数封装到单独js文件里面
因为以后经常使用这个动画函数,可以单独封装到一个js文件里面,使用的时候引用这个js文件即可
<!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>引用animate动画函数</title><style>.sliderbar {position: absolute;top: 45%;right: 0;}.sliderbar span {position: relative;display: block;width: 40px;height: 40px;line-height: 40px;background: purple;text-align: center;color: #fff;z-index: 1;transition: all .4;}.con {position: absolute;top: 0;/* left: 0; */width: 200px;height: 40px;line-height: 40px;text-align: center;background: purple;color: #fff;}</style><script src="03-animate.js"></script>
</head>
<body><div class="sliderbar"><span>←</span><div class="con">问题反馈</div></div><script>// 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧// 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧var sliderbar = document.querySelector('.sliderbar');var con = document.querySelector('.con');sliderbar.addEventListener('mouseenter', function() {// animate(obj, target, callback)animate(con, -160, function() {// 当我们动画执行完毕就把 ← 改为 →sliderbar.children[0].innerHTML = '→'});})sliderbar.addEventListener('mouseleave', function() {animate(con, 0, function() {sliderbar.children[0].innerHTML = '←'});})</script>
</body>
</html>
5.常见网页特效案例
案例:网页轮播图
轮播图也称为焦点图,是网页中比较常见的网页特效。
功能需求:
-
鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
- 因为js较多,我们单独新建js文件夹,再新建js文件,引入页面中。
- 此时需要添加load事件
鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
- 显示隐藏display按钮。
window.addEventListener('load', function () {// 1. 获取元素var arrow_l = document.querySelector('.arrow-l');var arrow_r = document.querySelector('.arrow-r');var focus = document.querySelector('.focus');// 2. 鼠标经过focus模块,就显示隐藏左右箭头focus.addEventListener('mouseenter', function () {arrow_l.style.display = 'block';arrow_r.style.display = 'block';})focus.addEventListener('mouseleave', function () {arrow_l.style.display = 'none';arrow_r.style.display = 'none';}) })
-
点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理。
-
图片播放的同时,下面小圆圈模块跟谁一起变化。
-
点击小圆圈,可以播放相应图片。
动态生成小圆圈
- 核心思路:小圆圈的个数要跟图片张数一致
- 所以首先先得到ul里面图片的张数(图片放入li里面,所以就是li的个数)
- 利用循环动态生成小圆圈(这个小圆圈要放入ol里面)
- 创建节点createElement(‘li’)
- 插入节点ol.appendChild(li)
- 第一个小圆圈需要添加current类
// 3.动态生成小圆圈 有几张图片,我就生成几个小圆圈var ul = focus.querySelector('ul');var ol = focus.querySelector('.circle');// ul 移动的距离 小圆圈的索引号 乘以图片的宽度 注意是负值var focusWidth = focus.offsetWidth;// console.log(ul.children.length);for (var i = 0; i < ul.children.length; i++) {// 创建一个livar li = document.createElement('li');// 将创建的li插入到ol里// 记录当前小圆圈的索引号 通过自定义属性li.setAttribute('index-data', i)ol.appendChild(li);}// 把ol里面的第一个小li设置类名为 currentol.children[0].className = 'current';
-
鼠标不经过轮播图,轮播图也会自动播放图片。
-
鼠标经过,轮播图模块,自动播放停止。
- 小圆圈的排他思想
- 点击当前小圆圈,就添加current类
- 其余的小圆圈就移除这个current类
- 注意:我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了
// 4. 小圆圈的排他思想 我们可以在生成的同时直接绑定事件li.addEventListener('mouseover', function () {// 干掉所有人 把所有的小li清除 current类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下我自己 当前的小li添加current类名this.className = 'current';})
点击小圆圈滚动图片
- 此时用到animate动画函数,将js文件引入(注意,因为index.js依赖animate.js所以,animate.js要写到index.js上面)
- 使用动画函数的前提,该元素必须要有定位
- 注意是ul移动而不是li
- 滚动图片的核心算法:点击某个小圆圈,就让图片滚动小圆圈的
索引号乘以图片的宽度
作为ul移动的距离 - 此时需要知道小圆圈的索引号,我们可以在生成小圆圈的时候,给他设置一个自定义属性,点击的时候获取这个自定义属性即可。
// 5. 点击小圆圈移动图片 当然移动的是ul// 当我们点击了某个小li 就拿到当前小li的索引号var index_data = this.getAttribute('index-data');// 当我们点击了某个小li 就要把这个小li的索引号给 numnum = index_data;// 当我们点击了某个小li 就要把这个小li的索引号给 circlecircle = index_data;// console.log(index_data); // console.log(focusWidth);animate(ul, -index_data * focusWidth)
点击右侧按钮一次,就让图片滚动一张。
- 声明一个变量num,点击一次,自增1,让这个变量乘以图片宽度,就是ul的滚动距离
- 图片无缝滚动原理
- 把ul第一个li复制一份,放到ul的最后面
- 当图片滚动到克隆的最后一张图片时,让ul快速的,不做动画的跳到最左侧:left为0
- 同时num赋值为0,可以从新开始滚动图片了
// 7. 点击右侧按钮 图片滚动一张var num = 0;// circle 控制小圆圈的播放var circle = 0;arrow_r.addEventListener('click', function () {// 如果走到了最后一张图片 此时我们的ul要快速复原 left改为0// console.log(num);if (num == ol.children.length) {ul.style.left = '0px';num = 0;}num++;animate(ul, -num * focusWidth)
克隆第一张图片
- 克隆ul第一个li cloneNode() 加 true 深克隆 复制里面的子节点 false 浅克隆
// 6. 克隆第一张图片(li),放到ul最后面var first = ul.children[0].cloneNode(true);ul.appendChild(first);
- 点击右侧按钮,小圆圈跟随变化
- 最简单的做法是再声明一个变量circle,每次点击自增1,注意左侧按钮也需要这个变量 ,因此要声明全局变量
- 但是图片有5张,我们小圆圈只有4个少一个,必须加一个判断条件
- 如果circle == 4 就从新复原为0
// 8. 点击右侧按钮,小圆圈跟随变化 可以再声明一个变量控制小圆圈的播放circle++;// 如果我们circle == 4 说明走到最后我们克隆的这张图片了,我们就复原if (circle == ul.children.length - 1) {circle = 0;}// 先清除其他小圆圈的current类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下当前小圆圈的current类名ol.children[circle].className = 'current';
- 添加一个定时器
- 自动播放轮播图,实际就类似点击了右侧按钮
- 此时我们使用
手动调用
右侧按钮点击事件
arrow_r.click() - 鼠标经过focus就停止定时器
- 鼠标离开就开启定时器
5.1 节流阀
防止轮播图按钮连续点击造成播放过快。
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量 var flag = true;
if (flag) {flag = false;do something} 关闭水龙头
利用回调函数动画执行完毕,flag = true 打开水龙头
<div class="main"><div class="focus fl"><!-- 左侧箭头 --><a href="javascript: void(0);" class="arrow-l"><</a><!-- 右侧箭头 --><a href="javascript: void(0);" class="arrow-r">></a><!-- 核心的滚动区域 --><ul><li><a href="#"><img src="upload/focus.png" 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>
.focus {overflow: hidden;position: relative;float: left;width: 721px;height: 455px;
}.focus ul {position: absolute;top: 0;left: 0;width: 500%;
}.focus ul li {float: left;
}.arrow-l,
.arrow-r {display: none;position: absolute;top: 50%;width: 36px;height: 50px;line-height: 48px;text-align: center;font-size: 26px;background: rgba(0,0,0, .2);color: #fff;transform: translate(0, -50%);z-index: 2;
}.focus .arrow-r {right: 0;
}.circle {position: absolute;bottom: 10px;left: 50px;
}.current {background-color: #fff;
}.circle li {float: left;width: 15px;height: 15px;margin: 0 6px;border: 2px solid #ccc;border-radius: 50%;cursor: pointer;
}
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();// }callback && callback();} else {// 把每次加1的这个步长值改为慢慢变小的步长值 步长公式:(目标值 - 现在的位置)/ 10 obj.style.left = obj.offsetLeft + step + 'px';}}, 15)
}
window.addEventListener('load', function () {// 1. 获取元素var arrow_l = document.querySelector('.arrow-l');var arrow_r = document.querySelector('.arrow-r');var focus = document.querySelector('.focus');// 2. 鼠标经过focus模块,就显示隐藏左右箭头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)})// 3.动态生成小圆圈 有几张图片,我就生成几个小圆圈var ul = focus.querySelector('ul');var ol = focus.querySelector('.circle');// ul 移动的距离 小圆圈的索引号 乘以图片的宽度 注意是负值var focusWidth = focus.offsetWidth;// console.log(ul.children.length);for (var i = 0; i < ul.children.length; i++) {// 创建一个livar li = document.createElement('li');// 将创建的li插入到ol里// 记录当前小圆圈的索引号 通过自定义属性li.setAttribute('index-data', i)ol.appendChild(li);// 4. 小圆圈的排他思想 我们可以在生成的同时直接绑定事件li.addEventListener('click', function () {// 干掉所有人 把所有的小li清除 current类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下我自己 当前的小li添加current类名this.className = 'current';// 5. 点击小圆圈移动图片 当然移动的是ul// 当我们点击了某个小li 就拿到当前小li的索引号var index_data = this.getAttribute('index-data');// 当我们点击了某个小li 就要把这个小li的索引号给 numnum = index_data;// 当我们点击了某个小li 就要把这个小li的索引号给 circlecircle = index_data;// console.log(index_data); // console.log(focusWidth);animate(ul, -index_data * focusWidth)})}// 把ol里面的第一个小li设置类名为 currentol.children[0].className = 'current';// 6. 克隆第一张图片(li),放到ul最后面var first = ul.children[0].cloneNode(true);ul.appendChild(first);// 7. 点击右侧按钮 图片滚动一张var num = 0;// circle 控制小圆圈的播放var circle = 0;// flag 节流阀var flag = true;arrow_r.addEventListener('click', function () {if (flag) {flag = false;// 如果走到了最后一张图片 此时我们的ul要快速复原 left改为0// console.log(num);if (num == ol.children.length) {ul.style.left = '0px';num = 0;}num++;animate(ul, -num * focusWidth, function () {flag = true; // 打开节流阀})// 8. 点击右侧按钮,小圆圈跟随变化 可以再声明一个变量控制小圆圈的播放circle++;// 如果我们circle == 4 说明走到最后我们克隆的这张图片了,我们就复原// if (circle == ul.children.length - 1) {// circle = 0;// }circle == ul.children.length - 1 ? circle = 0 : circle;circleChange()}})// 9. 左侧按钮arrow_l.addEventListener('click', function () {if (flag) {flag = false;// 如果走到了最后一张图片 此时我们的ul要快速复原 left改为0// console.log(num);if (num == 0) {num = ul.children.length - 1;ul.style.left = -num * focusWidth + 'px';}num--;animate(ul, -num * focusWidth, function() {flag = true;})// 8. 点击右侧按钮,小圆圈跟随变化 可以再声明一个变量控制小圆圈的播放circle--;// 如果我们circle < 0 说明是第一张图片 则小圆圈要改为第四个小圆圈(3)// if (circle < 0) {// circle = ol.children.length - 1;// }circle = circle < 0 ? ol.children.length - 1 : circle;circleChange()}})function circleChange() {// 先清除其他小圆圈的current类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = '';}// 留下当前小圆圈的current类名ol.children[circle].className = 'current';}// 10. 自动播放轮播图var timer = setInterval(function () {arrow_r.click();}, 2000)
})
案例:返回顶部
滚动窗口至文档中的特定位置。
window.scroll(x, y)
注意里面的x和y不跟单位
- 带有动画的返回顶部
- 此时我们可以继续使用我们封装的动画函数
- 只需要把所有的left相关的值改为跟页面垂直滚动距离相关就可以了
- 页面滚动了多少,可以通过window.pageYOffset得到
- 最后是页面滚动,使用window.scroll(x, y)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>返回顶部案例</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: 1000px;background-color: yellowgreen;}span {display: none;position: absolute;bottom: 0;}.goBack {cursor: pointer;}</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>// 1. 获取元素var sliderbar = document.querySelector('.slider-bar');var banner = document.querySelector('.banner');// 获取 main 主体元素var main = document.querySelector('.main');var goBack = document.querySelector('.goBack');var mainTop = main.offsetTop;// banner.offsetTop 就是被卷去的头部的大小 一定要写到滚动事件的外面var bannerTop = banner.offsetTop;// 当我们侧边栏固定定位之后应该变化的数值var sliderbarTop = sliderbar.offsetTop - bannerTop;// 2. 页面滚动事件 scrolldocument.addEventListener('scroll', function () {// console.log(11);// window.pageYOffset 页面被卷去的头部// console.log(window.pageYOffset);// 3. 当我们页面被卷去的头部大于等于了301 此时 侧边栏就要改为固定定位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';}})// 3. 当我们点击了返回顶部模块,就让窗口滚动到页面的最上方goBack.addEventListener('click', function () {// 里面的 x 和 y 不跟单位 直接写数字即可 // window.scroll(0, 0);animate(window, 0);});// 动画函数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 - window.pageYOffset) / 10;step = step > 0 ? Math.ceil(step) : Math.floor(step);if (window.pageYOffset == target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);// 回调函数写到定时器结束里面callback && callback();} else {// 把每次加1的这个步长值改为慢慢变小的步长值 步长公式:(目标值 - 现在的位置)/ 10 // obj.style.left = window.pageYOffset + step + 'px';window.scroll(0, window.pageYOffset + step)}}, 15)}</script>
</body></html>
案例:筋斗云案例
- 鼠标经过某个小li,筋斗云跟着到当前小li位置
- 鼠标离开这个小li,筋斗云复原为原来的位置
- 鼠标点击了某个小li,筋斗云就会留在点击这个小li的位置
案例分析
- 利用动画函数做动画效果
- 原先筋斗云的起始位置是0
- 鼠标经过某个小li,把当前的offsetLeft位置作为目标值即可
- 鼠标离开某个小li,就把目标值设为0
- 如果点击了某个小li,就把当前的位置存储起来,当鼠标离开,筋斗云就回到这个位置
<!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>筋斗云案例</title><style>* {margin: 0;padding: 0;}body {background: #000;}a {color: #000;text-decoration: none;text-align: center;}li {list-style: none;}.c-nav {position: relative;margin: 100px auto;width: 1000px;height: 42px;background: #fff url(images/rss.png) no-repeat right center;border-radius: 6px;}.c-nav ul {position: absolute;}.c-nav ul li {float: left;margin: 10px 11px;}.cloud {position: absolute;top: 0;left: 0;display: block;width: 83px;height: 42px;background: url(images/cloud.gif) no-repeat;}.c-nav li a:hover {color: white;}.c-nav li.current a {color: #0dff1d;}</style><script src="03-animate.js"></script><script>window.addEventListener('load', function() {// 1. 获取元素var cloud = document.querySelector('.cloud');var c_nav = document.querySelector('.c-nav');var lis = c_nav.querySelectorAll('li');// 2. 给所有的小li绑定事件var current = 0;// 这个current作为筋斗云的起始位置for (var i = 0; i < lis.length; i++) {// (1) 鼠标经过把当前的小li的位置作为目标值lis[i].addEventListener('mouseenter', function() {animate(cloud, this.offsetLeft - 10);})// (2) 鼠标离开就回到起始的位置lis[i].addEventListener('mouseleave', function() {animate(cloud, current);})lis[i].addEventListener('click', function() {current = this.offsetLeft - 10;})}})</script>
</head>
<body><div class="c-nav" id="c_nav"><span class="cloud"></span><ul><li class="current"><a href="#">首页新闻</a></li><li><a href="#">师资力量</a></li><li><a href="#">活动策划</a></li><li><a href="#">企业文化</a></li><li><a href="#">招聘信息</a></li><li><a href="#">公司简介</a></li><li><a href="#">你是佩奇</a></li><li><a href="#">啥是佩奇</a></li></ul></div>
</body>
</html>
移动端网页特效
1. 触屏事件
1.1 触屏事件概述
移动端浏览器兼容性好,我们不需要考虑以前js的兼容性问题,可以放心的使用原生js书写效果,但是移动端也有自己独特的地方。比如触屏事件touch
(也称触摸事件),Android和iOS都有。
常见的触屏事件如下:
触屏touch事件 | 说明 |
---|---|
touchstart | 手指触摸到一个dom元素时触发 |
touchmove | 手指在一个dom元素上滑动时触发 |
touchend | 手指从一个dom元素上移开时触发 |
<!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>div {width: 100px;height: 100px;background-color: pink;}</style>
</head>
<body><div></div>
</body>
</html><script>var div = document.querySelector('div');// 1. 手指触摸DOM元素事件div.addEventListener('touchstart', function() {console.log('我摸了你');})// 2. 手指在DOM元素身上移动事件div.addEventListener('touchmove', function() {console.log('我继续摸');})// 3. 手指离开DOM元素事件div.addEventListener('touchend', function() {console.log('轻轻的我走了');})
</script>
1.2 触摸事件对象(TouchEvent)
TouchEvent是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触电,使开发者可以检测触点的移动,触点的增加和减少,等等
touchstart、touchmove、touchend三个事件都会各自有事件对象。
触摸事件对象重点我们看三个常见对象列表:
触摸列表 | 说明 |
---|---|
touches | 正在触摸屏幕的所有手指的一个列表 |
targetTouches | 正在触摸当前DOM元素上的手指的一个列表 |
changeTouches | 手指状态发生了改变的列表,从无到有,从有道无的变化 |
<!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>* {margin: 0;padding: 0;}div {width: 100px;height: 100px;background-color: pink;}</style>
</head>
<body><div></div>
</body>
</html><script>var div = document.querySelector('div');// 1. 手指触摸DOM元素事件div.addEventListener('touchstart', function(e) {// console.log(e);// touches 正在触摸屏幕的所有手指的列表// targetTouches 正在触摸当前DOM元素的手指列表// changeTouches 手指状态发生了改变的列表,从无到有或者从有到无// 因为我们一般都是触摸元素,所以最经常使用的是 targetTouchesconsole.log(e.targetTouches[0]);// targetTouches[0] 就可以得到正在触摸dom元素的第一个手指的相关信息比如手指的坐标等等})// 2. 手指在DOM元素身上移动事件div.addEventListener('touchmove', function() {})// 3. 手指离开DOM元素事件div.addEventListener('touchend', function() {console.log('轻轻的我走了');// 当我们手指离开屏幕的时候,就没有了touches和targetTouches列表,但是会有changeTouches})
</script>
1.3 移动端拖动元素
- touchstart、touchmove、touchend可以实现拖动元素
- 但是拖动元素需要当前手指的坐标值我们可以使用targetTouches[0]里面的pageX和pageY
- 移动端拖动的原理:手指移动中,计算出手指移动的距离。然后用盒子原来的位置+手指移动的距离
- 手指移动的距离:手指滑动中的位置减去 手指刚开始触摸的位置
拖动元素三部曲:
- 触摸元素touchstart:获取手指初始坐标,同时获得盒子原来的位置
- 移动手指touchmove:计算手指滑动的距离,并且移动盒子
- 离开手指touchend;
注意:手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动e.preventDefault();
<!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>div {position: absolute;top: 0;left: 0;width: 100px;height: 100px;background-color: pink;}</style>
</head><body><div></div>
</body></html><script>// 触摸元素touchstart:获取手指初始坐标,同时获得盒子原来的位置// 移动手指touchmove:计算手指滑动的距离,并且移动盒子// 离开手指touchend;var div = document.querySelector('div');var startX = 0;var startY = 0; // 手指初始坐标var x = 0;var y = 0; // 盒子原来的位置div.addEventListener('touchstart', function (e) {// 获取手指初始坐标startX = e.targetTouches[0].pageX;startY = e.targetTouches[0].pageY;x = this.offsetLeft;y = this.offsetTop;})div.addEventListener('touchmove', function (e) {// 阻止屏幕滚动的默认行为e.preventDefault();// 计算手指的移动距离: 手指移动之后的坐标减去手指初始的坐标var moveX = e.targetTouches[0].pageX - startX;var moveY = e.targetTouches[0].pageY - startY;// 移动盒子 盒子原来的位置 + 手指移动的距离this.style.left = x + moveX + 'px';this.style.top = y + moveY + 'px';})
</script>
案例:移动端轮播图
移动端轮播图功能和pc端基本一致。
- 可以自动播放图片
- 手指可以拖动播放轮播图
案例分析
自动播放功能
-
开启定时器
-
移动端移动,可以使用translate移动
自动播放功能-无缝滚动
- 注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断
- 此时需要添加检测过渡完成事件 transitionend
- 判断条件:如果索引号等于3说明走到最后一张图片,此时索引号要复原为0
- 此时图片,去掉过渡效果,然后移动
classList属性
classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。
该元素用于在元素中添加,移除以及切换css类。有以下方法
添加类:
element.classList.add(‘类名’);
focus.classList.add('current');
移除类:
element.classList.remove(‘类名’);
focus.classList.remove('current');
切换类:
element.classList.toggle(‘类名’);
focus.classList.toggle('current');
小圆点跟随变化效果
- 把ol里面li带有current类名的选出来去掉类名
remove
- 让当前索引号的小li加上current
add
但是,是等着过渡结束之后变化,所以这个写到transitionend事件里面
手指滑动轮播图
本质就是ul跟谁手指移动,简单来说就是移动端拖动元素
- 触摸元素touchstart: 获取手指初始坐标
- 移动手指touchmove:计算手指的滑动距离,并且移动盒子
- 手指离开touchend:根据滑动的距离分不同的情况
- 如果移动距离小于某个像素就回弹原来位置
- 如果移动距离大于某个像素就上一张下一张滑动。
window.addEventListener('load', function () {// 1. 获取元素var focus = document.querySelector('.focus');var ul = focus.children[0];var ol = focus.children[1];// 获取focus的宽度var w = focus.offsetWidth;// 2. 利用定时器自定轮播图片var index = 0;var flag = false;var timer = setInterval(function () {index++;var translateX = -index * w;ul.style.transition = 'all .4s';ul.style.transform = 'translate(' + translateX + 'px)';}, 2000)// 等着我们过渡完之后,再去判断监听过渡完成的事件 transitionendul.addEventListener('transitionend', function () {// 无缝滚动if (index >= ul.children.length - 2) {index = 0;// 去掉过渡效果这样让我们的ul快速的跳到目标位置ul.style.transition = 'none';// 利用最新索引号乘以宽度,去滚动图片var translateX = -index * w;ul.style.transform = 'translate(' + translateX + 'px)';} else if (index < 0) {index = ul.children.length - 3;ul.style.transition = 'none';// 利用最新索引号乘以宽度,去滚动图片var translateX = -index * w;ul.style.transform = 'translate(' + translateX + 'px)';}// (3) 小圆点跟随变化// 1. 把ol里面li带有current类名的选出来去掉类名 removeol.querySelector('li.current').classList.remove('current');// 2. 让当前索引号的小li加上current addol.children[index].classList.add('current');})// (4) 手指滑动轮播图// 1. 触摸元素touchstart: 获取手指初始坐标var startX = 0;var moveX = 0;ul.addEventListener('touchstart', function (e) {startX = e.targetTouches[0].pageX;// 手指触摸的时候停止定时器clearInterval(timer);})// 2. 移动手指touchmove:计算手指的滑动距离,并且移动盒子ul.addEventListener('touchmove', function (e) {e.preventDefault(); // 阻止屏幕滚动的行为// 计算移动距离moveX = e.targetTouches[0].pageX - startX;if (index >= ul.children.length - 2) {index = 0;// 去掉过渡效果这样让我们的ul快速的跳到目标位置ul.style.transition = 'none';// 利用最新索引号乘以宽度,去滚动图片var translateX = -index * w;ul.style.transform = 'translate(' + translateX + 'px)';} else if (index < 0) {index = ul.children.length - 3;ul.style.transition = 'none';// 利用最新索引号乘以宽度,去滚动图片var translateX = -index * w;ul.style.transform = 'translate(' + translateX + 'px)';}// 移动盒子: 盒子原来的位置 + 手指移动的距离var translateX = -index * w + moveX;// 手指拖动的时候,不需要动画效果所以要取消过渡效果ul.style.transition = 'none';ul.style.transform = 'translate(' + translateX + 'px)';flag = true; // 如果用户手指移动过,再去判断否则不做判断效果})// 手指离开touchend:根据滑动的距离分不同的情况 ul.addEventListener('touchend', function () {// (1) 如果移动距离大于50像素我们就播放上一张或者下一张if (flag) {if (Math.abs(moveX) > 50) {// 如果是右划就是播放上一张moveX是正值// if (moveX > 0) {// index--;// } else {// // 如果是左划就是播放上一张moveX是负值// index++;// }moveX > 0 ? index-- : index++;var translateX = -index * w;ul.style.transition = 'all .4s';ul.style.transform = 'translate(' + translateX + 'px)';} else {// (2) 如果移动距离小于50像素我们就回弹var translateX = -index * w;ul.style.transition = 'all .1s';ul.style.transform = 'translate(' + translateX + 'px)';}flag = false;}// 手指离开重启定时器clearInterval(timer);timer = setInterval(function () {index++;var translateX = -index * w;ul.style.transition = 'all .4s';ul.style.transform = 'translate(' + translateX + 'px)';}, 2000)})
})
案例:返回顶部
当页面滚动到某个地方,就显示,否则隐藏
点击可以返回顶部
案例分析
- 滚动某个地方显示
- 事件:scroll页面滚动事件
- 如果被卷去的头部(window.scrollY)大于某个数值
- 点击,window.scroll(0, 0) 返回顶部
2. 移动端常见特效
2.1 click延时解决方案
移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放(double tap to zoom)页面。
解决方案:
- 禁用缩放。浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟。
<meta name="viewport" content="user-scalable=no">
- 利用touch事件自己封装这个事件解决300ms延迟。
原理就是:
- 当我们手指触摸屏幕,记录当前触摸时间
- 当我们手指离开屏幕,用离开的时间减去触摸的时间
- 如果时间小于150ms,并且没有滑动屏幕,那么我们就定位为点击
// 封装tap,解决click 300ms 延时function tap(obj, callback) {var isMove = false;var startTime = 0; // 记录触摸时候的时间变量obj.addEventListener('touchstart', function(e) {startTime = Date.now(); // 记录触摸时间});obj.addEventListener('touchmove', function(e) {isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击})obj.addEventListener('touchend', function(e) {if (!isMove && (Date.now() - startTime < 150)) { // 如果手指触摸和离开时间小于150ms算点击callback && callback(); // 执行回调函数}isMove = false; // 取反 重置startTime = 0;})}tap(div, function() {// 执行代码})
- 使用插件。fastclick插件解决300ms延迟。
3. 移动端常用开发插件
3.1 什么是插件
移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我们完成操作,那么什么是插件呢?
js插件就是js文件,
它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。
特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
我们以前写的animate.js也算一个最简单的插件
fastclick插件解决300ms延迟。使用延时
GitHub官网地址:https://github.com/ftlabs/fastclick
3.2 插件的使用
- 引入js文件
- 按照相关插件规定语法使用
3.3 Swiper插件的使用
中文官网地址:https://www.swiper.com.cn/
- 引入插件相关文件
- 按照规定语法使用
3.4 其他移动端常见插件
- superslide:http://www.superslide2.com/
- iscroll:https://github.com/cubiq/iscroll
3.5 插件的使用总结
- 确认插件实现的功能
- 去官网查看使用说明
- 下载插件
- 打开demo实例文件,查看需要引入的相关文件,并且引入
- 复制demo实例文件中的结构html,样式css以及js代码
3.6 练习-移动端视频插件 zy.mdeia.js
H5给我们提供了video标签,但是浏览器的支持情况不同。
不同的.视频格式文件,我们可以通过source解决。
但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。
这个时候我们可以使用插件方式来制作。
4. 移动端常用开发框架
4.1 框架概述
框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套
较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。
插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
前端常用的框架有Bootstart、Vue、Angular、React
等。既能开发pc端,也能开发移动端
前端常用的移动端插件有swiper、superslide、iscroll
等。
框架:大而全,一整套解决方案
插件:小而专一,某个功能的解决方案
本地存储
1. 本地存储
随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案
本地存储特性
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage约5M、localStorage约20M
- 只能存储字符串,可以将对象JSON.stringify()编码后存储
fastclick插件解决300ms延迟。使用延时
GitHub官网地址:
2.window.sessionStorage
- 生命周期为关闭浏览器窗口
- 在同一个窗口(页面)下数据可以共享
- 以键值对的形式存储使用
存储数据:
sessionStorage.setItem(key, value)
获取数据:
sessionStorage.getItem(key)
删除数据:
sessionStorage.removeItem(key)
删除所有数据:
sessionStorage.clear()
<!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>本地存储之sessionStorage</title>
</head><body><input type="text" autofocus="autofocus"><button class="set">存储数据</button><button class="get">获取数据</button><button class="remove">删除数据</button><button class="del">清空所有数据</button>
</body></html><script>var ipt = document.querySelector('input');var set = document.querySelector('.set')var get = document.querySelector('.get')var remove = document.querySelector('.remove')var del = document.querySelector('.del')var flag = 0;var num = 0;set.addEventListener('click', function () {// 当我们点击了之后, 就可以把表单里面的数据存储起来// console.log(11);var val = ipt.valueif (ipt.value == '') {alert('请输入内容啦,小伙计')} else {if (flag === 0) {sessionStorage.setItem('username', val)flag = 1;ipt.value = ''} else if (flag === 1) {sessionStorage.setItem('pwd', val)flag = 2;ipt.value = ''} else if (sessionStorage.getItem('username') == null && sessionStorage.getItem('pwd') == null) {flag = 0} else {alert('超出存储数量了啦!')}}})get.addEventListener('click', function () {console.log(sessionStorage.getItem('username'));})remove.addEventListener('click', function () {if (sessionStorage.getItem('username') == null && sessionStorage.getItem('pwd') == null) {alert('没有数据可删了啦, 不要再点啦!')} else if (num === 0) {sessionStorage.removeItem('username')num = 1;ipt.value = ''} else {sessionStorage.removeItem('pwd')num = 0;ipt.value = ''}})del.addEventListener('click', function () {if (sessionStorage.getItem('username') == null && sessionStorage.getItem('pwd') == null) {alert('没有数据可删了啦, 不要再点啦!')} else {sessionStorage.clear()ipt.value = ''}})document.addEventListener('keyup', function (e) {if (e.keyCode === 13) {set.click()}})
</script>
3.window.localStorage
- 生命周期永久生效,除非手动删除否则关闭页面也会存在
- 可以多窗口(页面)共享(同一个浏览器可以共享)
- 以键值对的形式存储使用
存储数据:
localStorage.setItem(key, value)
获取数据:
localStorage.getItem(key)
删除数据:
localStorage.removeItem(key)
删除所有数据:
localStorage.clear()
<!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>本地存储之localStorage</title>
</head><body><input type="text" autofocus="autofocus"><button class="set">存储数据</button><button class="get">获取数据</button><button class="remove">删除数据</button><button class="del">清空所有数据</button>
</body></html><script>var ipt = document.querySelector('input');var set = document.querySelector('.set')var get = document.querySelector('.get')var remove = document.querySelector('.remove')var del = document.querySelector('.del')var flag = 0;var num = 0;set.addEventListener('click', function () {// 当我们点击了之后, 就可以把表单里面的数据存储起来// console.log(11);var val = ipt.valueif (ipt.value == '') {alert('请输入内容啦,小伙计')} else {if (flag === 0) {localStorage.setItem('username', val)flag = 1;ipt.value = ''} else if (flag === 1) {localStorage.setItem('pwd', val)flag = 2;ipt.value = ''} else if (localStorage.getItem('username') == null && localStorage.getItem('pwd') == null) {flag = 0;} else {alert('超出存储数量了啦!')}}})get.addEventListener('click', function () {console.log(localStorage.getItem('username'));})remove.addEventListener('click', function () {if (localStorage.getItem('username') == null && localStorage.getItem('pwd') == null) {alert('没有数据可删了啦, 不要再点啦!')} else if (num === 0) {localStorage.removeItem('username')num = 1;ipt.value = ''} else {localStorage.removeItem('pwd')num = 0;ipt.value = ''}})del.addEventListener('click', function () {if (localStorage.getItem('username') == null && localStorage.getItem('pwd') == null) {alert('没有数据可删了啦, 不要再点啦!')} else {localStorage.clear()ipt.value = ''}})document.addEventListener('keyup', function (e) {if (e.keyCode === 13) {set.click()}})
</script>
案例:记住用户名
如果勾选记住用户名,下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名
案例分析
- 把数据存起来,用到本地存储
- 关闭页面,也可以显示用户名,所以用到localStorage
- 打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框
- 当复选框发生改变的时候change事件
- 如果勾选,就存储,否则就移除
<!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>记住用户名</title>
</head>
<body><input type="text" id="username"><label for="remember"><input type="checkbox" name="checkbox" id="remember">记住用户名</label>
</body>
</html><script>var username = document.querySelector('#username')var remember = document.querySelector('#remember')if (localStorage.getItem('username')) {username.value = localStorage.getItem('username')remember.checked = true;}remember.addEventListener('change', function() {if (this.checked) {localStorage.setItem('username', username.value)} else {localStorage.removeItem('username')}})
</script>
案例:toDoList
- 刷新页面不会丢失数据,因此需要用到本地存储localStorage
- 核心思路:不管按下回车,还是点击复选框,都是把本地存储的数据加载到页面中,这样保证刷新页面不会丢失数据
- 存储的数据格式:var todolist = [{title: ‘xxx’, done: false}]
- 注意点1:本地存储localStorage里面只能存储字符串格式,因此需要把对象转换为字符串JSON.stringify(data)
- 注意点2:获取本地存储数据,需要把里面的字符转换为对象格式JSON.parse()我们才能使用里面的数据
toDoList按下回车把新数据添加到本地存储里面
- 切记:页面中的数据,都要从本地存储里面获取,这样刷新页面不会丢失数据,所以先要把数据保存到本地存储里面
- 利用事件对象.keyCode判断用户按下回车键(13)
- 声明一个数组,保存数据
- 先要把读取本地存储原来的数据(声明函数getData()),放到这个数组里面
- 之后把最新从表单获取过来的数据,追加到数组里面
- 最后把数组存储给本地存储(声明函数setDate())
toDoList本地存储数据渲染加载到页面
- 因为后面也会经常渲染加载操作,所以声明一个函数load,方便后面调用
- 先要读取本地存储数据。(数据不要忘记转换为对象格式)
- 之后遍历这个数据,有几条数据,就生成几个小li添加到ol里面
- 每次渲染之前,先把原先里面ol的内容清空,然后渲染加载最新的数据
toDoList删除操作
- 点击里面的a链接,不是删除的li,而是删除本地存储对应的数据
- 核心原理:先获取本地存储数据,删除对应的数据,保存给本地存储,重新渲染列表li
- 我们可以给链接自定义属性记录当前的索引号
- 根据这个索引号删除相关的数据----数组的splice(i, 1)方法
- 存储修改后的数据,然后存储给本地存储
- 重新渲染加载数据列表
- 因为a是动态创建的,我们要写在创建a的函数内
toDoList正在进行和已完成选项操作
- 当我们点击了小的复选框,修改本地存储数据,再重新渲染数据列表
- 点击之后,获取本地存储数据
- 修改对应数据属性done为当前复选框的checked状态
- 之后保存数据到本地存储
- 重新渲染加载数据列表
- load加载函数里面,新增一个条件,如果当前数据的done为true就是已经完成的,就把列表渲染加载到ul里面
- 如果当前数据的done为false,则是待办事项,就把列表渲染加载到ol里面
toDoList统计正在进行个数和已经完成个数
- 在我们load函数里面操作
- 声明2个变量:todoCount待办个数 doneCount已完成个数
- 当进行遍历本地存储数据的时候,如果数据done为false,则todoCount++,否则doneCount++
- 最后修改相应的元素innerHTML
toDolist修改本地存储数据
- 当我们点击了li,就让当前小li中的p隐藏,插入input标签在p后面并把p元素内的元素给input的value
- 循环当前小li中所有的input标签判断个数,小于2的话就插入input防止重复插入
- 利用focus()方法自动获取input焦点,selectionEnd = 文本框内容的长度,将文本框内光标定位于最后
- 将修改之后的内容给本地存储对应的数据并保存到本地存储
<!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>ToDoList—最简单的待办事项列表</title><meta name="description" content="ToDoList无须注册即可使用,数据存储在用户浏览器的html5本地数据库里,是最简单最安全的待办事项列表应用!"><link rel="stylesheet" href="css/todolist.css"><script src="js/todolist.js"></script>
</head><body><header><section><form action="javascript:;" id="form"><label for="title">ToDoList</label><input type="text" id="title" name="title" placeholder="添加ToDo" required="required"autofocus="autofocus" maxlength="40" autocomplete="off"></form></section></header><section><h2>正在进行 <span id="todocount"></span></h2><ol id="todolist" class="demo-box"></ol><h2>已经完成 <span id="donecount"></span></h2><ul id="donelist"></ul></section><footer>Copyright © 2014 todolist.cn <a href="javascript: void(0);" class="clear">clear</a></footer>
</body></html>
* {margin: 0;padding: 0;box-sizing: border-box;
}body {background-color: #cdcdcd;font-family: 'Courier New', Courier, monospace;
}button,
input {border: 0;
}label {cursor: pointer;
}header {width: 100vw;height: 52px;background-color: #323232;
}header section {color: #ddd;font-size: 24px;
}header section input {float: right;width: 60%;height: 27px;padding-left: 6px;margin-top: 13px;border-radius: 6px;border: 1px solid #ccc;box-shadow: 0 0 6px -2px rgba(0, 0, 0, .9) inset;outline: none;
}section {width: 650px;height: 52px;line-height: 52px;margin: 0 auto;
}li {overflow: hidden;text-overflow: ellipsis;position: relative;width: 100%;height: 40px;line-height: 40px;font-size: 16px;background-color: #fff;border: 1px solid #ccc; border-radius: 4px;cursor: move;list-style: none;margin-top: 10px;box-shadow: 0 6px 6px -1px rgba(0, 0, 0, .2);color: #666;
}li::before {content: '';position: absolute;top: 0;left: 0;width: 6px;height: 40px;background-color: #629a9c;border-radius: 4px 0 0 4px;border: 1px solid #ccc;
}li input {position: absolute;top: 8px;left: 24px;width: 24px;height: 24px;cursor:pointer;opacity: .6;outline: none;
}li input[type='text'] {width: 64%;height: 24px;font-size: 14px;font-family: 'Courier New', Courier, monospace;color: #000;border: 1px solid #4f4f4f;border-radius: 4px;margin-left: 40px;padding: 8px;
}li p {margin-left: 72px;
}li a {position: absolute;top: 7px;right: 8px;width: 24px;height: 24px;line-height: 24px;text-align: center;color: #fff;font-size: 14px;font-weight: bold;background-color: #999;border-radius: 50%;text-decoration: none;
}#donelist li {background-color: #e6e6e6;color: #676767;opacity: .9;
}section h2 {position: relative;margin: 20px 0;
}#todocount,
#donecount {position: absolute;top: 16px;right: 0;width: 24px;height: 24px;line-height: 22px;text-align: center;font-size: 12px;color: #666;background-color: #e6e6fa;border: 1px solid #ccc;border-radius: 50%;
}#donelist li::before {background-color: #b3b3b3;
}footer {position: fixed;top: 90%;left: 50%;margin-top: 20px;color: #666;font-size: 14px;transform: translateX(-50%);
}footer a {text-decoration: none;color: #999;
}
window.addEventListener('load', function () {var ipt = document.querySelector('input') // 获取input输入框var ol = document.querySelector('#todolist') // 获取正在进行下面的olvar ul = document.querySelector('#donelist') // 获取已经完成下面的ulvar ToDoList = document.querySelector('label') // 获取ToDoListconsole.log(ToDoList);// 获取正在进行数量统计var todocount = document.querySelector('#todocount')// 获取已经完成数量统计var donecount = document.querySelector('#donecount')// 获取clear清除所有数据链接var clear = document.querySelector('.clear')// 定义全局变量var index = 0;var li = null;var checkbox = null;var p = null;var as = null;var text = null;// 禁止用户选中document.addEventListener('selectstart', function(e) {e.preventDefault();})// 点击ToDoList之后刷新页面ToDoList.addEventListener('click', function() {location.reload()})// 页面加载完毕时调用一次页面渲染函数load()ipt.addEventListener('keyup', function (e) {// 判断用户是否按下了回车键if (e.keyCode === 13) {// 判断输入框内容是否为空if (ipt.value !== '') {// 获取本地存储的数据var data = getDate('todolist')// console.log(data);// 将输入框中的内容取过来插入给datadata.push({ title: this.value, done: false })// console.log(data);// 将data里面的数据保存进本地存储setDate('todolist', data)// 重新渲染页面load()// 清空输入框内容ipt.value = ''} else {// 如果为空弹出警示框, 提醒用户输入内容alert('请输入内容啦!笨蛋!')}}})// 清除本地存储所有数据clear.addEventListener('click', function () {localStorage.clear()// 重新渲染页面load()})// 读取本地存储数据函数function getDate(name) {var data = localStorage.getItem(name)// console.log(data);if (data !== null) {return JSON.parse(data)} else {return []}}// 保存本地存储数据函数function setDate(name, ele) {localStorage.setItem(name, JSON.stringify(ele))}// 页面渲染函数function load() {// 获取本地存储的数据var date = getDate('todolist')// 声明变量统计正在进行的数量var todoCount = 0;// 声明变量统计已经完成的数量var doneCount = 0;// console.log(date);// 每次生成元素渲染页面时, 清空ol和ul里面的内容ol.innerHTML = ''ul.innerHTML = ''// 动态生成元素函数function generate(ele) {// 动态生成li、input、p以及a标签li = document.createElement('li')checkbox = document.createElement('input');p = document.createElement('p')as = document.createElement('a')// 为每个a链接的href做赋值操作as.href = 'javascript: void(0);';as.innerHTML = '×' // 每个a链接的内容设置为×// 默认隐藏aas.style.display = 'none'// 将本地存储数组对象中所有title属性的值赋值给pp.innerHTML = date[i].title// 设置input的样式为复选框checkbox.type = 'checkbox'// 将复选框插入到li的最前面li.insertBefore(checkbox, li.children[0])// 将p插入到复选框的后面li.insertBefore(p, li.children[1])// 将a插入到li的最后面li.appendChild(as)// 将生成的li插入到ele里面ele.insertBefore(li, ele.children[0])}for (var i = 0; i < date.length; i++) {// console.log(date[i].done);// generate(ol)// 判断本地存储数据中的done是否为true, 如果是就生成元素到ul(已经完成)里面, 并且修改复选框为选定状态和让已经完成数量+1if (date[i].done) {generate(ul)checkbox.checked = truedoneCount++} else {// 反之, 则生成元素到ol(正在进行)中也让正在进行数量统计加1generate(ol)todoCount++}// 给a设置自定义属性as.setAttribute('data-index', i)// 为动态生成的a链接绑定点击事件, 注意一定要写在创建a链接的for循环里as.addEventListener('click', function () {// 得到当前a链接的索引号index = this.getAttribute('data-index')// console.log(index);// 删除当前a链接对应的本地存储数据date.splice(index, 1)// 将删除后的数组重新赋值给本地存储// console.log(date[index]);setDate('todolist', date)// 重新渲染页面load()})// 为动态生成的复选框绑定点击事件, 注意点同上面a链接checkbox.addEventListener('change', function () {// console.log(this.checked);// console.log(date);// 得到当前a链接的索引号var index = this.nextElementSibling.nextElementSibling.getAttribute('data-index')// console.log(index);// console.log(date[index].done);// 将对应本地存储数据的done属性修改为当前复选框的选定状态date[index].done = this.checkedsetDate('todolist', date)load()})// 给每个li标签绑定点击事件li.addEventListener('click', function () {// 创建input标签text = document.createElement('input')// 获取当前li标签所在的a的索引号, 从而得到li的索引var index = this.children[2].getAttribute('data-index')// 获取当前li里面的p元素var p = this.children[1];// 获取当前li里面所有的input标签var texts = this.querySelectorAll('input');// 判断input标签个数, 如果小于2则像li里面插入textif (texts.length < 2) {this.insertBefore(text, this.children[2])}// console.log(p);// console.log(index);// 将p元素的内容赋值给text的valuetext.value = p.innerHTML// 设置input样式为文本框text.type = 'text'// 自动获取焦点text.focus()// 将input内部光标定位到input内容部分最后面text.selectionStart = text.selectionEnd = text.value.length; // 这一步虽然是多余的但是是本着练习语法的心态写上的,欸嘿~// 默认全选中input内容// text.select();// this.replaceChild(text, p) // text替换p元素// 隐藏p元素p.style.display = 'none'// 选中内容从最前面到内容长度的一半位置 // text.setSelectionRange(0, text.value.length / 2)text.addEventListener('blur', function () {// 如果当前的表单的值不为空就修改数据if (this.value != '') {date[index].title = this.value;// 将修改之后的数据插入本地存储setDate('todolist', date)load()} else {alert('请输入内容啦!笨蛋!');load();}})// 文本框绑定键盘事件text.addEventListener('keyup', function (e) {// 用户按下回车就执行以下代码if (e.keyCode === 13) {// 将修改之后的数据插入本地存储setDate('todolist', date)load(); }})})// 鼠标经过li显示ali.addEventListener('mouseover', function () {this.lastElementChild.style.display = 'block'})// 鼠标经过li隐藏ali.addEventListener('mouseout', function () {this.lastElementChild.style.display = 'none'})}// 将计算好的数量统计分别赋值给正在进行和已经完成donecount.innerHTML = doneCounttodocount.innerHTML = todoCount}
})
数据可视化项目
1. 什么是数据可视化
1.1 数据可视化
- 数据可视化主要目的:借助于图形化手段,清晰有效地传达与沟通信息。
- 数据可视化可以把数据从冰冷的数字转换成图形,揭示蕴含在数据中的规律和道理。
1.2 数据可视化的场景
目前互联网公司通常有这么几个大类的可视化需求:
- 通用报表
- 移动端图表
- 大屏可视化
- 图编辑&图分析
- 地里可视化
1.3 常见的数据可视化库
- D3.js 目前Web端评价最高的JavaScript可视化工具库(入手难)
ECharts.js 百度出品的一个开源JavaScript数据可视化库
Highcharts.js 国外的前端数据可视化库,非商用免费,被许多国外大公司所使用
- AntV 蚂蚁金服全新一代数据可视化解决方案
- 等等
Highcharts和ECharts就像是Office和WPS的关系
1.4 小结
- 数据可视化主要目的:借助于图形化手段,清晰有效地传达与沟通信息
- 数据可视化在我们互联网公司中经常用于通用数据报表,移动端图表,大屏可视化,图编辑等
- 数据可视化库有很多,接下来我们重点学习ECharts
2. 数据可视化项目概述
2.1 项目目的
市场需求:
应对现在数据可视化的趋势,越来越多企业需要在很多场景(营销数据,生成数据,用户数据)下使用,**可视化图标
**来展示提现数据,让数据更加直观,数据特点更加突出
学习阶段需求:
项目对我们同学来说,起着**承上启下
**的作用。
承上
- 复习以前学习内容
- HTML5 + CSS3 布局相关技术
- JavaScript \ jQuery 相关技术
启下
- 为学习服务器编程做铺垫
- 如果把服务器里面的数据渲染到页面中?
2.3 项目技术
- HTML5 + CSS3 布局
- CSS3动画、渐变
- flex布局和rem适配方案
图片边框border-image
ES6模板字符
ECharts可视化库等等
2.4 小结
- 数据可视化项目展示
- 学习这个项目的目的:市场需求和学习阶段需求
- 项目用到的技术:以前学习过的技术和新技术
- CSS3动画、渐变
- jQuery库 + 原生JavaScript
- flex布局和rem适配方案
图片边框border-image
ES6模板字符
ECharts可视化库等等
3. ECharts简介
ECharts是一个使用JavaScript实现的开源可视化库,可以流畅的运行在pc和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图标。
官网地址:
https://www.echartsjs.com/zh/index.html
官网地址:
https://echarts.apache.org
- 丰富的可视化类型
- 多种数据格式支持
- 流数据的支持
- 移动端优化
- 跨平台使用
- 绚丽的特效
- 详细的文档说明
4. ECharts的基本使用
4.1 ECharts使用五步曲
注意:这里只要求记住ECharts使用的步骤,具体修改定制稍后会讲。
-
步骤一:下载并引入echarts.js文件 ——————-> 图标依赖这个
js库
-
步骤二:准备一个具备大小的DOM容器 ———————-> 生成的图标会放入这个
容器
内 -
步骤三:初始化echarts实例对象 ————————–>
实例化
echarts对象 -
步骤四:指定配置项和数据(option)————————–> 根据具体需求修改
配置
选项 -
步骤五:将配置设置得echarts实例对象 ———————–> 让echarts对象根据修改好的配置
生效
-
准备一个具备大小的DOM容器
<div id="main" style="width: 600px; height: 400px;"></div>
- 初始化echarts实例对象
var myChart = echarts.init(document.getElementById('main'));
- 指定配置项和数据
// 指定图表的配置项和数据var option = {title: {text: 'ECharts 入门示例'},tooltip: {},legend: {data: ['销量']},xAxis: {data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']},yAxis: {},series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]};
4.3 相关配置讲解
- title:标题组件
- tooltip:提示框组件
- legend:图例组件
- grid:直角坐标系内绘图网格
- XAxis:直角坐标系grid中的x轴
- yAxis:直角坐标系grid中的y轴
- series:系列列表。每个系列通过type决定自己的图表类型(什么类型的图表)
- color:调色盘颜色列表
先了解以上9个配置的作用,其余配置还有具体细节通过查阅文档:文档菜单-配置项手册
学echarts关键在于学会查阅文档,根据需求修改配置
grid: {left: '0%',right: '0%',bottom: '3%',// 当刻度标签溢出的时候,grid区域是否包含坐标轴的刻度标签。如果为true则显示// 如果left right等设置为0%,刻度标签就溢出了,此时决定是否显示刻度标签containLabel: true}
series:系列列表
-
type:类型(什么类型的图表)比如line是折线bar柱形等
-
name:系列名称,用于tooltip的显示,legend的图例筛选变化
-
stack:数据堆叠。如果设置相同值,则会数据堆叠。
-
数据堆叠:第二个数据值 = 第一个数据值 + 第二个数据值
第三个数据值 = 第二个数据值 + 第三个数据值…. 依次叠加
如果给stack指定不同值或者去掉这个属性则不会发生数据堆叠
-
5. 数据可视化项目适配方案
5.1 项目需求
- 设计稿是1920px
- PC端适配:宽度在1024~1920之间页面元素宽高自适应
5.2 适配方案
flexible.js
检测浏览器宽度
修改html文字大小
rem单位
页面元素根据rem适配大小
配合cssrem插件
flex布局
页面快速布局
- flexible.js把屏幕分为24等份
- PC端的效果图是1920px
- cssrem插件的基准值是80px
- rem值自动生成
要把屏幕约束在1024~1920之间有适配
@media screen and (max-width: 1024px) {html {font-size: 42.66px!important;}}@media screen and (min-width: 1920px) {html {font-size: 80px!important;}}
6. 数据可视化项目开发
6.1 边框图片
- 边框图片的使用场景
- 边框图片的切图原理
- 边框图片语法规范
盒子大小不一,但是边框样式相同,此时就需要边框图片来完成
为了实现丰富多彩的边框效果,在CSS3中,新增了border-image属性,这个新属性允许指定一幅图像作为元素的边框。
1. 边框图片切图原理:(重要)
把四个角切出去(九宫格的由来),中间部分可以铺排,拉伸或者环绕。
2. 边框图片语法:(重要)
属性 | 描述 |
---|---|
border-image-source | 用在边框的图片的路径。(哪个图片?) |
border-image-slice | 图片边框向内偏移。(裁剪的尺寸,一定不加单位,上右下左顺序) |
border-image-width | 图片边框的宽度(需要加单位)(不是边框的宽度,是边框图片的宽度) |
border-image-repeat | 图像边框是否应平铺(repeat)、铺满(round)或拉伸(stretch)默认拉伸 |
6.2 公共面板样式开发
面板类:.panel
border-image-slice:按照 上右下左顺序切割
/* 公共面板样式 */
.panel {position: relative;border: 15px solid transparent;border-width: .6375rem .475rem .25rem 1.65rem;border-image-source: url(../images/border.png);border-image-slice: 51 38 20 132;margin-bottom: .25rem;
} .panle h3 {color: #fff;font-size: .35rem;margin-bottom: .15rem;
}.inner {position: absolute;top: -.6375rem;right: -.475rem;bottom: -0.25rem;left: -1.65rem;padding: .3rem .45rem;
}
6.3 通过类名调用字体图标
- HTML页面引入字体图标中css文件。
- 标签直接调用图标对应的类名即可。(类名在css文件中标注)
引入css文件和声明字体图标的时候,一定注意路径问题。
6.4 立即执行函数用法
JS文件中,会有大量的变量命名,特别是ECharts使用中,需要大量初始化ECharts对象?
为了防止变量名冲突(变量污染)我们采用立即执行函数策略:
(function () {}) ();
(function () {var num = 10;}) ();(function () {var num = 10;}) ();
6.5 无缝滚动原理
- 先克隆marquee里面所有的行(row)
- 通过css3动画滚动marquee
- 鼠标经过marquee就停止动画
animation-play-state: paused;
// 先克隆marquee里面所有的行(row)var rows = null;var newRows = null;for (var i = 0; i < marquees.length; i++) {rows = marquees[i].querySelectorAll('.row')for (var j = 0; j < rows.length; j++) {newRows = rows[j].cloneNode(true);marquees[i].appendChild(newRows);}}
.marquee-view .marquee {position: absolute;display: flex;flex-direction: column;width: 100%;animation: move 15s linear infinite;
}/* 通过css3动画滚动marquee */
@keyframes move {from {}to {transform: translateY(-50%);}
}/* 鼠标经过marquee就停止动画 */
.marquee-view .marquee:hover {animation-play-state: paused;
}
6.6 点位分析模块-使用ECharts图表
- 先官网找到类似的图表引入到页面中
- 根据需求修改具体的配置
1. 引入图表
// 点位分布统计模块(function() {// 1. 实例化ECharts对象var myCharts = echarts.init(document.querySelector('.pie'));// 2. 指定配置项和数据var option = {tooltip: {trigger: 'item',formatter: '{a} <br/>{b} : {c} ({d}%)'},series: [{name: 'Area Mode',type: 'pie',radius: [20, 140],center: ['75%', '50%'],roseType: 'area',itemStyle: {borderRadius: 5},data: [{ value: 30, name: 'rose 1' },{ value: 28, name: 'rose 2' },{ value: 26, name: 'rose 3' },{ value: 24, name: 'rose 4' },{ value: 22, name: 'rose 5' },{ value: 20, name: 'rose 6' },{ value: 18, name: 'rose 7' },{ value: 16, name: 'rose 8' }]}]};// 3. 配置项和数据给实例化对象myCharts.setOption(option);}) ();
2. 定制图表
第一步:参考官方列子,熟悉里面参数具体含义
var option = {tooltip: {trigger: 'item',formatter: '{a} <br/>{b} : {c} ({d}%)'},series: [{name: 'Area Mode',type: 'pie',radius: [20, 140],center: ['75%', '50%'],roseType: 'area',itemStyle: {borderRadius: 5},data: [{ value: 30, name: 'rose 1' },{ value: 28, name: 'rose 2' },{ value: 26, name: 'rose 3' },{ value: 24, name: 'rose 4' },{ value: 22, name: 'rose 5' },{ value: 20, name: 'rose 6' },{ value: 18, name: 'rose 7' },{ value: 16, name: 'rose 8' }]}]};
第二步:按照需求定制
- 需求1:颜色设置
color : ['#006cff', '#60cda0', '#ed8884', '#ff9f7f', '#0096ff', '#32c5e9', '#1d9dff']
- 需求2:修改饼形图大小(series对象)
radius: ['10%', '70%'],
- 需求3:数据使用更换(series对象里面data对象)
{ value: 20, name: '云南' },{ value: 26, name: '北京' },{ value: 24, name: '山东' },{ value: 25, name: '河北' },{ value: 20, name: '江苏' },{ value: 25, name: '浙江' },{ value: 30, name: '四川' },{ value: 42, name: '湖北' }
- 需求4:字体略小些 10px (series对象里面设置)
饼图图形上的文本标签可以控制饼形图的文字的一些样式。 label对象设置
series: [{// 图表名称name: '点位统计',// 图表类型type: 'pie',// 南丁格尔玫瑰图有两个圆,内圆半径10%,外圆半径70%// 饼形图半径。可以是像素,也可以是百分比(基于图表bom容器的半径 第一项是内半径,第二项是外半径)radius: ['10%', '70%'],// 图表中心位置left 50% top 50% 距离图表DOM容器center: ['50%', '50%'],// radius 半径模式,另外一种是area面积模式roseType: 'radius',// 数据集 value 数据的值 name 数据的名称data: [{ value: 20, name: '云南' },{ value: 26, name: '北京' },{ value: 24, name: '山东' },{ value: 25, name: '河北' },{ value: 20, name: '江苏' },{ value: 25, name: '浙江' },{ value: 30, name: '四川' },{ value: 42, name: '湖北' }],// 文本标签控制饼形图文字的相关样式,注意它是一个对象label: {fontSize: 10}}]
- 需求5:防止缩放的时候,引导性过长。引导线略短些(series对象里面的labelLine对象设置)
- 连接图表6px
- 连接文字8px
// 引导线调整labelLine: {// 连接扇形图线长length: 6,// 连接文字线长length2: 8}
6.7 ES6模板文字
var star = {name: "刘德华",age: 18};// 以前的写法 拼接的时候引号很容易出问题console.log("我的名字是" + star.name + "我的年龄是" + star.age);// ES6 模板字符写法console.log(`我的名字是${star.name}我的年龄是${star.age}`);console.log(`<span>${star.name}</span><span>${star.age}</span>`);</script>