1.gif
不知道这种效果是不是你们想要的效果, 这是超人鸭开发公司官网遇到的一个需求, 结合上面的动图梳理一下需求:
一进页面动画会自动执行, 且是循环执行.
分为三项, 中间以类似进度条的元素连接, 主要动画也是这个进度条.
上面第几个图标激活时, 下面显示对应的内容.
点击上面的图标时, 下面内容做对应切换, 同时停止动画不再执行, 进度条也不显示.
这就是大概的需求, 那如何实现呢, 我进行了简单的分析:
上面的进度条动画用到 jQuery 中的 animate 方法, 一开始将进度条的宽度设置为零, 在一定的时间内将宽度变成 100%, 激活对应的图标, 同时在动画开始时要将其他的进度条宽度设置为零. 下面内容的切换根据上面的激活的图标是第几个进行显示, 问题的关键就是如何知道激活的图标是第几个, 也就是 index, 在激活的图标改变时下面的内容也要跟着改变, 所以就要监听这个 index 的变化, 在它变化时做出相应的操作, 那在 JS 中监听一个变量的变化我用到的是 Object.defineProperty(), 用它来改写对象属性的 get 和 set, 不了解这个 API 的朋友可以翻阅一下, 不难理解.
下面我就一步一步现实这个自动切换的动画效果, 首先是上面控制的图标与进度条 html 和 CSS, 这个 dom 结构看清楚可以提升文章观看体验:
HTML:
- <div class="control-box">
- <div class="process-box">
- <div class="process-item active"></div>
- <!-- process-line 就代表进度条, 里面有两个子元素, 代表初始的线和进度条 -->
- <div class="process-line">
- <div class="active-line"></div>
- <div class="dot-line"></div>
- </div>
- <div class="process-item"></div>
- <div class="process-line">
- <div class="active-line"></div>
- <div class="dot-line"></div>
- </div>
- <div class="process-item"></div>
- </div>
- </div>
CSS:
- <style>
- .control-box{
- margin: 0 auto;
- width: 50%;
- display: flex;
- align-items: center;
- }
- .process-box{
- flex: 1;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .process-item{
- width: 30px;
- height: 30px;
- border-radius: 50%;
- border: 1px solid gray;
- }
- .process-item.active{
- background: blue;
- }
- .process-line{
- width: 100px;
- position: relative;
- }
- .dot-line{
- position: absolute;
- width: 100%;
- height: 1px;
- background: red;
- }
- .active-line{
- position: absolute;
- background: blue;
- width: 0px;
- height: 3px;
- z-index: 2;
- top: -1px;
- }
- </style>
效果:
image.PNG
接下来是实现动画, 上面说到, 要去监听一个变量的变化, 代表上面图标的 index, 当它改变时去做相应的操作, 所以先来监听一个变量:
- var indexObj = {}
- var temp = null
- Object.defineProperty(indexObj, 'index', {
- get: function() {
- return temp
- },
- set: function(value) {
- temp = value
- // 执行某些操作
- setActive(temp)
- }
- });
其中 temp 是一个中间变量, 因为我们要取这个 index, 也就是执行 get 方法, 如果在 get 里面直接 return 这个属性, 那 return 的时候相当会再执行一次 get, 就会陷入死循环, 最后报错:
- var indexObj = {}
- var temp = null
- Object.defineProperty(indexObj, 'index', {
- get: function() {
- return this.index
- },
- set: function(value) {
- temp = value
- // 执行某些操作
- setActive(temp)
- }
- });
- indexObj.index = 0
- console.log(indexObj.index)
image.PNG
所以使用要使用 get 的时候需要一个临时变量, return 这个临时变量 temp. 到这里我们就能监听一个变量的变化了, 它代表着当前激活的是第几个图标, 也就是 index, 当它改变时我们去执行 setActive 这个方法, 并把 index 传进去:
- function setActive(val) {
- // $(".process-item")就是代表上面的图标(圆点)
- $(".process-item").removeClass('active')
- // eq 里面写变量需要像这样写, 不然会被识别成字符串
- $(".process-item:eq("+(val)+")").addClass('active')
- }
接下来就是进度条的动画了, 当进度条动画执行完后, index+1, 图标的激活样式就会自动改变, 因为我们已经监听了它的变化, 这里用到 jQuery 的 animate 方法, 而且用到了它的回调函数, 用法是这样:
$(selector).animate(styles,speed,easing,callback)
第三个参数我忽略掉了, 看看例子:
$('div').animate({width:'100%'},1000,function(){alert()})
表示选中 div 元素在一秒内宽度变成 100%, 完成后 alert()
初步实现进度条动画:
- function setLineWidth(){
- // 动画开始前将其他进度条清空
- $(".active-line").not(".active-line:eq("+(indexObj.index)+")").CSS("width",'0px')
- $(".active-line:eq("+(indexObj.index)+")").animate({width:'100%'},1000,function() {
- indexObj.index += 1
- setLineWidth() // 再次执行
- })
- }
- setLineWidth()
这样是能执行一遍动画, 但是以我的图片例子, 进度条只有两个, 所以操作进度条的 index 最大只能是 1, 而上面的图标 (圆点) 包括内容有三个, 所以操作他们的 index 最大只能是 2, 配合动画循环的需求, 我改写一下监听 index 的方法:
- Object.defineProperty(indexObj, 'index', {
- get: function() {
- return temp
- },
- set: function(value) {
- if(value===3){
- temp = 0
- }
- temp = value
- // 执行某些操作
- setActive(temp)
- }
- });
加个判断让 index 可以在 0 到 2 之间循环, 但是操作进度条的 index 最大只能是 1, 当 index=2 的时候, 应该不操作进度条, 改写一下上面的 setLineWidth 方法:
- function setLineWidth(){
- // 动画开始前将其他进度条清空
- $(".active-line").not(".active-line:eq("+(indexObj.index)+")").CSS("width",'0px')
- if(indexObj.index <2) {
- $(".active-line:eq("+(indexObj.index)+")").animate({width:'100%'},1000,function() {
- indexObj.index += 1
- setLineWidth() // 再次执行
- })
- } else { // index 等于 2 的情况
- setTimeout(function() {
- indexObj.index += 1 // 当 index=3 的时候会自动变成 0
- setLineWidth()
- },1000) // 保持一样的时间
- }
- }
- setLineWidth()
效果:
1.gif
接下来实现点击上面的图标时, 内容对应切换, 并停止动画:
首先是对应的激活样式切换, 这个直接在点击事件里面改变 index 就可以, 然后要停止动画, 并且清空进度条, 我们先定义一个全局变量, 然后在这个点击事件里面改变, 并改写上面的 setLineWidth 方法:
- var isClick = false // 表示是否点击
- $(".process-item").click(function() {
- $(".active-line").CSS("display", "none") // 隐藏进度条
- isClick = true // 表示点击
- indexObj.index = $(".process-item").index(this) // 改变 index
- })
- function setLineWidth() {
- if(isClick) {
- return
- }
- $(".active-line").not(".active-line:eq("+(indexObj.index)+")").CSS("width",'0px')
- if(indexObj.index < 2) {
- $(".active-line:eq("+(indexObj.index)+")").animate({width:'100%'},1000,function() {
- if(isClick) {
- return
- }
- indexObj.index += 1
- setLineWidth()
- })
- } else {
- setTimeout(function() {
- if(isClick) {
- return
- }
- indexObj.index += 1
- setLineWidth()
- }, 1000)
- }
- }
之所以在里面动画执行完也加了判断是因为动画的执行时间为一秒, 那当你点击的时候它可能在执行的过程中, 那 index 还是会改变, 这样是不行的, 所以每一个步骤都要加上判断, 看看现在的效果:
1.gif
到这里, 上面的图标控制和进度条动画已经实现了, 下面的内容跟着切换其实也是一样, 用的还是写好的 index 逻辑, 切换的动画直接用 CSS 的过渡就可以实现, 下面我简单演示一下, 加一下 HTML 与 CSS, 都是在上面的代码后面直接加上:
HTML:
<div class="content"> <div class="content-item active">1</div> <div class="content-item">2</div> <div class="content-item">3</div> </div>
CSS:
.content-item{ width: 100%; height: 200px; position: absolute; right: -400px; font-size: 30px; opacity: 0; transition: all 0.8s; } .content-item:nth-of-type(1){ background: yellow; } .content-item:nth-of-type(2){ background: blue; } .content-item:nth-of-type(3){ background: red; } .content-item.active{ opacity: 1; right: 0; }
所以只需要根据 index 切换 active 这个 class 就可以了, 在 setActive 方法里面添加切换的代码:
function setActive(val) { // $(".process-item")就是代表上面的图标(圆点) $(".process-item").removeClass('active') // eq 里面写变量需要像这样写, 不然会被识别成字符串 $(".process-item:eq("+(val)+")").addClass('active') $(".content-item").removeClass('active') $(".content-item:eq("+(val)+")").addClass('active') }
效果:
1.gif
到这里完整的效果已经实现了, 下面是完整的代码:
<!DOCTYPE HTML> <HTML lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> Document </title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"> </script> <style> .control-box{ margin: 0 auto; width: 50%; display: flex; align-items: center; } .process-box{ flex: 1; display: flex; justify-content: space-between; align-items: center; } .process-item{ width: 30px; height: 30px; border-radius: 50%; border: 1px solid gray; } .process-item.active{ background: blue; } .process-line{ width: 100px; position: relative; } .dot-line{ position: absolute; width: 100%; height: 1px; background: red; } .active-line{ position: absolute; background: blue; width: 0px; height: 3px; z-index: 2; top: -1px; } .content{ position: relative; height: 200px; margin: 0 auto; width: 50%; margin-top: 20px; overflow: hidden; } .content-item{ width: 100%; height: 200px; position: absolute; right: -400px; font-size: 30px; opacity: 0; transition: all 0.8s; } .content-item:nth-of-type(1){ background: yellow; } .content-item:nth-of-type(2){ background: blue; } .content-item:nth-of-type(3){ background: red; } .content-item.active{ opacity: 1; right: 0; } </style> </head> <body> <div class="control-box"> <div class="process-box"> <div class="process-item active"> </div> <div class="process-line"> <div class="active-line"> </div> <div class="dot-line"> </div> </div> <div class="process-item"> </div> <div class="process-line"> <div class="active-line"> </div> <div class="dot-line"> </div> </div> <div class="process-item"> </div> </div> </div> <div class="content"> <div class="content-item active"> 1 </div> <div class="content-item"> 2 </div> <div class="content-item"> 3 </div> </div> <script> $(document).ready(function() { var indexObj = {} var temp = null var isClick = false Object.defineProperty(indexObj, 'index', { get: function() { return temp }, set: function(value) { if (value === 3) { temp = 0 } else if (value === -1) { temp = 2 } else { temp = value } setActive(temp) } }); indexObj.index = 0 setLineWidth() function setLineWidth() { if (isClick) { return } $(".active-line").not(".active-line:eq(" + (indexObj.index) + ")").CSS("width", '0px') if (indexObj.index < 2) { $(".active-line:eq(" + (indexObj.index) + ")").animate({ width: '100%' }, 1000, function() { if (isClick) { return } indexObj.index += 1 setLineWidth() }) } else { setTimeout(function() { if (isClick) { return } indexObj.index += 1 setLineWidth() }, 1000) } } $(".process-item").click(function() { $(".active-line").CSS("display", "none") isClick = true indexObj.index = $(".process-item").index(this) }) function setActive(val) { $(".process-item").removeClass('active') $(".process-item:eq(" + (val) + ")").addClass('active') $(".content-item").removeClass('active') $(".content-item:eq(" + (val) + ")").addClass('active') } }) </script> </body> </HTML>
虽然现在 jQuery 用得越来越少(我也不愿意写), 但是不得不说在做公司官网这种以展示为主的项目, 用 jQuery 还是首选, 所以熟悉一下也不吃亏是吧, 嘻嘻.
作者微信: Aqing1906
欢迎指教哦
来源: http://www.jianshu.com/p/ce20222a502f