Javascript学习记录——原生JS实现旋转木马特效

  

  昨天学习到了JS特效部分,然后老师讲了旋转木马特效的实现,如上图。不过只是讲了通过点击箭头实现图片的切换,对于点击图片本身以及二者联动却是没有讲解。

  本着一颗追求完美的心,今天花费了一个中午终于将整个功能全部完善(死了太多脑细胞~~)。

  接下来直接进入主题哈~(主要讲解JS,所以对其中的HTML及CSS不做详细说明。)

  首先是HTML代码

  

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>旋转木马轮播图</title>
    <link rel="stylesheet" href="css/css.css"/>
</head>
<body>
<div class="wrap" id="wrap">
    <div class="slide" id="slide">
        <ul>
            <li><a href="#"><img src="images/slidepic1.jpg" /></a></li>
            <li><a href="#"><img src="images/slidepic2.jpg" /></a></li>
            <li><a href="#"><img src="images/slidepic3.jpg" /></a></li>
            <li><a href="#"><img src="images/slidepic4.jpg" /></a></li>
            <li><a href="#"><img src="images/slidepic5.jpg" /></a></li>
        </ul>
        <div class="arrow" id="arrow">
            <a href="javascript:;" class="prev" id="arrLeft"></a>
            <a href="javascript:;" class="next" id="arrRight"></a>
        </div>
    </div>
</div>
</body>
</html>

    以下为CSS代码:

blockquote, body, button, dd, dl, dt, fieldset, form, h1, h2, h3, h4, h5, h6, hr, input, legend, li, ol, p, pre, td, textarea, th, ul {
    margin: 0;
    padding: 0
}

body, button, input, select, textarea {
    font: 12px/1.5 "Microsoft YaHei", "微软雅黑", SimSun, "宋体", sans-serif;
    color: #666;
}

ol, ul {
    list-style: none
}

a {
    text-decoration: none
}

fieldset, img {
    border: 0;
    vertical-align: top;
}

a, input, button, select, textarea {
    outline: none;
}

/*以上为简单的初始化*/

a, button {
    cursor: pointer;
}

.wrap {
    width: 1200px;
    margin: 100px auto;
}

.slide {
    height: 500px;
    position: relative;
}

.slide li {
    position: absolute;
    left: 200px;
    top: 0;
}

.slide li img {
    width: 100%;
}

.arrow {
    opacity: 0;
    position: absolute;
    top: 200px;
    z-index: 1000;
}
.arrow .next{
    left: 1120px;
}
.prev, .next {
    width: 76px;
    height: 112px;
    position: absolute;
    top: 50%;
    margin-top: -56px;
    background: url(../images/prev.png) no-repeat;
    z-index: 99;
}

.next {
    right: 0;
    background-image: url(../images/next.png);

  以下为JS代码:先说明一下整体的思路

  1、设置鼠标移入移出事件,显示图片中的左右两个箭头

  2、设置左右两个箭头的点击事件,通过点击箭头,实现配置单中的样式切换(因为获取图片所在的li是一个伪数组,无法使用数组的方法,所以图片的轮播实现主要是通过对配置单内元素索引的改变完成的。)

  3、设置点击li(也就是图片,后面统一写为li)标签实现点击的li出现在界面最前端。

  4、实现点击箭头后,点击li的联动,也就是无论点击箭头多少次,再次点击li后,也能将当前点击的li显示在界面最前端。

  5、实现点击li后,点击箭头的联动,也就是无论点击li后当前界面最前端的li是哪一个,再次点击箭头后,都能按照点击箭头的规则切换到当前li的下一个或上一个li。

  是不是有点啰嗦啊 0...0

  

<script>  (function(){//今天偶尔看到一篇问文章,其中说道用自调用函数封装自己的JS代码可以防止环境污染哈~好像还有其他很好的功能,还没有详细了解,这里就拿出来爽一下~
    var config = [
        {
            width: 400,
            top: 20,
            left: 50,
            opacity: 0.2,
            zIndex: 2
        },//0
        {
            width: 600,
            top: 70,
            left: 0,
            opacity: 0.8,
            zIndex: 3
        },//1
        {
            width: 800,
            top: 100,
            left: 200,
            opacity: 1,
            zIndex: 4
        },//2
        {
            width: 600,
            top: 70,
            left: 600,
            opacity: 0.8,
            zIndex: 3
        },//3
        {
            width: 400,
            top: 20,
            left: 750,
            opacity: 0.2,
            zIndex: 2
        }//4
    ];//config为一个配置单 规定了每张图片的大小位置层级透明度
    //获取元素
    var wrap = document.getElementById(‘wrap‘)
    var slide = document.getElementById(‘slide‘)
    var ul = slide.children[0]
    var lis = ul.children
    var arrow = document.getElementById(‘arrow‘)
    var arrLeft = document.getElementById(‘arrLeft‘)
    var arrRight = document.getElementById(‘arrRight‘)
    //4.3 因为要控制图片显示的频率,因此要等到每次图片正常切换完成才进行下一次图片切换,假设初始时图片切换完成 flag == true
    var flag = true, count = 2, cFlag = false //7.92 这里设置cFlag初始值为false

    /**
     * 缓动改变样式的函数,先封装这个函数,后面进行配置单内的样式添加只需要调用这个函数就可以了。
     * @param tag 要改变的元素
     * @param json 一个对象,里面以属性保存要修改的样式
     * @param fn 传入函数,可以在第一次调用执行后,再执行传入的函数体内容
     */
    function perfectAnimate(tag, json, fn) {
        clearInterval(tag.timer)//首先清除定时器
        tag.timer = setInterval(function () {//新建一个setInterval,设置周期时间为18ms,这个数字不固定~
            var flag = true //这里运用了假设成立法
            for (var k in json) { //遍历传入的对象
                if (k == ‘opacity‘) { //判断对象中保存的样式,如果为opacity,单独设置
                    var leader = getStyle(tag, k) * 100 //这里获取opacity的初始值。这里使用的是下面获取样式的兼容函数。                                 //乘100是因为JS小数计算存在精度问题。所以这里在下将其扩大100倍进行使用。
                    var target = json[k] * 100  //这里取出opacity的目标值,也就是对象中的样式属性值
                    var step = (target - leader) / 10; //这里用目标值减去初始值除以10达到缓动的目的(变速改变)
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);//这里也可以不使用这个判断,对step向上或向下保证最后的时候每次都能至少走1
                    leader = leader + step; //初始值加上每次增加或减少的值
                    tag.style[k] = leader / 100; //将值设置给opacity属性,当然别忘了把100除回来~
                } else if (k == ‘zIndex‘) {
                    tag.style[k] = json[k] //如果为zIndex 这时候就直接设置给样式就行了。
                } else {
                    var leader = parseInt(getStyle(tag, k)) || 0 //当样式属性值为带单位的时候,这时候要进行一个取整去掉单位,同时为了防止没有初始值而默认为auto,使用一个短路操作
                    var target = json[k]
                    var step = (target - leader) / 10
                    step = step > 0 ? Math.ceil(step) : Math.floor(step)
                    leader = leader + step
                    tag.style[k] = leader + ‘px‘
                }
                if (target != leader) {
                    flag = false //这里只要有一个没有样式没有设置完成,假设便不成立,便不执行下面的清除
                }
            }
            if (flag) { //假设所有属性都设置完成,便清除定时器,节约空间
                clearInterval(tag.timer)
                // if ("function" == typeof fn) {
                //     fn()
                // }
                fn && fn();//清除定时器后,可能会有需要的功能要在以上执行完成后执行,这里使用一个回调函数,                           //同样使用短路操作,防止没有传函数而报错。上面注释的与这个功能一样,只是更严谨,这个更简洁
            }
        }, 18)
    }

    /**
     * 获取多个样式的方法的兼容性。current是IE自己的,不支持getComputedStyle
     * @param tag 是标签名
     * @param attr 是具体的某个样式的属性
     * @returns {*} 返回的是这个样式的属性值
     */
    function getStyle(tag, attr) {
        if (tag.currentStyle) {
            return tag.currentStyle[attr];
        } else {
            return getComputedStyle(tag, null)[attr];
        }
    }

  //以上准备工作做完,然后可以开始我们代码代码的书写了。  //哎呀,好困呀~~还好后面的内容在我中午写的时候已经做好注释了,省了不少时间哇,哈哈哈哈....  //建议以序号顺序进行阅读哈,虽然自我感觉挺详细的,但是可能存在某些表述不准确的地方,望谅解~毕竟这才是我的第二篇博客啦...

/**
     * 1 封装函数,将config中的样式一一对应设置给li标签,因为后面会多次调用它
     */
    function change() {
        for (var i = 0; i < config.length; i++) {
            perfectAnimate(lis[i], config[i], function () {
                flag = true//4.6 这里在perfectAnimate函数中传入函数,赋值flag为true,因为这时候图片切换已经完成,可以进行下一次切换了。
            })
        }
    }
  //2 最开始的默认分布(也就是上图那个效果~),直接调用函数
change() //3 设置移入移出事件,显示与隐藏箭头

wrap.onmouseover = function () {
        perfectAnimate(arrow, {‘opacity‘: 1})
    }
    wrap.onmouseout = function () {
        perfectAnimate(arrow, {‘opacity‘: 0})
    }

    /**
     *
     */
    function clearChangeConfig2() {
        if (cFlag) { //7.93 因为最开始点击箭头时,没有进行li的点击切换,不需要进行联动,只有点击过li后,再点击箭头才执行,可以
                     // 使用cFlag作为判断条件
            cFlag = false //7.91 这里被调用是需要执行changeConfig1(this.index)里面的if判断,所以首先设置cFlag为false
            for (var i = 0; i < lis.length; i++) {
                //7.4 因为点击li后图片进行切换后,config中的元素又回到初始位置,与设置给li的并不相同,这时就需要
                // 将点击li时最后执行的changeConfig2(this.index),再退回到changeConfig1(this.index),要做到上一
                // 步,就需要找到当前点击的li的索引,因为当前点击的li肯定是设置有属性zIndex=4,并且其index属性保存了
                // 其索引值,所以通过这就可以找到需要的索引了。
                if (lis[i].style.zIndex == 4) {
                    changeConfig1(lis[i].index)//7.5 找到索引后直接将config中元素的位置退回到changeConfig1(this.index)
                }
            }
        }
    }

    function pushCount() {
        if (count <= config.length && count > 0) {
            count--
        } else {
            count = 4
        }
    }
    function unshiftCount() {
        if (count < config.length && count >= 0) {
            count++
        } else {
            count = 0
        }
    }

    //4 添加右箭头点击事件
    arrRight.onclick = function () {
        if (flag) {//4.4 如果flag为true,即4.3假设成立,便执行下面的代码
            flag = false//4.5 给flag设置为false,使假设不成立,这时候只要没有改变flag值,点击事件源将不在执行这里的代码,不过此次下面代码依旧会执行
            clearChangeConfig2()//7.3 这里调用是为了点击li后,实现箭头的联动,具体看7.4
            config.push(config.shift())//4.1 点击后将第一个样式剪切到最后一个
            change()//4.2 通过调用函数,将新生成的config元素一一对应设置给li标签

  //7.0 点击箭头后,实现与li点击联动,每次点击右箭头count值都会自减一次,count初始值为2.小于0后直接赋值为4
 //count的值对应的是每次进行切换后,zIndex值为4的元素在config中的索引值,并以此为初始值(取代6.5设置的2)——界面最前端的配置属性,使得点击li时正常切换
 pushCount()  } } //5 添加左箭头点击事件
    arrLeft.onclick = function () {
        if (flag) {
            flag = false
            clearChangeConfig2()
            config.unshift(config.pop())//5.1 点击后将最后一个样式剪切到第一个
            change() //5.2 通过调用函数,将新生成的config元素一一对应设置给li标签
            unshiftCount()//7.1 将箭头点击与li点击联动,每次点击左箭头count值都会自增一次,count初始值为2.大于4后直接赋值为0
        }

    }
    /**
     * 6.3 根据当前点击的li的索引值,给li设置config内元素中的属性
     * @param i 当前点击li的索引值
     */
    function changeConfig1(i) {
        //6.5 未设置联动时,count位置应该是数字2,即最开始时zIndex值为4的config元素对应的索引,改变的中心思想为:zIndex中值为4的config元素(界面最前端的配置属性)
        //    总是设置给当前的点击的li,保持二者索引一致(count先不管)
        //6.6 然后进行判断,如果当前li的索引值小于等于2
        //7.2 这时将count作为条件依据,等同于以zIndex值为4的元素的索引为依据
        if (i <= count) {
            for (var i = i; i < count; i++) {
                config.push(config.shift())//6.7 那么根据其索引值进行0位config元素的剪切,直到zIndex值为4的config元素对应的索引与当前点击的li的索引一致
                if (cFlag == false) {//7.8 又因为点击li时,config元素与li进行对应设置后,又会回到初始位置,不会影响到count的变化
                                     //所以点击li时调用这个函数不能执行这个if判断,这里再次使用假设成立法,假设cFlag为false时执行if判断成立
                    pushCount()//7.6 因为将config中元素的位置改为正常的后,zIndex=4的元素的索引又会发生变化,所以这时count也要跟着变化
                    i--//7.7 这里的count为--,所以i值也要--才能保证count自减的次数与循环执行次数一致(即与config元素剪切的次数一致,即保证count的值总是配置单中zIndex=4的元素的索引)
                }
            }
        } else {
            for (var i = i; i > count; i--) {
                config.unshift(config.pop())//6.8 如果大于2,那么根据其索引值进行索引为4的config元素的剪切,直到zIndex值为4的config元素对应的索引与当前点击的li的索引一致
                if (cFlag == false) {
                    unshiftCount()
                    i++
                }
            }
        }

    }

    function changeConfig2(i) {
        if (i <= count) {
            for (var i = i; i < count; i++) {
                config.unshift(config.pop())
                if (cFlag == false) {
                    pushCount()
                }
            }
        } else {
            for (var i = i; i > count; i--) {
                config.push(config.shift())
                if (cFlag == false) {
                    unshiftCount()
                }
            }
        }
    }

    //6 遍历lis,给li设置点击事件
    for (var i = 0; i < lis.length; i++) {
        lis[i].index = i//6.4 以lis[i]的自定义属性index保存其索引值
        lis[i].onclick = function () {//6.1 添加事件
            cFlag = true //7.9 点击li时执行changeConfig1(this.index)之前将cFlag赋值true,即不执行它里面的if判断
            changeConfig1(this.index)//6.2 点击li后将zIndex值为4的config元素添加给当前点击的li,其他元素依次挪动。
            change()//6.9通过调用函数,将修改后的config内的元素属性一一对应设置给li
            changeConfig2(this.index)//6.91 在第6.2步中,zIndex值为4config元素进行过剪切可能已经不在索引为2的位置了,这时后我们再进行与6.2步中相反的操作
                                     //     将config中的元素位置回归到初始状态,但是并不设置给li.
        }
    }  }())
</script>

  最后本着负责的态度,还是认真对着上面的文字检查了20分钟,确保在自己水平内将问题讲清楚——可是、好像还是不够太具体化...

   看来只能等以后的理解更加透彻后,再对本文进行修改了。

          ——BY:骑士与魔王(我微博也是这个名字哈,里面还有自己之前玩微博时写的图文小说,《骑士与魔王》,可惜太监啦,哈哈哈哈)

时间: 2024-10-27 00:02:17

Javascript学习记录——原生JS实现旋转木马特效的相关文章

JavaScript学习记录day1

JavaScript学习记录day1 学习 javascript JavaScript学习记录day1 1. 快速入门 2. 编程工具 3. 语法 4. 注释 5. 大小写 JavaScritps 是什么? JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能. 在Web世界里,只

JavaScript学习记录day2

JavaScript学习记录day2 学习 javascript JavaScript学习记录day2 1.1 数据类型 1.2 变量 1.1.1 Number 1.1.2 字符串 1.1.3 布尔值 1.1.4 比较运算符 1.1.5 null和undefined 1.1.6 数组 1. 数据类型和变量 1. 数据类型和变量 1.1 数据类型 1.1.1 Number JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型: 123; // 整数1230

javascript 学习记录

关于牛B的Jquery源头 (function(){ //这里省略jQuery所有实现 })(); 1:无论你怎么去定义你的函数 JS解释器都会把它翻译成一个 Function对象 2:那什么是Function? Function对象是javascript里面 固有对象,所有的函数实际上都是一个 Function对象 3: alert(typeof function(){});// "function" alert(typeof function(x,y){return x+y;});

JavaScript学习记录day7-高阶函数

JavaScript学习记录day7-高阶函数 [TOC] 1. 什么是高阶函数 高阶函数英文叫Higher-order function.那么什么是高阶函数? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数. 一个最简单的高阶函数: function add(x, y, f) { return f(x) + f(y); } 当我们调用add(-5, 6, Math.abs)时,参数x,

JavaScript学习记录day9-标准对象

JavaScript学习记录day9-标准对象 [TOC] 在JavaScript的世界里,一切都是对象. 但是某些对象还是和其他对象不太一样.为了区分对象的类型,我们用typeof操作符获取对象的类型,它总是返回一个字符串: typeof 123; // 'number' typeof NaN; // 'number' typeof 'str'; // 'string' typeof true; // 'boolean' typeof undefined; // 'undefined' typ

JavaScript学习记录day5-函数的定义和调用

JavaScript学习记录day5-函数的定义和调用 [TOC] 1. 定义函数 在JavaScript中,定义函数的方式如下: function abs(x) { if (x >= 0) { return x; } else { return -x; } } 上述abs()函数的定义如下: function指出这是一个函数定义:abs是函数的名称:(x)括号内列出函数的参数,多个参数以,分隔:{ ... }之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句.请注意,函数体内部的语句在

Javascript学习记录——一次小项目的总结

这几天都忙着上课还有准备项目,没有时间写博客,今天总是完成了项目的评比,有点失落,有点感悟. 背景:博主是传智博客的培训生,学完了html,css,以及js特效,班级共84人,项目分为8组,博主有幸担任第8组组长. 项目概况:每个组各自以一个网站为模板进行模仿,页面为非静态页面,时间为1周,包括上课时间.(博主用的原生js) 项目完成过程: 1.接到任务当天,博主便组织小组成员在晚自习时间进行一次简单的讨论,然后发布任务,每人选择两个自己中意的网站,并说明理由,第二天将要写的网站定下来. 2.次

诶西,JavaScript学习记录。。。。。。

由于大学课程缘故,老师巨爱叫人问问题,还记分呢,随便记录一下Js的学习情况,以后复习什么的也比较方便吧...... 开始咯,就按照C语言学习那样的方法来吧! ==================================割割割================================== 1.数据类型(这里只是大概提一下) 1 /* 2 我认为Js里没有明显的数据类型,仅有 字符串.数字.布尔.数组.对象.Null.Undefined 3 */ 4 5 var temp = 'leg

javascript学习一、js的初步了解

1.javascript的简介: *javascript 是一种基于对象和事件驱动的语言,主要应用于客户端. -- 基于对象:  ** 提供了很多对象,可以直接使用. --事件驱动: ** html做的网页是静态效果,javascript做的是动态效果. --客户端: 主要是指浏览器. *js的特点: (1)交互性: --信息的动态交互. (2)安全性: --s不能访问本地磁盘中的文件. (3)跨平台性: --与java不同,js直接使用 浏览器跨平台. *javascript和java之间的区