javascript(2)-高级操作

252 阅读1分钟

1. 表单事件

概念:

  • form.onsubmit = () =>{}:当表单提交时触发的事件。
  • form.onreset = () =>{}:当表单重置时触发的事件。
  • input.onkeyup = () => {}:在文本框中,将按键抬起来时触发的事件。
  • input.onblur = () => {}:当文本框丧失焦点时触发的事件。

布局:

<section id="test-onsubmit">
    <form id="onsubmit-form" method="get" action="#">
        <label>
            <span>帐号:</span>
            <input id="onsubmit-username-ipt" name="username" />
        </label>
        <label>
            <span>密码:</span>
            <input id="onsubmit-password-ipt" name="password" type="password"/>
        </label>
        <button type="submit">登录</button>
    </form>
</section>

<section id="test-onreset">
    <form id="onreset-form" method="get" action="#">
        <label>
            <span>帐号:</span>
            <input id="onreset-username-ipt" name="username" />
        </label>
        <label>
            <span>密码:</span>
            <input id="onreset-password-ipt" name="password" type="password"/>
        </label>
        <button type="submit">登录</button>
        <button type="reset">重置</button>
    </form>
</section>

<section id="test-onkeyup">
    <form id="onkeyup-form" method="get" action="#">
        <label>
            <span>帐号:</span>
            <input id="onkeyup-username-ipt" name="username" />
        </label>
        <button type="submit">登录</button>
    </form>
</section>

<section id="test-onblur">
    <form id="onblur-form" method="get" action="#">
        <label>
            <span>帐号:</span>
            <input id="onblur-username-ipt" name="username" />
        </label>
        <button type="submit">登录</button>
    </form>
</section>

<script type="text/javascript">

    window.onload = ()=>{
   		      当表单提交时触发的事件。
        let onsubmitForm = document.querySelector("#onsubmit-form");
        onsubmitForm.onsubmit = () => {
            let usernameIpt = document.querySelector("#onsubmit-username-ipt");
            let passwordIpt = document.querySelector("#onsubmit-password-ipt");
            if("" === usernameIpt["value"] || "" === passwordIpt["value"] ){
                alert("账号或者密码不能为空!");
                return false;
            }
        };
			当表单重置时触发的事件。
        let onresetForm = document.querySelector("#onreset-form");
        onresetForm.onreset = () => {
            let response = confirm("您将要重置表单,确认吗?");
            if (!response) {
                return false;
            }
        };
			在文本框中,将按键抬起来时触发的事件。
        let onkeyupUsernameIpt = document.querySelector("#onkeyup-username-ipt");
        onkeyupUsernameIpt.onkeyup = () => {
            onkeyupUsernameIpt["value"] = onkeyupUsernameIpt["value"].toUpperCase();
        };
			当文本框丧失焦点时触发的事件。
        let onblurUsernameIpt = document.querySelector("#onblur-username-ipt");
        onblurUsernameIpt.onblur = () => {
            if (!onblurUsernameIpt["value"].match("^[\\w]+$")) {
                onblurUsernameIpt["value"] = "";
                onblurUsernameIpt.focus();
                alert("账号中不允许存在非法字符!");
            }
        }
    };
</script>

2. event关键字

概念: event 是一个函数中内置对象,用来获取事件的详细信息,如鼠标,键盘等:

  • event兼容性:ev = ev || event
    • IE9以下版本的浏览器仅支持函数体中直接使用 event
    • FF浏览器仅支持在函数参数中的一号位置直接定义 ev,命名可以更改。
    • Chrome浏览器两种写法都支持。
  • event事件冒泡:子元素会继续向上触发父元素的同类型事件:
    • event.cancelBubble = true:在子元素的事件中取消事件冒泡。
  • event鼠标坐标:
    • event.clientX:获取鼠标点击时横坐标。
    • event.clientY:获取鼠标点击时纵坐标。

布局:

    <style type="text/css">
        #test-body {
            border: 1px solid red;
        }

        #test-sec {
            margin-top: 100px;
            height: 100px;
            background: yellowgreen;
        }
    </style>
	//子孙三代
<body id="test-body">
<section id="test-sec">
    <button id="test-btn" type="button">事件冒泡</button>
</section>

<script type="text/javascript">
    window.onload = () => {
    //点击出坐标
        document.onclick = ev => {
            ev = ev || event;
            let x = ev.clientX;
            let y = ev.clientY;
            console.log(x, y);
        };

        let testBody = document.querySelector("#test-body");
        let testSec = document.querySelector("#test-sec");
        let testBtn = document.querySelector("#test-btn");

        testBody.onclick = () => {
            alert("I am body...");
        };
		//阻止继续冒泡
        testSec.onclick = (ev) => {
            ev = ev || event;
            alert("I am sec...");
            ev.cancelBubble = true;
        };

        testBtn.onclick = () => {
            alert("I am btn...");
        };

    };
</script>
</body>

2.1鼠标跟踪

概念:

  • document.onmousemove = () => {}:当鼠标在文档上移动时触发的事件。
  • obj["style"]["width"]:获取当前宽度,值带单位,如 50px,所以只适合设置。
  • obj.offsetWidth:获取当前宽度,值不带单位,如 50,适合计算,但不适合设置。
  • document.documentElement.scrollLeft:获取当前滚动条距离页面左边界的距离。
    • document.body.scrollLeft:IE9以下支持的写法。
  • document.documentElement.scrollTop:获取当前滚动条距离页面上边界的距离。
    • document.body.scrollTop:IE9以下支持的写法。

布局:

 <style type="text/css">
        body {
            height: 1000px;
            width: 5000px;
        }
        #pen {
            width: 50px;
            height: 50px;
            background-color: yellow;
            border-radius: 50%;
            position: absolute;
        }
</style>

<section id="pen"></section>

<script type="text/javascript">
    /*需求:让一个黄色的圆形跟随鼠标移动而移动。*/
    window.onload = () => {

        let pen = document.querySelector("#pen");
        document.onmousemove = (ev) => {
            ev = ev || event;
            let [x, y] = [ev.clientX, ev.clientY];
            let [width, height] = [pen.offsetWidth, pen.offsetHeight];
            let scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

            /*pen的左距离 = 鼠标横坐标 + 滚动条距离页面左边界的距离 - pen半宽 + "px"*/
            pen["style"]["left"] = x + scrollLeft - (width / 2) + "px";
            pen["style"]["top"] = y + scrollTop - (height / 2) + "px";
        };
    }
</script>
<style>
       .bug {
           width: 20px;
           height: 20px;
           border-radius: 50%;
           position: absolute;
       }
</style>

<body>

<section>
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">
   <img class="bug" src="../../z-image/bug.png" alt="">

</section>

<script type="text/javascript">
   window.onload = () => {
       document.onmousemove = (ev) => {
           ev = ev || event;
           let bugs = document.querySelectorAll(".bug");
           bugs[0]["style"]["left"] = ev.clientX + "px";
           bugs[0]["style"]["top"] = ev.clientY + "px";

           for (let i = bugs.length - 1; i > 0; i--) {
               bugs[i]["style"]["left"] = bugs[i - 1]["style"]["left"];
               bugs[i]["style"]["top"] = bugs[i - 1]["style"]["top"];
           }
       }
   }
</script>

2.2 鼠标拖拽

概念:

  • document.onmousedown = () => {}:当鼠标按下时触发的事件。
  • document.onmousemove = () => {}:当鼠标移动时触发的事件。
  • document.onmouseup = () => {}:当鼠标抬起时触发的事件。
  • document.documentElement.clientWidth:获取屏幕宽。
  • document.documentElement.clientHeight:获取屏幕高。
    <style type="text/css">
        #ball {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            border-radius: 50%;
        }
    </style>

<section id="ball"></section>

<script type="text/javascript">
    window.onload = () => {
        let ball = document.querySelector("#ball");

        /*鼠标按下时,计算l和t这两个恒定值*/
        ball.onmousedown = (ev) => {
            ev = ev || event;
            let l = ev.clientX - ball.offsetLeft;
            let t = ev.clientY - ball.offsetTop;

            /*在鼠标按下的前提下移动**/
            document.onmousemove = (ev) => {
                ev = ev || event;
                ball["style"]["left"] = ev.clientX - l + "px";
                ball["style"]["top"] = ev.clientY - t + "px";
            };

            /*在鼠标按下的前提下抬起*/
            document.onmouseup = () => document.onmousemove = null;

        }
    };
</script>
    <style type="text/css">
        body {
            margin: 0;
        }

        #board {
            width: 300px;
            height: 300px;
            background-color: red;
            position: relative;
        }

        #empty {
            width: 100px;
            height: 100px;
            background-color: white;
            position: absolute;
            left: 100px;
            top: 100px;
        }

        #fill {
            width: 100px;
            height: 100px;
            background-color: green;
            position: absolute;
        }
    </style>


<section id="board">
    <section id="empty"></section>
</section>
<section id="fill"></section>

<script type="text/javascript">
    window.onload = () => {
        let fill = document.querySelector("#fill");
        fill.onmousedown = ev => {
            ev = ev || event;
            let l = ev.clientX - fill.offsetLeft;
            let t = ev.clientY - fill.offsetTop;
            document.onmousemove = ev => {
                ev = ev || event;
                fill["style"]["left"] = ev.clientX - l + "px";
                fill["style"]["top"] = ev.clientY - t + "px";
            };

            document.onmouseup = () => {
                document.onmousemove = null;

                /* 如果<section>正好和 [空缺] 重合,则宣布成功*/
                if (fill.offsetLeft === 100 && fill.offsetTop === 100) {
                    fill.style.backgroundColor = "red";
                }} } }
</script>

2.3 键盘控制

概念:

  • event.keyCode:获取当前键盘码编号:如 37 表示左键,38 表示上键等。
  • document.onkeydown = () => {}:某个按键按下去时触发的事件。
    <style type="text/css">
        #board {
            width: 1000px;
            height: 500px;
            outline: 5px solid gray;
            position: relative;
            margin: 0 auto;
        }

        #car {
            width: 50px;
            height: 50px;
            background-color: green;
            position: absolute;
        }
    </style>

<section id="board">
    <section id="car"></section>
</section>

<script type="text/javascript">
    const [leftKey, topKey, rightKey, bottomKey] = [37, 38, 39, 40];
    const [horizontalStep, verticalStep] = [50, 50];
    const [carWidth, carHeight] = [50, 50];
    const [boardWidth, boardHeight] = [1000, 500];

    /*用键盘的上下左右键控制单个section移动。*/
    window.onload = () => {
        let car = document.querySelector("#car");
        document.onkeydown = (ev) => {
            ev = ev || event;
            switch (ev.keyCode) {
                case leftKey :
                    if (car.offsetLeft <= 0) return;
                    car["style"]["left"] = car.offsetLeft - horizontalStep + "px";
                    break;
                case topKey :
                    if (car.offsetTop <= 0) return;
                    car["style"]["top"] = car.offsetTop - verticalStep + "px";
                    break;
                case rightKey :
                    if (car.offsetLeft >= boardWidth - carWidth) return;
                    car["style"]["left"] = car.offsetLeft + horizontalStep + "px";
                    break;
                case bottomKey :
                    if (car.offsetTop >= boardHeight - carHeight) return;
                    car["style"]["top"] = car.offsetTop + verticalStep + "px";
                    break;
            }
        }
    };
</script>
    <style type="text/css">
        #board {
            width: 1000px;
            height: 500px;
            outline: 10px solid red;
            position: relative;
            margin: 0 auto;
        }

        .snake {
            width: 50px;
            height: 50px;
            border-radius: 10px;
            background-color: green;
            position: absolute;
        }

        #snake-head {
            background-color: black;
            z-index: 1;
        }
    </style>

<body>
<section id="board">
    <div class="snake" id="snake-head"></div>
    <div class="snake"></div>
    <div class="snake"></div>
    <div class="snake"></div>
    <div class="snake"></div>
</section>

<script type="text/javascript">
    const [leftKey, topKey, rightKey, bottomKey] = [37, 38, 39, 40];
    const [horizontalStep, verticalStep] = [50, 50];
    const [snakeWidth, snakeHeight] = [50, 50];
    const [boardWidth, boardHeight] = [1000, 500];

    window.onload = () => {
        document.onkeydown = () => {
            snakeBodyMove();
            snakeHeadMove();
        };
    };

    /*后面的蛇身追赶前面的蛇身*/
    function snakeBodyMove() {
        let snakes = document.querySelectorAll(".snake");
        for (let i = snakes.length - 1; i > 0; i--) {
            snakes[i]["style"]["left"] = snakes[i - 1]["style"]["left"];
            snakes[i]["style"]["top"] = snakes[i - 1]["style"]["top"];
        }
    }

    function snakeHeadMove(ev) {
        let snakeHead = document.querySelector("#snake-head");
        ev = ev || event;
        switch (ev.keyCode) {
            case leftKey :
                if (snakeHead.offsetLeft <= 0) return;
                snakeHead["style"]["left"] = snakeHead.offsetLeft - horizontalStep + "px";
                break;
            case topKey :
                if (snakeHead.offsetTop <= 0) return;
                snakeHead["style"]["top"] = snakeHead.offsetTop - verticalStep + "px";
                break;
            case rightKey :
                if (snakeHead.offsetLeft >= boardWidth - snakeWidth) return;
                snakeHead["style"]["left"] = snakeHead.offsetLeft + horizontalStep + "px";
                break;
            case bottomKey :
                if (snakeHead.offsetTop >= boardHeight - snakeHeight) return;
                snakeHead["style"]["top"] = snakeHead.offsetTop + verticalStep + "px";
                break;
        }
    }
</script>

2.4 组合提交

概念: 多功能键如 shiftalt 或者 ctrl 可以配合 keyCode 一起使用,但前提是快捷键没有被占用:

  • event.keyCode && event.shiftKeykeyCode 配合 shift 键。
  • event.keyCode && event.altKeykeyCode 配合 alt 键。
  • event.keyCode && event.ctrlKeykeyCode 配合 ctrl 键。
    <style type="text/css">
        #screen {
            width: 505px;
            height: 200px;
            outline: 1px solid red;
            margin: 5px auto;
            overflow-y: auto;
        }

        #msg {
            text-align: center;
        }
    </style>

<section id="screen"></section>
<section id="msg">
    <label>
        <textarea id="textarea" rows="3" cols="60"></textarea>
    </label>
</section>

<script type="text/javascript">
    /*需求:模拟聊天框,使用 `shift + Enter` 组合提交信息。*/
    window.onload = () => {
        let screen = document.querySelector("#screen");
        let textarea = document.querySelector("#textarea");
        textarea.focus();
        document.onkeydown = (ev) => {
            ev = ev || event;
            if (ev.keyCode === 13 && ev.shiftKey) {
                let msgHead = `[赵四]:192.168.0.0:<br/>::`;
                screen.innerHTML += msgHead + textarea["value"] + "<br/><br/>";
                textarea["value"] = "";
                textarea.focus();
            }
        }
    }
</script>

2.5 右键菜单

概念: document.oncontextmenu = () => {}:单击鼠标右键时触发的事件。

    <style type="text/css">
        #context-menu {
            width: 250px;
            background-color: black;
            display: none;
            position: absolute;
        }

        #context-menu a {
            text-decoration: none;
            color: white;
        }

        #context-menu li {
            margin: 5px 0;
            list-style-type: none;
        }

        #context-menu .divider {
            border-bottom: 1px solid gray;
            margin-top: 5px;
            margin-bottom: 5px;
            margin-left: -40px;
            height: 1px;
        }
    </style>

<nav>
    <ul id="context-menu">
        <li><a href="#">返回(B)</a></li>
        <li><a href="#">前进(F)</a></li>
        <li><a href="#">重新加载</a></li>
        <li class="divider"></li>
        <li><a href="#">另存为(A)...</a></li>
        <li><a href="#">打印(p)...</a></li>
        <li><a href="#">投射(C)...</a></li>
        <li><a href="#">翻成中文(简体)(T)</a></li>
        <li class="divider"></li>
        <li><a href="#">查看网页源代码(V)</a></li>
        <li><a href="#">检查(N)</a></li>
    </ul>
</nav>

<script type="text/javascript">
    /*需求:当在页面上点击鼠标右键的时候,弹出自己自定义的右键菜单。*/
    window.onload = () => {
        let contextMenu = document.querySelector("#context-menu");
        document.oncontextmenu = (ev) => {
            ev = ev || event;
            contextMenu["style"]["left"] = ev.clientX + "px";
            contextMenu["style"]["top"] = ev.clientY + "px";
            contextMenu["style"]["display"] = "block";
            return false;
        };
        document.onclick = () => {
            contextMenu["style"]["display"] = "none";
        }
    };
</script>

3. 时间类型

概念: JS中通过 let now = new Date() 来创建本地时间对象:

  • now.getFullYear():返回今年是哪一个年。
  • now.getMonth():返回现在是几月份,老外从12月份开始算1月份。
  • now.getDate():返回今天是几号。
  • now.getDay():返回今天是星期几。
  • now.getHours():返回现在的小时数。
  • now.getMinutes():返回现在的分钟数。
  • now.getSeconds():返回现在的秒数。
  • now.getMilliseconds():返回现在的毫秒数。

如果是用反引号作为字符串符号,那么可以在其中直接使用 ${} 插值表达式进行插值。

    <style type="text/css">
        #screen {
            width: 250px;
            height: 50px;
            border: 1px solid red;
        }
    </style>

<section id="screen"></section>
<button id="current-date-btn">获取当前时间</button>

<script type="text/javascript">
    /*需求:点击一个按钮,能够显示出当前的时间。*/
    window.onload = () => {
        let currentDateBtn = document.querySelector("#current-date-btn");
        currentDateBtn.onclick = () => {
            let screen = document.querySelector("#screen");
            let now = new Date();
            let yyyy = now.getFullYear();
            let mm = now.getMonth() + 1;
            let dd = now.getDate();
            let day = now.getDay();
            let hh = now.getHours();
            let mi = now.getMinutes();
            let ss = now.getSeconds();
            screen.innerHTML = `${yyyy}${mm}${dd}日 星期${day} ${hh}:${mi}:${ss}`;
        }
    }
</script>

4.1 周期定时器

概念: 周期定时器会每隔X时间重复执行Y事件:

  • window.setInterval(fn, t):每隔t毫秒执行一次 fn 函数。
    • param1:定时函数的函数名,也可以直接使用一个匿名函数。
    • param2:周期时间,单位毫秒。
    • return:本次开启的定时器的编号,number类型。
  • window.clearInterval(id):通过定时器编号来关闭周期定时器。
    <style type="text/css">
        #screen {
            width: 250px;
            height: 50px;
            border: 1px solid red;
        }
    </style>

<section id="screen"></section>
<button id="start-btn">开启周期定时器</button>
<button id="stop-btn">停止周期定时器</button>

<script type="text/javascript">
    window.onload = () => {
        let timer01;
        let screen = document.querySelector("#screen");
        let startBtn = document.querySelector("#start-btn");
        startBtn.onclick = () => {
            timer01 = setInterval(() => {
                screen.innerHTML = `${new Date().getSeconds()}`;
            }, 1000);
        };

        let stopBtn = document.querySelector("#stop-btn");
        stopBtn.onclick = () => {
            clearInterval(timer01);
        };
    }
</script>
    <style type="text/css">
        body {
            text-align: center;
        }

        #clock {
            width: 1000px;
            margin: 0 auto;
            border: 5px inset black;
            padding: 20px;
            color: red;
            font-size: 50px;
        }

        #date{
            font-size: 1em;
        }

        #time{
            font-size: 0.8em;
        }
    </style>

<h4>数码时钟案例</h4>
<section id="clock">
    <div id="date"></div>
    <div id="time"></div>
</section>

<script type="text/javascript">

    /*需求:制作一个数码时钟*/
    onload = () => {
        /*在定时器开启之前,先调用一次show;*/
        task();
        setInterval(task, 1000);
    };

    /*定时器任务*/
    function task() {
        let now = new Date();
        let yy = now.getFullYear();
        let mm = format(now.getMonth() + 1);
        let dd = format(now.getDate());
        let hh = format(now.getHours());
        let mi = format(now.getMinutes());
        let ss = format(now.getSeconds());
        let dateArt = document.querySelector("#date");
        let timeArt = document.querySelector("#time");
        dateArt.innerHTML = `${yy} - ${mm} - ${dd}`;
        timeArt.innerHTML = `${hh} : ${mi} : ${ss}`;
    }

    /*对一位数的值进行处理,在前面加上一个0,否则原样返回*/
    function format(obj) {
        return obj < 10 ? "0" + obj : obj;
    }
</script>

4.2 延时定时器

概念: 延时定时器会在X时间后仅执行一次Y事件:

  • window.setTimeout(fn, t):t毫秒后执行一次 fn 函数。
    • param1:定时函数的函数名,也可以直接使用一个匿名函数。
    • param2:延迟时间,单位毫秒。
    • return:本次开启的定时器的编号,number类型。
  • window.clearTimeout(id):通过定时器编号来关闭延迟定时器。
    <style type="text/css">
        #screen {
            width: 250px;
            height: 50px;
            border: 1px solid red;
        }
    </style>

<section id="screen"></section>
<button id="start-btn">开启延时定时器</button>
<button id="stop-btn">停止延时定时器</button>

<script type="text/javascript">
    onload = () => {
        let timer01;
        let startBtn = document.querySelector("#start-btn");
        let screen = document.querySelector("#screen");
        startBtn.onclick = () => {
            timer01 = setTimeout(() => {
                screen.innerHTML = "start!";
            }, 3000);
        };

        let stopBtn = document.querySelector("#stop-btn");
        stopBtn.onclick = () => {
            clearTimeout(timer01);
        };
    }
</script>
    <style type="text/css">
        #head {
            width: 50px;
            height: 50px;
            background-color: red;
            float: right;
            margin-left: 10px;
        }

        #info {
            width: 500px;
            height: 500px;
            background-color: blue;
            display: none;
            float: right;
        }
    </style>

<div id="head"></div>
<div id="info"></div>

<script type="text/javascript">
    /*  思路:
        1. head(头像)默认显示,info(信息)默认不显示。
        2. 当鼠标移入头像head,立即显示信息info。
        3. 当鼠标移入信息info,继续显示信息info。
        4. 当鼠标移出头像head
            - 进入空白区,开启延时定时器,0.5秒后关闭信息info。
            - 在0.5秒内进入信息info,则关闭让信息info消失的延时定时器。
        5. 当鼠标移出信息info
            - 进入空白区,开启延时定时器,0.5秒后关闭信息info。
            - 在0.5秒内进入头像head,则关闭让信息info消失的延时定时器。
    */
    window.onload = () => {
        let head = document.querySelector("#head");
        let info = document.querySelector("#info");
        let timer = null;

        head.onmouseover = info.onmouseover = () => {
            clearTimeout(timer);
            info["style"]["display"] = "block";
        };

        head.onmouseout = info.onmouseout = () => {
            timer = setTimeout(() => {
                info["style"]["display"] = "none";
            }, 500);
        };
    }
</script>

4.3 运动框架

概念: 常见运动分为匀速运动和缓冲运动两种类型:

  • 匀速运动:速度 = 每次移动距离。
  • 缓冲运动:速度 = 当前距离 / 缓冲常数。
    • 计算机最小单位为整数级像素,所以 12.x 像素全都视为 12px,向下取整。

案例 点击小球 匀速移动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #board {
            margin: 10px auto;
            width: 600px;
            height: 100px;
            outline: 5px solid black;
            position: relative;
        }

        #ball {
            width: 100px;
            height: 100px;
            background-image: radial-gradient(circle at center, red, black);
            border-radius: 50%;
            position: absolute;
        }
    </style>
</head>
<body>
<section id="board">
    <div id="ball"></div>
</section>

<script type="text/javascript">
    const [period, speed] = [50, 10];/*每隔50ms移动10px*/
    const [boardWidth, ballWidth] = [600, 100];
    let timer, ball;

    /* 需求:点击小球,小球开始匀速运动。*/
    window.onload = () => {
        ball = document.querySelector("#ball");
        ball.onclick = () => {
            clearInterval(timer);
            timer = setInterval(move, period);
        }
    };

    function move() {
        if (ball.offsetLeft >= boardWidth - ballWidth) {
            clearInterval(timer);
            return;
        }
        ball["style"]["left"] = ball.offsetLeft + speed + "px";
    }
</script>
</body>
</html>

案例 侧边栏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #sidebar {
            width: 150px;
            height: 200px;
            background: #00CBCB;
            position: absolute;
            top: 50px;
            left: -150px;
        }

        #sidebar span {
            width: 20px;
            height: 60px;
            background: #00CBCB;
            line-height: 20px;
            position: absolute;
            right: -20px;
            top: 70px;
        }
    </style>
</head>
<body>
<section id="sidebar">
    <span>侧边栏</span>
</section>

<script type="text/javascript">
    let timer, sidebar;
    let [speed, period] = [10, 50];

    window.onload = () => {
        sidebar = document.querySelector("#sidebar");
        sidebar.onmouseover = () => move(0);
        sidebar.onmouseout = () => move(-150);
    };

    function move(target) {
        let currentSpeed = sidebar.offsetLeft > target ? -speed : speed;
        clearInterval(timer);
        timer = setInterval(() => {
            if (sidebar.offsetLeft === target) {
                clearInterval(timer);
                return;
            }
            sidebar["style"]["left"] = sidebar.offsetLeft + currentSpeed + "px";
        }, period);
    }
</script>
</body>
</html>

案例 点击小球缓冲运动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        body {
            margin: 0;
        }

        #ball {
            width: 100px;
            height: 100px;
            background-image: radial-gradient(circle at center, red, black);
            border-radius: 50%;
            position: absolute;
        }

        #board {
            margin: 10px auto;
            width: 600px;
            height: 100px;
            outline: 5px solid black;
            position: relative;
        }
    </style>
</head>
<body>
<section id="board">
    <div id="ball"></div>
</section>

<script type="text/javascript">

    const period = 50;
    const [boardWidth, ballWidth] = [600, 100];
    const speedNum = 10;
    let speed, timer, ball;

    /*需求: 点击小球,小球开始进行缓冲运动。*/
    window.onload = () => {
        ball = document.querySelector("#ball");
        ball.onclick = () => {
            clearInterval(timer);
            timer = setInterval(() => {
                if (ball.offsetLeft >= boardWidth - ballWidth) {
                    clearInterval(timer);
                    return;
                }
                speed = (boardWidth - ballWidth - ball.offsetLeft) / speedNum;
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                ball["style"]["left"] = ball.offsetLeft + speed + "px";
            }, period);
        }
    };
</script>

</body>
</html>

案例 完美运动框架

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        section {
            width: 200px;
            height: 200px;
            font-size: 10px;
            background: red;
        }
    </style>
</head>
<body>
<section id="test-sec">完美运动框架</section>

<script type="text/javascript">
    /*需求:将section高度变为500px,宽度变为1000px,字体大小变为50px。*/
    window.onload = () => {
        let testSec = document.querySelector("#test-sec");
        let target = {"width": "500px", "height": "1000px", "fontSize": "50px"};
        let params = {"target": target, "callback": callback};
        testSec.onclick = () => move(testSec, params);

        function callback() {
            alert("change over!");
        }
    };

    /* 完美运动框架
     * obj:要发生运动的元素,object类型。
     * params:运动参数,json类型:
     *     target: 运动目标,json类型。
     *     speedNum:缓冲常数,数值越大,幅度越大,默认10。
     *     period:运动周期,数值越大,运动越快,默认50毫秒。
     *     callback:运动结束后的回调函数,可以省略。
     * */
    function move(obj, params) {
        const speedNum = params["speedNum"] ? params["speedNum"] : 10;
        const period = params["period"] ? params["period"] : 50;
        const target = params["target"] ? params["target"] : null;
        const callback = params["callback"] ? params["callback"] : null;
        if (!target) return;
	/*清空指定物体之前所挂载的旧定时器*/
        clearInterval(obj.timer);
         /*对指定物体开启新定时器,周期为period*/
        obj.timer = setInterval(() => {
         /*遍历运动物体的目标参数*/
            for (let key in target) {
            //判断target中有没有key
                if (target.hasOwnProperty(key)) {
               		 /*获取参数目标值*/
                    let targetValue = parseInt(target[key]);
                    		  /*获取参数当前值*/
                    let currentValue = parseInt(css(obj, key));
                    	 /*如果达到目标参数停止定时器*/
                    if (currentValue === targetValue) {
                        clearInterval(obj.timer);
                         /*如果用户传入回调函数,则执行回调函数*/
                        if (callback) callback();
                        return;
                    }
                     /*计算缓冲速度,处理小数,运动*/
                    let speed = (targetValue - currentValue) / speedNum;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    obj["style"][key] = currentValue + speed + "px";
                }
            }
        }, period);
		/*通过元素的样式key,来获取value*/
        function css(obj, key) {
            return obj["currentStyle"] ? obj["currentStyle"][key] : getComputedStyle(obj)[key];
        }
    }
</script>
</body>
</html>