原生JS轮播-各种效果的极简实现
寒假持续摸鱼中此为老早以前博客的重写,当时还是分开写的,这里汇总重写,正好复习一遍
春招我来了!
所有有意思的,一股脑先扔进收藏,然后再也不看哈哈,真是糟糕。
今日事,今日毕,说起来容易。
当时竟然不是用markdown
写的!
当时使用var还需要解决必报的问题!而如今使用ES6的let
,自带领域的感觉就是不一样!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
ul, li {
list-style: none;
}
body {
background-color: #000;
}
.wrap {
position: relative;
border: 8px solid #fff;
margin: 100px auto;
width: 400px;
height: 250px;
overflow: hidden;
}
.pic-group {
position: absolute;
top: -250px;
}
.pic-group li img {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="wrap">
<ul class="pic-group">
<li><img src="./pic1.jpg" alt=""></li>
<li><img src="./pic2.jpg" alt=""></li>
<li><img src="./pic3.jpg" alt=""></li>
<li><img src="./pic4.jpg" alt=""></li>
<li><img src="./pic5.jpg" alt=""></li>
</ul>
<ol class="num-group">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ol>
</div>
</body>
</html>
一.轮播是什么?
轮播其实就是一个定时变换的广告(卡片?图片?)。
在HTML结构上他们其实是ul
里面的li
。数据层面来说的话,他们就是list
的一条数据。
那么,这其中的变换其实也可以有很多种~你可能会知道现在的 vue-swiper
这么绚丽的轮播效果
- 滑动效果 --- position top位置的变化(意味着所有的
item
单/竖/横向排列) - 缓冲效果 --- 透明度变化(意味着一开始所有的
item
刚开始都是叠在一起的)
二.变化和上述的代码分析 --- 改变 top 则切换图片(我们以滑动效果来开刀)。
如上面的代码,只搭了个结构,我们看到wrap
层被设为了 1.position:relative
和 2.overflow:hidden
,本质上是为了子层的pic-group
服务的,我们设子层pic-group
为3.position:absolute
,这样的话 3 就可以根据 1和2 来达到 => 改变top
位置(top位置为图片高度的 n 倍哦!)
为了极简!!!!!我这里只设置了 .pic-group li img 的宽高。一般情况下其实每个外层元素都会写一个具体的高宽的。
- 为什么我给
.pic-group li img
设置display:block
?实际上呢,
img
它是行内元素。行内元素有默认的 4px 边距。去除的办法有font-size:0
等诸多方法,这里我追求代码极简。所以一句话,变成块级元素吧少年。 - 为什么我只给
.pic-group li img
设置了 宽高?
依旧为了极简~实际上应该根据要求都设置一下宽高的。这里的结果就是
a.内层img
宽高继承了最外的400 250
=>
b.继而作为li
的子层撑开了li
=>
c.li
再继续撑开.pic-group
层
=>
d.因为.pic-group
的高度被老大的overflow:hidden
限制了,最后就变成了一张图片。
原来的样子应该是好几层图片~
三.变化的契机 --- 谁来改变 top?
我们给我们的轮播案例加上数字,通过点击数字来按下改变世界的开关!
首先这是我们给数字添加的样式
.num-group {
position: absolute;
bottom: 0;
right: 0;
height: 20px;
}
.num-group li {
float: left;
width: 20px;
height: 20px;
line-height: 20px;
font-size: 10px;
margin-right: 10px;
background-color: #089e8a;
color: #eee;
text-align: center;
border-radius: 10px;
cursor: pointer;
opacity: 0.8;
}
.num-group li:hover {
box-shadow: 0 0 18px #000;
}
.num-group li.current {
opacity: 1;
color: #089a8a;
background-color: #000;
}
这里就不多解释了,上面是是样式的部分。
下面是JS的部分
// 首先,获取我们要改变的元素
const oImage = document.getElementsByClassName("pic-group")[0];
// 其次,获取改变世界的钥匙
const oNumber = document.getElementsByClassName("num-group")[0];
const numList = oNumber.getElementsByTagName("li");
// 接下来,便是世界改变的逻辑
for (let i = 0; i < numList.length; i++) {
// 关键:我们要把我们的 索引index 传进 onclick/onmouseover 里面
numList[i].onclick = () => {
// numList[i].className = "current";
clearNumState();
numList[i].className = "current";
const topValue = - i * 250;
oImage.style.top = topValue + "px";
}
}
// 每次新点击 num,先置空所有list的current的类,此后再添加当前索引
const clearNumState = () => {
for (const item of numList) {
item.className = "";
}
}
通过上面的代码我们已经可以实现点击数字按钮进行对图片的切换了.
改变世界的最终还是 JS,而不是CSS,我们用JS给数字们添加上事件。
四.用户体验为王! 下面就开始出现分歧了,如何来切换图片呢?
- 滑动的轮播
滑动轮播,所有的图原先是排列着的。通过绝对定位来控制。同时外层设置
overflow:hidden
top变化 -> 切图。
* 这里的难点主要是我们要在当前的基础top
上滚动,并且滚动的效果要平滑。
// 首先,获取我们要改变的元素
const oImage = document.getElementsByClassName("pic-group")[0];
// 其次,获取改变世界的钥匙
const oNumber = document.getElementsByClassName("num-group")[0];
const numList = oNumber.getElementsByTagName("li");
// 接下来,便是世界改变的逻辑
for (let i = 0; i < numList.length; i++) {
// 关键:我们要把我们的 索引index 传进 onclick/onmouseover 里面
numList[i].onmouseover = () => {
clearNumState();
show(i);
}
}
// 每次新点击 num,先置空所有list的current的类,此后再添加当前索引
const clearNumState = () => {
for (const item of numList) {
item.className = "";
}
}
// 滑动轮播,原则: 每次获取当前位置,每次滚动的时间应该一致。(这是效果)
// 即是说,差的越远,滚动越快。
// obj.offsetTop 指的是 obj 距离上方或者父级元素(position非static)的位置。
// 整型。单位像素。属性为只读!!只读!!只读!!
let timer = null;
const show = (index) => {
if (timer) clearInterval(timer);
const pre = oImage.offsetTop;
let now = pre;
const pos = -250 * index;
const dis = pos - pre;
const avg = dis / 10;
timer = setInterval(() => {
if (now === pos) {
clearInterval(timer);
} else {
now += avg;
console.log(now);
oImage.style.top = now + "px";
}
}, 20);
}
通过以上的代码,我们基本能够实现滑动轮播了,但是滑动的太僵硬了,没有一种平时物体滑动的那种惯性。
简单的来说,它是匀速运动的!
我们要让它先 加速运动 -> 匀速运动 -> 减速运动.
So!修改代码如下
let timer = null;
const show = (index) => {
numList[index].className = "current";
if (timer) clearInterval(timer);
const target = -250 * index;
let now = oImage.offsetTop;
timer = setInterval(() => {
if (now === target) {
clearInterval(timer);
} else {
now += move(target);
oImage.style.top = now + "px";
console.log(oImage.offsetTop);
}
}, 20);
}
// 加速运动 -> 均速运动 -> 减速运动
const move = (target) => {
// 当前值
const now = oImage.offsetTop;
const differ = target - now;
console.log(differ / 10);
const res = differ > 0 ? Math.ceil(differ / 10) : Math.floor(differ / 10);
// 为什么呢?唉,基础差导致这里还写错了。
// 如果是 -0.1,我们要让它为 -1,所以这里要用地板(返回的值更小)
// -1 < -0.1
// 如果是 0.1, 我们要让它为 1,所以这里要用天花板(返回的值更大)
// 0.1 < 1
return res;
}
- 缓冲的轮播
缓冲的轮播,其实就是渐隐效果的实现。这是一个透明度变化。
透明度变化 -> 切图 -> 所有的图原先都是在同一层的,就像叠起来的千层饼。
下面是CSS上的差别。
缓冲轮播的主要改动在这里,因为缓冲轮播是千层饼,所以我们不能让它排列,让它重叠!~
.pic-group li {
position: absolute;
}
.pic-group li.current {
opacity: 1;
}
在滑动中是下面这样的,注意下面这句话要删掉或是注视了。因为这里的absolute
会影响到子级 li
的absolute
。(加个 li, 完事儿)
.pic-group {
position: absolute;
}
然后下面是JS的一些差别,这个比较简单。如果滑动的话,是只要控制 外层的 ul滚动就可以了,
缓冲轮播的话则是要控制 imageList
里面的每一张图的透明度,所以这里多获取一个东西。
const numList = oNumber.getElementsByTagName("li");
五.重要知识点!!
时代在进步,刀耕火种的JS --- 谈谈重写之前用var
,重写之后用let
的变化
不用纠结闭包的问题了!!!!
哦草!
我他妈竟然忘了!
秀一秀古老的
onclick
for (var i = 0; i < oList.length; i++) {
oList[i].onclick = function () {
console.log(i);
}
}
这样写的后果是,无论怎么写,最后的i
的结果都会是oList.length
。
闭包(匿名函数)要使用外部作用于中变量的结果,这也是由于匿名函数本身无法传递参数,
故无法维护自己的作用域。
当函数调用外部变量就构成一个闭包,里面的变量会受到别的地方的影响。不可抗力!所以解决的方式是:构建一个只有匿名函数本身才可以访问的闭包,保存只供本身使用的变量。
- 古老的解决方案一.使用过去的 function 式函数,将 当前的 i 赋值给 index
for (var i = 0; i < oList.length; i++) {
oList[i].index = i;
oList[i].onclick = function () {
console.log(this.index);
}
}
- 古老的解决方案二.使用匿名函数的方式隔离外部的i => 匿名函数:这就是我的领域,魔法-画地为牢!!!
for (var i = 0; i < oList.length; i++) {
(function (i) {
oList[i].onclick = () => {
console.log(i);
}
})(i);
}
- 然而我是现代人,能坐车我干嘛要走路???So !
for (let i = 0; i < oList.length; i++) {
oList[i].onclick = () => {
console.log(i);
}
}
正所谓四两拨千斤!什么?你说没差别? var -> let
归根结底还是作用域的问题,let 是自带领域的。
六.相关知识点- Math.floor()、Math.ceil()、Math.round
我们在滑动轮播中为了达到距离越远滑动越快的效果。
我们的解决方案:
令每一次的 interval 时间一致。
改变每一次的移动距离来实现。
每次的移动距离由 当前offsetTop 和 目的地 决定。
每次的移动距离都有最小值。
同时怕计算得出的值相加会超出原先设定的 目的地。
所以使用到了上面的两个函数。(目的地和当前的offsetTop的差可正可负).
- Math.floor 地板:返回的永远小于等于原来的(下限)。
const a = 0.1; // 0 0 < 0.1
const b = -a; // -1 -1 < -0.1
const c = 0.6; // 0
const d = -c; // -1
- Math.ceil 天花板:返回的永远大于等于原来的(上限)。
const a = 0.1; // 1
const b = -a; // 0
const c = 0.6; // 1
const d = -c; // 0
- Math.round 返回最近的。
const a = 0.1; // 0 四舍
const b = -a; // -0 依旧四舍
const c = 0.6; // 1 五入
const d = -c; // -1 五入
当我们的移动距离为 -0.1 的时候,用 Math.ceil 意味着它的 top 永远为0
了QAQ.
当我们的移动距离为 0.1 的时候,用 Math.floor 也以为这 移动top 为0
.
原生JS轮播-各种效果的极简实现的更多相关文章
- 原生JS轮播-各种效果的极简实现(二)面向对象版本的实现和优化
之前写了一篇原生JS轮播,不过是非面向对象的,并且也没有添加上自动轮播.这里来写一下如何优化和进阶. 这里简单地介绍一下之前的代码,这是html结构 <body> <div clas ...
- js原生实现轮播图效果(面向对象编程)
面向对象编程js原生实现轮播图效果 1.先看效果图 2.需要实现的功能: 自动轮播 点击左右箭头按钮无缝轮播 点击数字按钮切换图片 分析:如何实现无缝轮播? 在一个固定大小的相框里有一个ul标签,其长 ...
- js___原生js轮播
原生js轮播 作为一名前端工程师,手写轮播图应该是最基本掌握的技能,以下是我自己原生js写的轮播,欢迎指点批评: 首先css代码 a{text-decoration:none;color:#3DBBF ...
- javascript原生js轮播图
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 原生js轮播图
//用原生js实现了一个简单的轮播图效果 <!DOCTYPE html><html> <head> <meta charset="UTF-8&quo ...
- 原生js轮播以及setTimeout和setInterval的理解
下面这个代码是从一个群下载下来的,为了帮助自己理解和学习现在贴出来,与初学者共勉. <!DOCTYPE html> <html> <head> <meta c ...
- 手把手原生js轮播图
在团队带人,突然被人问到轮播图如何实现,进入前端领域有一年多了,但很久没自己写过,一直是用大牛写的插件,今天就写个简单的适合入门者学习的小教程.当然,轮播图的实现原理与设计模式有很多种,我这里讲的是用 ...
- 用require.js封装原生js轮播图
index.html页面: <!DOCTYPE html><html> <head> <meta charset="UTF-8"> ...
- 原生js轮播图实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
随机推荐
- Arc082_F Sandglass
Description有一个沙漏由两个上下相通玻璃球$A$和$B$构成,这两个玻璃球都含有一定量的沙子,我们暂且假定$A,B$中位于上方的玻璃球的为$U$,下方的玻璃球为$L$,则除非$U$中没有沙子 ...
- Deferred Shading延迟渲染
Deferred Shading 传统的渲染过程通常为:1)绘制Mesh:2)指定材质:3)处理光照效果:4)输出.传统的过程Mesh越多,光照处理越费时,多光源时就更慢了. 延迟渲染的步骤:1)Pa ...
- [转]PNG8和PNG24的区别
首先我们要知道: 1.png8和png24的根本区别,不是颜色位的区别,而是存储方式不同. 2.png8有1位的布尔透明通道(要么完全透明,要么完全不透明),png24则有8位(256阶)的布尔透明通 ...
- JavaScript运行机制与setTimeout
前段时间,老板交给了我一个任务:通过setTimeout来延后网站某些复杂资源的请求.正好借此机会,将JavaScript运行机制和setTimeout重新认真思考一遍,并将我对它们的理解整理如下. ...
- ECMAScript基本函数、概念区分总结
1.使用Number()和parseInt() parseFloat()转换区别. 详见<JavaScript高级程序设计>P30 Number()可以针对任何类型. parseInt() ...
- strcmp strcpy的使用 char类型
//判断从控制台输入的密码是否正确 BOOL varifyPassWord(char passWord[] , int index) { BOOL result = YES; int count = ...
- from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import Select Select(d.find_element_by_id(u'key_开户行')).first_sele ...
- PTA实验作业-01
一.PTA实验作业 本周要求挑3道题目写设计思路.调试过程.设计思路用伪代码描述.题目选做要求: 顺序表选择一题(6-2,6-3,7-1选一题),代码必须用顺序结构抽象数据类型封装 单链表选择一题(6 ...
- C++STL库中vector容器常用应用
#include<iostream> #include<vector> #include<algorithm> using namespace std; int m ...
- puppet前端管理工具foreman-proxy bind 127.0.0.1:8443问题解决
最近在玩foreman,发现部署foreman-proxy的时候,总是出现8443bind在127.0.0.1端口,导致无法访问的情况. 如下图: 经过strace -o log.txt bin/sm ...