JS-part12.4-ES6-运动函数

引入运动函数

+ 就是把运动的这一段过程抽离出来, 封装成一个函数

需求1: 实现点击 div 移动到 200px 的位置
  => 需要运动
  => 需要一个 setInterval(), 每隔一段时间移动一段距离, 到达目标位置后关闭定时器

需求2: 实现点击 p 移动到 300px 的位置

在这里插入图片描述

// css
*{
    margin: 0;
     padding: 0;
 }
 div{
     width: 100px;
     height: 100px;
     background-color: #ccc;
     position: absolute;
     left: 0;
     top: 0;
 }
 p{
     width: 100px;
     height: 100px;
     background-color: pink;
     position: absolute;
     left: 0;
     top: 110px;
 }
// html
<div></div>
<p></p> 

// js
var div = document.querySelector('div')
div.addEventListener('click', function(){

    let distance = 0
    let timer = setInterval(function(){
        distance += 10
        div.style.left = distance + 'px'
        if(distance >= 200){
            clearInterval(timer)
        }

    }, 30)
})

var p = document.querySelector('p')
p.addEventListener('click', function(){

    let distance = 0
    let timer = setInterval(function(){
        distance += 10
        p.style.left = distance + 'px'
        if(distance >= 300){
            clearInterval(timer)
        }

    }, 30)
})

运动函数第一版

需要一个函数, 接收三个参数
   => 元素
   => 属性
   => 目标位置
   
发现问题
     + 只能运动一个属性

在这里插入图片描述

// 准备运动函数
function move(ele, type, target){
    let distance = 0
    let timer = setInterval(function(){
        distance += 10
        ele.style[type] = distance + 'px'
        if(distance >= target){
            clearInterval(timer)
        }

    }, 30)
}

// 将来用的时候
// move(div, 'left', 200)


var div = document.querySelector('div')
div.addEventListener('click', function(){
    move(div, 'left', 200)
})

运动函数第二版

发现问题
  + 只能运动一个属性

解决问题
  + 以对象的形式把你要运动的属性传递进去
    => { left: 200, top: 300 }
  + 我的函数其实只接收 两个 参数就够了
    => 元素
    => 目标位置

发现问题
  + 当多个属性的目标值不一样的时候, 不能走出对角线
  + 当不从 0 开始的时候, 运动开始时会先回到 0 位置一次
var div = document.querySelector('div')
div.addEventListener('click', function(){
    move(div, { left: 200, top: 300 })
})

// 准备运动函数
function move(ele, target){
    // target 有多少属性, 开启多少定时器
    for(let key in target){
        let distance = 0
        let timer = setInterval(() => {
            distance += 10
            ele.style[key] = distance + 'px'
            if(distance >= target[key]){
                clearInterval(timer)
            }
        }, 30)
    }
}

在这里插入图片描述

运动函数第三版

发现问题
  1. 当多个属性的目标值不一样的时候, 不能走出对角线
  2. 当不从 0 开始的时候, 运动开始时会先回到 0 位置一次

解决问题
  1. 改变速度
    => 每 30ms 移动一次
    => 每次移动都移动剩余距离的 十分之一
  2. 拿到元素的初始位置
    => 把 distance 默认设置为元素初始位置即可
  
发现问题
  + 我不一定从 整5倍数开始, 比如 101
  + 我的目标位置也可以是一个零散的数, 此时永远无法到达目标位置
  + 我不一定向正方向前进, 此时在运动一次后定时器便会关闭
  + 运动不了 opacity
// 解决问题2

var div = document.querySelector('div')
div.addEventListener('click', function(){
    // move(div, { left: 112, top: 89 })
    move(div, { left: 623, top: 598 })
})

function move(ele, target){
  // target 有多少属性, 开启多少定时器
  for(let key in target){
      // 解决问题2: 设置为元素初始位置
      // 获取元素非行内样式(用了自己之前封装的 getStyle 函数)
      let distance = parseInt(getStyle(ele, key))
      let timer = setInterval(() => {
          distance += 10
          ele.style[key] = distance + 'px'
          if(distance >= target[key]){
              clearInterval(timer)
          }
      }, 30)
  }
}
// 解决问题1

function move(ele, target){
    // target 有多少属性, 开启多少定时器
    for(let key in target){
        // 解决问题2: 设置为元素初始位置
        // 获取元素非行内样式(用了自己之前封装的 getStyle 函数)
        let current = parseInt(getStyle(ele, key))
        let timer = setInterval(() => {
            // 每次要走的距离
            let distance += (target[key] - current) / 10 
            ele.style[key] = distance + 'px'
            if(distance >= target[key]){
                clearInterval(timer)
            }
        }, 30)
    }
}

/*
  0 ms
   0
  30ms
   拿到当前位置 0
   目标位置是 200
   计算剩余距离是 200
   我要前进到 20
  60ms
   拿到当前位置 20
   目标位置是 200
   计算剩余距离是 180
   我要前进到 20 + 18
*/
// 解决 1.因为开始和目标位置不为正数/整数而导致的永远无法到达目标位置 
// 解决 2.因为负方向运动,定时器在运动一次后便关闭

// 还是通过改变速度的方式来解决
function move(ele, target){
    for(let key in target){
        let timer = setInterval(() => {
            let current = parseInt(getStyle(ele, key))
            // 计算本次要移动的距离
            let distance = (target[key] - current) / 10 
            // 取整
            // 当 distance 是正数的时候, 向上取整
            // 当 distance 是负数的时候, 向下取整
            distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

            console.log(current, distance)

            // 判断是否到达目标位置
            if(current === target[key]){
                // 到达了
                clearInterval(timer)
            }else{
                // 没到达
                // 当前位置 + 要移动的距离
                ele.style[key] = current + distance + 'px'
            }

        }, 30)
    }
}

运动函数第四版

发现问题
  + 运动不了 opacity

解决问题
  + 拿到的时候放大 100 倍
  + 最后设置的时候再缩小 100 倍

发现问题
  + opacity 只能从 1 到 0
var div = document.querySelector('div')
div.addEventListener('click', function(){
     move(div, { opacity: 0 })
 })


 function move(ele, target){
     for(let key in target){
         let timer = setInterval(() => {
             // 获取当前样式的时候, 分成两种
             // 一种是带有 px 的, 我要去单位
             // 一种是 opacity, 放大 100 倍
             
             let current = key === 'opacity' ? getStyle(ele, 'opacity') * 100 : parseInt(getStyle(ele, key))
             // let current = 0
             // if(key === 'opacity'){
             //     current = getStyle(ele, 'opacity') * 100
             // }else{
             //     current = parseInt(getStyle(ele, key))
             // }

             let distance = (target[key] - current) / 10 
             distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

             console.log(current, distance)

             // target[key] === 1
             if(current === target[key]){
                 clearInterval(timer)
             }else{
                 // 赋值的时候, 分开赋值, 有的需要加 px, 有的需要缩小 100 倍
                 ele.style[key] = key === 'opacity' ? (current + distance) / 100 : current + distance + 'px'
             }

         }, 30)
     }
 }
opacity 只能从 1 到 0 的问题出现原因:
	=> 若开始位置 current 为0.2 , 目标位置 target[key] 为1
	=> current 放大 100 倍, 为 20,  distance 为 (target[key] - current) / 10 = -1.9 
	=> 在做判断时, 进入赋值, ele.style[key] 就等于 (20 + (-1.9) ) / 100 = 0.181
	=> 往 0 的方向走了

在这里插入图片描述

运动函数第五版

发现问题
  + opacity 只能从 1 到 0
  + 原因: target[key] 要么是 0, 要么是 1

解决问题
  + 当我准备运动的时候
  + 我判断一下, 如果你需要运动的有一个是 opacity 
    => 我直接把 target 里面的值放大 100 倍

发现问题
  + 我没办法捕捉到运动的真正结束
  + 最后一个属性运动结束以后, 才是整个运动结束
var div = document.querySelector('div')
div.addEventListener('click', function(){
    move(div, { opacity: 1 , left: 200, top: 300 })
})


function move(ele, target){
    for(let key in target){
        // 开启定时器之前, 就做好判断
        if(key === 'opacity') target[key] *= 100

        let timer = setInterval(() => {
            let current = key === 'opacity' ? getStyle(ele, 'opacity') * 100 : parseInt(getStyle(ele, key))

            let distance = (target[key] - current) / 10 
            distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

            console.log(current, distance)

            if(current === target[key]){
                clearInterval(timer)
            }else{
                ele.style[key] = key === 'opacity' ? (current + distance) / 100 : current + distance + 'px'
            }

        }, 30)
    }
}

运动函数第六版

发现问题
  + 我没办法捕捉到运动的真正结束
  + 最后一个属性运动结束以后, 才是整个运动结束

解决问题
  + 计数器
    => 在哪定义
    => 在哪开启
    => 在哪关闭

发现问题
  + 我没有办法在运动结束再做点什么
  + 需求: 运动结束之后, 背景颜色变成 粉色
  + 需求: 运动结束之后, 把 display 变成 none
var div = document.querySelector('div')
div.addEventListener('click', function(){
    move(div, { opacity: 1, left: 200, top: 300 })
})


function move(ele, target){
    // 准备一个计数器
    let count = 0
    for(let key in target){
        // 开启定时器之前, 就做好判断
        if(key === 'opacity') target[key] *= 100

        // 每开启一个计数器 定时器++
        count++

        let timer = setInterval(() => {
            let current = key === 'opacity' ? getStyle(ele, 'opacity') * 100 : parseInt(getStyle(ele, key))

            let distance = (target[key] - current) / 10 
            distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

            if(current === target[key]){
                clearInterval(timer)

                // 每结束一个属性, 关闭一个定时器, 计数器--
                count--
                
                // 当 count === 0 的时候, 表示所有的属性都运动结束了
                if(!count){
                    // console.log('运动真的结束了')
                    ele.style.backgroundColor = 'pink'
                }

            }else{
                ele.style[key] = key === 'opacity' ? (current + distance) / 100 : current + distance + 'px'
            }

        }, 30)
    }
}

运动函数第七版

发现问题
  + 我没有办法在运动结束再做点什么
  + 需求: 运动结束之后, 背景颜色变成 粉色
  + 需求: 运动结束之后, 把 display 变成 none

解决问题:
  + 目的: 运动结束后做点事情
  + 思路: 
    1. 把我想做的事情放在一个 盒子里 (函数: 就是一个盒子, 承载一段代码, 在你需要的时候调用)
    2. 把这个盒子传递给 move 函数
    3. 在运动结束的时候, 你帮我把盒子里面的代码执行一下
  + 方法:
    => 第三个参数传递一个函数, 把我想要在运动结束做的事情传递进去

发现问题
  + 当调用函数不传函数参数时, 会报错
解决问题
  方法1: fn && fn()
  方法2: 参数默认值
var div = document.querySelector('div')
div.addEventListener('click', function(){
    move(div, { opacity: 1, left: 200, top: 300 }, end)

    // 我要在运动结束做的事情
    function end(){
        div.style.backgroundColor = 'purple'
    }
})

var p = document.querySelector('p')
p.addEventListener('click', function(){
    move(p, {width: 200, height: 200 }, function(){
        p.style.display = 'none'
    })
})

// p.addEventListener('click', function(){
//     move(p, {width: 200, height: 200 })
// })     

function move(ele, target, fn = () => {}){
    // fn 接收的就是你传递进来的那个函数
    let count = 0
    for(let key in target){
        if(key === 'opacity') target[key] *= 100

        count++

        let timer = setInterval(() => {
            let current = key === 'opacity' ? getStyle(ele, 'opacity') * 100 : parseInt(getStyle(ele, key))

            let distance = (target[key] - current) / 10 
            distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

            if(current === target[key]){
                clearInterval(timer)

                count--
                
                if(!count){
                    // 把你传递进来的函数执行一下
                    // fn && fn()  // 以前的做法
                    fn()
                }

            }else{
                ele.style[key] = key === 'opacity' ? (current + distance) / 100 : current + distance + 'px'
            }

        }, 30)
    }
}

简单版多属性运动函数的封装

/**
 * 简单版多属性运动函数
 * @param {ELEMENT} ele 要运动的元素 
 * @param {OBJECT} target 要运动的属性(对象)
 * @param {FUNCTION} fn 运动结束的回调函数
 */
 function move(ele, target, fn = () => {}){
     let count = 0
     for(let key in target){
         if(key === 'opacity') target[key] *= 100

         count++

         let timer = setInterval(() => {
             let current = key === 'opacity' ? getStyle(ele, 'opacity') * 100 : parseInt(getStyle(ele, key))

             let distance = (target[key] - current) / 10 
             distance = distance > 0 ? Math.ceil(distance): Math.floor(distance)

             if(current === target[key]){
                 clearInterval(timer)

                 count--
                 
                 if(!count) fn()

             }else{
                 ele.style[key] = key === 'opacity' ? (current + distance) / 100 : current + distance + 'px'
             }

         }, 30)
     }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值