成为前端牛马第二十天 —— js案例大杂烩

        一时拖更一时爽,一直拖更一直爽。。。

        再拖就掉粉了。。。

        赶紧爬起来更新。。。

        今天做了好些个案例,要求比较全面,要有合理的布局,扎实的js基础,熟练的DOM操作,这些我都没有 。。。OvO,写的很小学生水平,看个乐就好,如有错误,恳请指正。

一. 模拟手机验证码

        要实现下图的功能点,验证手机号码,判断号码格式,以及倒计时再发送。

        手机号码的校验可以使用正则匹配

        let btn = document.querySelector(".btn");
        let inp = document.querySelector(".inp");
        let timer = null;
        let count = 5;

        btn.addEventListener("click", () => {
          let reg = /^1[3-9]\d{9}$/;
          if (inp.value == "") {
            alert("要输入手机号 OvO ");
            return;
          }
          if (reg.test(inp.value)) {
            // console.log(1);
            fn();
          } else {
            alert("手机号格式不正确 OvO ");
            return;
          }
        });

        要实现倒计时功能,必须保证定时器在运行时不能被打断,不能被重新赋值。

        我的解决思路是,没有定时器即 timer 为空,取反时添加定时器,定时器不停止,timer 不清空时,!timer 为false,是不能再添加定时器的,这样就能解决了。

function fn() {
          if (!timer) {
            timer = setInterval(() => {
              count--;
              btn.classList.toggle("active");
              btn.innerHTML = `${count}秒后可再次发送`;
              //   console.log(count);
              if (count <= 0) {
                btn.classList.toggle("active");
                clearInterval(timer);
                count = 5;
                btn.innerHTML = `点击发送验证码`;
              }
            }, 1000);
          }
        }

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模拟发送验证码</title>
    <style>
      .box {
        display: flex;
        height: 60px;
        align-items: center;
      }
      .format {
        width: 100px;
        height: 32px;
        border-radius: 5px;
        background-color: skyblue;
        text-align: center;
        line-height: 32px;
        margin-right: 20px;
      }
      .inp {
        width: 150px;
        height: 30px;
        border-radius: 5px;
        border: 1px solid black;
        margin-right: 20px;
        padding: 0 10px;
        outline: 0;
      }
      .btn {
        width: 120px;
        height: 32px;
        border-radius: 5px;
        background-color: skyblue;
        border: 0;
        cursor: pointer;
      }
      .active {
        background-color: rgb(175, 225, 245);
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="format">中国 +86</div>
      <label for="">手机号码:</label>
      <input type="text" class="inp" />
      <button class="btn">点击发送验证码</button>

      <script>
        let btn = document.querySelector(".btn");
        let inp = document.querySelector(".inp");
        let timer = null;
        let count = 5;

        btn.addEventListener("click", () => {
          let reg = /^1[3-9]\d{9}$/;
          if (inp.value == "") {
            alert("要输入手机号 OvO ");
            return;
          }
          if (reg.test(inp.value)) {
            // console.log(1);
            fn();
          } else {
            alert("手机号格式不正确 OvO ");
            return;
          }
        });

        function fn() {
          if (!timer) {
            timer = setInterval(() => {
              count--;
              btn.classList.toggle("active");
              btn.innerHTML = `${count}秒后可再次发送`;
              //   console.log(count);
              if (count <= 0) {
                btn.classList.toggle("active");
                clearInterval(timer);
                count = 5;
                btn.innerHTML = `点击发送验证码`;
              }
            }, 1000);
          }
        }
      </script>
    </div>
  </body>
</html>

二.模拟滑块验证码

        实现以下功能,鼠标按下滑块并移动时,滑块跟随移动,验证码页随之移动,最后重叠误差小于4像素时判定验证成功。

        右侧的是遮罩层,用一个半透明盒子遮盖住背景图片的一部分,通过定位方式排版,左侧的验证滑块需要塞入整个背景图片,通过 backgroundPosition 属性来移动背景图,值就为遮罩层的定位偏移数据,主页就可以实现两个滑块显示背景图的同一部分了。

        实现的一个难点是,验证块的半圆也需要添加背景图片,通过定位的方式跟验证保持一致,半圆定位 left top 值,就是背景图片需要偏移的值。

let img = document.querySelector(".img");
      let mask = document.querySelector(".mask");
      let square = document.querySelector(".square");
      let sliding = document.querySelector(".sliding");
      let sliding_box = document.querySelector(".sliding_box");
      let square_after = document.querySelector(".after");
      let square_before = document.querySelector(".before");

      //遮罩层随机位置,只能出现在右半部分,所以需要进行限制
      let ran_left = Math.floor(Math.random() * 240 + 240);
      let ran_top = Math.floor(Math.random() * 205);
      //   console.log(ran_position);
      mask.style["left"] = ran_left + "px";
      mask.style["top"] = ran_top + "px";

      //方块位置同高 方块对应背景图
      square.style["top"] = ran_top + "px";
      square.style["backgroundPosition"] = `-${ran_left}px -${ran_top}px`;

      square_before.style["backgroundPosition"] = `-${ran_left - 15}px -${
        ran_top + 30
      }px`;
      square_after.style["backgroundPosition"] = `-${ran_left + 30}px -${
        ran_top - 15
      }px`;

         剩下的功能难点就集中在两个滑块的偏移位置了

//滑块移动
      let isStop = true;

      //鼠标按下时
      sliding_box.addEventListener("mousedown", () => {
        isStop = false;
      });

      //鼠标按下并移动时
      sliding_box.addEventListener("mousemove", (e) => {
        if (!isStop) {
          //获取鼠标距离盒子的距离,减去滑块的一半宽度,使鼠标居于滑块中心点
          let leftX =
            e.pageX - sliding.offsetLeft - sliding_box.offsetWidth / 2;
          //作两个判断,滑块移动到右端停止,移动到左端赋0
          if (leftX > sliding.offsetWidth - sliding_box.offsetWidth)
            leftX = sliding.offsetWidth - sliding_box.offsetWidth;
          if (leftX < 0) leftX = 0;
          //赋值给滑块
          sliding_box.style["left"] = leftX + "px";
          //方块跟随滑块移动
          square.style["left"] = leftX + "px";
        }
      });

      //鼠标抬起时
      sliding_box.addEventListener("mouseup", () => {
        isStop = true;
        //做个绝对值判断
        let left_abs = Math.abs(
          parseInt(square.style["left"]) - parseInt(mask.style["left"])
        );
        if (left_abs < 5) {
          console.log("验证成功");
        } else {
          console.log("验证失败,请重新验证");
          square.style["left"] = 25 + "px";
          sliding_box.style["left"] = 0 + "px";
        }
      });

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模拟滑动验证</title>
    <style>
      body {
        background-color: rgba(232, 232, 232, 1);
      }
      .img {
        width: 640px;
        height: 300px;
        background-image: url(./img/3.jpeg);
        background-repeat: no-repeat;
        position: relative;
      }
      .mask {
        width: 80px;
        height: 80px;
        background-color: rgba(240, 240, 240, 0.5);
        position: absolute;
        background-repeat: no-repeat;
        top: 110px;
        left: 400px;
      }
      .mask::after {
        content: "";
        width: 15px;
        height: 25px;
        /* background-color: pink; */
        position: absolute;
        top: 30px;
        left: -15px;
        border-top-left-radius: 15px;
        border-bottom-left-radius: 15px;
        background-color: rgba(240, 240, 240, 0.5);
        /* background-image: url(./img/3.jpeg); */
      }
      .mask::before {
        content: "";
        width: 25px;
        height: 15px;
        /* background-color: pink; */
        position: absolute;
        top: -15px;
        left: 30px;
        border-top-right-radius: 15px;
        border-top-left-radius: 15px;
        background-color: rgba(240, 240, 240, 0.5);

        /* background-image: url(./img/3.jpeg); */
      }
      .square {
        width: 80px;
        height: 80px;
        background-color: rgba(240, 240, 240, 0.5);
        position: absolute;
        left: 25px;
        background-image: url(./img/3.jpeg);
        background-repeat: no-repeat;
        /* background-position: -100px -100px; */
        /* border: 1px solid #fff; */
      }
      .before {
        width: 15px;
        height: 25px;
        position: absolute;
        top: 30px;
        left: -15px;
        border-top-left-radius: 15px;
        border-bottom-left-radius: 15px;
        background-image: url(./img/3.jpeg);
      }
      .after {
        width: 25px;
        height: 15px;
        position: absolute;
        top: -15px;
        left: 30px;
        border-top-right-radius: 15px;
        border-top-left-radius: 15px;
        background-image: url(./img/3.jpeg);
      }
      .sliding {
        width: 640px;
        height: 40px;
        background-color: rgba(240, 240, 240, 1);
        margin-top: 10px;
        border-radius: 10px;
        position: relative;
      }
      .sliding_box {
        width: 80px;
        height: 40px;
        background-color: #fff;
        border-radius: 10px;
        text-align: center;
        line-height: 40px;
        cursor: pointer;
        position: absolute;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="img">
        <!-- 遮罩层 -->
        <div class="mask"></div>
        <!-- 方块 -->
        <div class="square">
          <i class="before"></i>
          <i class="after"></i>
        </div>
      </div>
      <div class="sliding">
        <!-- 滑块 -->
        <div class="sliding_box"></div>
      </div>
    </div>

    <script>
      let img = document.querySelector(".img");
      let mask = document.querySelector(".mask");
      let square = document.querySelector(".square");
      let sliding = document.querySelector(".sliding");
      let sliding_box = document.querySelector(".sliding_box");
      let square_after = document.querySelector(".after");
      let square_before = document.querySelector(".before");

      //遮罩层随机位置
      let ran_left = Math.floor(Math.random() * 240 + 240);
      let ran_top = Math.floor(Math.random() * 205);
      //   console.log(ran_position);
      mask.style["left"] = ran_left + "px";
      mask.style["top"] = ran_top + "px";

      //方块位置同高 方块对应背景图
      square.style["top"] = ran_top + "px";
      square.style["backgroundPosition"] = `-${ran_left}px -${ran_top}px`;

      square_before.style["backgroundPosition"] = `-${ran_left - 15}px -${
        ran_top + 30
      }px`;
      square_after.style["backgroundPosition"] = `-${ran_left + 30}px -${
        ran_top - 15
      }px`;

      //滑块移动
      let isStop = true;

      //鼠标按下时
      sliding_box.addEventListener("mousedown", () => {
        isStop = false;
      });

      //鼠标按下并移动时
      sliding_box.addEventListener("mousemove", (e) => {
        if (!isStop) {
          //获取鼠标距离盒子的距离,减去滑块的一半宽度,使鼠标居于滑块中心点
          let leftX =
            e.pageX - sliding.offsetLeft - sliding_box.offsetWidth / 2;
          //作两个判断,滑块移动到右端停止,移动到左端赋0
          if (leftX > sliding.offsetWidth - sliding_box.offsetWidth)
            leftX = sliding.offsetWidth - sliding_box.offsetWidth;
          if (leftX < 0) leftX = 0;
          //赋值给滑块
          sliding_box.style["left"] = leftX + "px";
          //方块跟随滑块移动
          square.style["left"] = leftX + "px";
        }
      });

      //鼠标抬起时
      sliding_box.addEventListener("mouseup", () => {
        isStop = true;
        //做个绝对值判断
        let left_abs = Math.abs(
          parseInt(square.style["left"]) - parseInt(mask.style["left"])
        );
        if (left_abs < 5) {
          console.log("验证成功");
        } else {
          console.log("验证失败,请重新验证");
          square.style["left"] = 25 + "px";
          sliding_box.style["left"] = 0 + "px";
        }
      });
    </script>
  </body>
</html>

三.模拟时钟

        实现模拟时钟的功能,时钟能够展示当前时间

        实现的难点不在于js,而在于布局,详情可以找找我的其他博客,有更详细的说明,想偷个懒~ OvO ~

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .box {
        width: 380px;
        height: 380px;
        border: 20px solid pink;
        border-radius: 50%;
        position: relative;
      }
      .point {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background-color: pink;
        position: absolute;
        top: 180px;
        left: 180px;
        z-index: 100;
      }
      .box ul {
        list-style: none;
      }
      .box ul li {
        height: 10px;
        width: 360px;
        /* border: 1px solid black; */
        position: absolute;
        top: 185px;
        padding: 0 10px;
        display: flex;
        justify-content: space-between;
        /* left: 180px; */
      }
      .box ul li::after,
      .box ul li::before {
        content: "";
        display: block;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background-color: black;
      }
      .box ul li:nth-child(1) {
        transform: rotate(30deg);
      }
      .box ul li:nth-child(2) {
        transform: rotate(60deg);
      }
      .box ul li:nth-child(3) {
        transform: rotate(90deg);
      }
      .box ul li:nth-child(4) {
        transform: rotate(120deg);
      }
      .box ul li:nth-child(5) {
        transform: rotate(150deg);
      }
      .hour,
      .minute,
      .second {
        width: 10px;
        position: absolute;
        transform-origin: bottom center;
      }
      .hour {
        height: 90px;
        background-color: pink;
        top: 100px;
        left: 185px;
      }
      .minute {
        height: 120px;
        background-color: skyblue;
        top: 70px;
        left: 185px;
      }
      .second {
        height: 150px;
        background-color: beige;
        top: 40px;
        left: 185px;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="point"></div>
      <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <!-- 时针 -->
      <div class="hour"></div>
      <!-- 分针 -->
      <div class="minute"></div>
      <!-- 秒针 -->
      <div class="second"></div>
    </div>
    <script>
      let hour = document.querySelector(".hour");
      let minute = document.querySelector(".minute");
      let second = document.querySelector(".second");

      setInterval(() => {
        //获取当前时分秒
        let hour_time = new Date().getHours();
        let minute_time = new Date().getMinutes();
        let second_time = new Date().getSeconds();
        // console.log(hour_time, minute_time, second_time);
        //每一小时时针旋转30°,每一分钟分针旋转6°,秒钟也是6°

        hour_time = hour_time + minute_time / 60; // 不足一小时占多少角度
        minute_time = minute_time + second_time / 60;

        hour.style["transform"] = `rotate(${hour_time * 30}deg)`;
        minute.style["transform"] = `rotate(${minute_time * 6}deg)`;
        second.style["transform"] = `rotate(${second_time * 6}deg)`;
      }, 1000);
    </script>
  </body>
</html>

四. 生成随机彩票

        根据输入的值,生成随机数彩票

        实现思路可以是生成一个随机的二维数组,通过数组的遍历进行动态生成结构。

        先声明外层数组,通过for循环生成里层,再通过第二个for循环添加元素,这样就能生成一个二维数组了。如何通过 sort( ) 方法打乱,取出前六个作为红色球的数值,最后再添加一个随机数作为蓝色球的数值,这样就可以拿到需要的数组。

function createLottery(red_max, red_min, blue_max, blue_min, sel) {
        let arr_1 = [];
        for (let j = 0; j < sel; j++) {
          let arr_2 = [];
          for (let i = 0; i < red_max; i++) {
            arr_2.push(i);
            //打乱数组
            arr_2.sort(function () {
              return Math.random() - 0.5;
            });
          }
          arr_2 = arr_2.splice(0, 6);
          arr_2.push(
            Math.floor(Math.random() * (blue_max - blue_min) + blue_min)
          );
          arr_1.push(arr_2); //数组添加数组,形成二维数组
        }
        return arr_1;
      }

        要注意的是,获取输入框的数据类型是字符串,需要进行数值类型的转换。

        最后根据二维数组的结构,进行两次循环取值渲染就OK了。 

btn.addEventListener("click", () => {
        list.innerHTML = "";
        //数据转数组类型
        let red_max_value = red_max.value - 0;
        let red_min_value = red_min.value - 0;
        let blue_max_value = blue_max.value - 0;
        let blue_min_value = blue_min.value - 0;
        let sel_value = sel.value - 0;

        if (
          !red_max.value ||
          !red_min.value ||
          !blue_max.value ||
          !blue_min.value ||
          !sel.value
        ) {
          alert("请输入完整数据");
        } else {
          console.log("生成彩票ing");
          let arr = createLottery(
            red_max_value,
            red_min_value,
            blue_max_value,
            blue_min_value,
            sel_value
          );
          for (let i = 0; i < arr.length; i++) {
            let ul = document.createElement("ul");
            for (let j = 0; j < arr[i].length; j++) {
              let li = document.createElement("li");
              li.innerHTML = `${arr[i][j]}`;
              ul.appendChild(li);
            }
            list.appendChild(ul);
          }
        }
      });

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      input {
        width: 50px;
        height: 15px;
      }
      .list ul {
        display: flex;
        padding: 0 15px;
        list-style: none;
      }

      .list ul li {
        width: 50px;
        height: 50px;
        background-color: #ccc;
        text-align: center;
        line-height: 50px;
        border-radius: 50%;
        margin: 10px 10px 0 0;
      }

      .list ul li:nth-child(-n + 6) {
        background-color: red;
        color: #fff;
      }

      .list ul li:nth-child(7) {
        background-color: deepskyblue;
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div class="layout">
      <div class="red">
        红色数字 <label for="">最大值:</label><input type="text" class="max" />
        <label for="">最小值:</label><input type="text" class="min" />
      </div>
      <div class="blue">
        蓝色数字 <label for="">最大值:</label><input type="text" class="max" />
        <label for="">最小值:</label><input type="text" class="min" />
      </div>
      <label for="">数量:</label>
      <select class="sel">
        <option value="2">2注</option>
        <option value="5">5注</option>
        <option value="10">10注</option>
      </select>
      <button class="btn">生成</button>
      <div class="list"></div>
    </div>

    <script>
      let red_max = document.querySelector(".red .max");
      let red_min = document.querySelector(".red .min");
      let blue_max = document.querySelector(".blue .max");
      let blue_min = document.querySelector(".blue .min");
      let sel = document.querySelector(".sel");
      let btn = document.querySelector(".btn");
      let list = document.querySelector(".list");

      btn.addEventListener("click", () => {
        list.innerHTML = "";
        //数据转数组类型
        let red_max_value = red_max.value - 0;
        let red_min_value = red_min.value - 0;
        let blue_max_value = blue_max.value - 0;
        let blue_min_value = blue_min.value - 0;
        let sel_value = sel.value - 0;

        if (
          !red_max.value ||
          !red_min.value ||
          !blue_max.value ||
          !blue_min.value ||
          !sel.value
        ) {
          alert("请输入完整数据");
        } else {
          console.log("生成彩票ing");
          let arr = createLottery(
            red_max_value,
            red_min_value,
            blue_max_value,
            blue_min_value,
            sel_value
          );
          for (let i = 0; i < arr.length; i++) {
            let ul = document.createElement("ul");
            for (let j = 0; j < arr[i].length; j++) {
              let li = document.createElement("li");
              li.innerHTML = `${arr[i][j]}`;
              ul.appendChild(li);
            }
            list.appendChild(ul);
          }
        }
      });

      function createLottery(red_max, red_min, blue_max, blue_min, sel) {
        let arr_1 = [];
        for (let j = 0; j < sel; j++) {
          let arr_2 = [];
          for (let i = 0; i < red_max; i++) {
            arr_2.push(i);
            //打乱数组
            arr_2.sort(function () {
              return Math.random() - 0.5;
            });
          }
          arr_2 = arr_2.splice(0, 6);
          arr_2.push(
            Math.floor(Math.random() * (blue_max - blue_min) + blue_min)
          );
          arr_1.push(arr_2); //数组添加数组,形成二维数组
        }
        return arr_1;
      }
    </script>
  </body>
</html>

五.模拟弹幕

        模拟B站弹幕效果

        要实现功能为,点击发送校验输入框内容,成功校验后生成弹幕,并随机速度向左移动,超出可视区后停止定时器,并且消除该弹幕。

        要注意的点是,每一条弹幕都是一个独立的定时器,不能出现点击发送后吧上一条弹幕覆盖的情况,所以不能设置定时器容器 timer 为全局的,而是生成新的弹幕时,容器就要容纳新的定时器。

      function getScrolling() {
        //1.生成弹幕
        let scrolling = document.createElement("p");
        scrolling.innerHTML = txt.value;
        scrolling.style["height"] = 18 + "px";

        //2.设置弹幕随机高度,统一生成在左侧
        let rTop = Math.floor(Math.random() * (400 - 18));
        console.log(rTop);
        scrolling.style["top"] = rTop + "px";
        scrolling.style["left"] = 600 + "px";

        //3.设置弹幕滚动
        let rSpeed = Math.floor(Math.random() * 15);

        let timer = null;
        timer = setInterval(() => {
          let step = 1;
          let left = parseInt(scrolling.style["left"]);
          scrolling.style["left"] = left - step + "px";
          if (left < -scrolling.offsetWidth) {
            clearInterval(timer);
            screen.removeChild(scrolling);
          }
        }, rSpeed);

        screen.appendChild(scrolling);
      }

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./reset.css" />
    <style>
      body {
        background-color: rgba(232, 232, 232, 1);
      }
      .box {
        width: 640px;
        height: 490px;
        padding: 20px;
        background-color: rgba(242, 242, 242, 1);
      }
      .screen {
        width: 600px;
        height: 400px;
        background-color: rgba(255, 255, 255, 1);
        position: relative;
        overflow: hidden;
      }
      .bottom {
        width: 600px;
        height: 50px;
        background-color: skyblue;
        display: flex;
      }
      .bottom input {
        width: 496px;
        height: 50px;
        border: 2px solid skyblue;
        padding: 0 10px;
      }
      .bottom button {
        width: 100px;
        height: 50px;
        background-color: skyblue;
        font-size: 20px;
        line-height: 50px;
        text-align: center;
      }

      /* 设置弹幕样式 */
      .screen p {
        display: block;
        font-size: 14px;
        position: absolute;
        top: 400px;
        text-align: center;
        white-space: nowrap;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="screen"></div>
      <div class="bottom">
        <input type="text" class="txt" placeholder="输入要发布的弹幕~~" />
        <button class="btn">发送</button>
      </div>
    </div>
    <script>
      let btn = document.querySelector(".btn");
      let txt = document.querySelector(".txt");
      let screen = document.querySelector(".screen");

      //点击事件监听
      btn.addEventListener("click", () => {
        if (!txt.value) {
          alert("还没有输入喔~ OvO");
          return;
        }
        getScrolling();
      });

      function getScrolling() {
        //1.生成弹幕
        let scrolling = document.createElement("p");
        scrolling.innerHTML = txt.value;
        scrolling.style["height"] = 18 + "px";

        //2.设置弹幕随机高度,统一生成在左侧
        let rTop = Math.floor(Math.random() * (400 - 18));
        console.log(rTop);
        scrolling.style["top"] = rTop + "px";
        scrolling.style["left"] = 600 + "px";

        //3.设置弹幕滚动
        let rSpeed = Math.floor(Math.random() * 15);

        let timer = null;
        timer = setInterval(() => {
          let step = 1;
          let left = parseInt(scrolling.style["left"]);
          scrolling.style["left"] = left - step + "px";
          if (left < -scrolling.offsetWidth) {
            clearInterval(timer);
            screen.removeChild(scrolling);
          }
        }, rSpeed);

        screen.appendChild(scrolling);
      }
    </script>
  </body>
</html>

六.备忘录

        实现一个备忘录,能够存入代办事件功能。

        该案例比较简单,主要难点在于删除和勾选后的数据变化,要和件数统一。

//4.给ul添加事件委托,因为li标签是动态生成的,获取其标签要用事件委托的方式
      ul.addEventListener("click", (e) => {
        //当点击到删除按钮
        if (e.target.classList.contains("delete")) {
          ul.removeChild(e.target.parentNode.parentNode); //删除该节点
          sum -= 1;
          completed ? (completed -= 1) : 0;
          uncompleted ? (uncompleted -= 1) : 0;
          fn(); //重新调用函数渲染
          sum == 0 ? (list_p.className = "") : sum;
        }
        //5.点击复选框
        if (e.target.classList.contains("checkbox")) {
          //   console.log(e.target.checked);
          //   if (e.target.checked) {
          //     completed += 1;
          //     console.log(completed);
          //   } else {
          //     completed -= 1;
          //     console.log(completed);
          //   }
          //进行数据修改
          e.target.checked ? (completed += 1) : (completed -= 1);
          !e.target.checked ? (uncompleted += 1) : (uncompleted -= 1);
          fn();
        }
      });

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        background-color: rgba(247, 247, 247, 1);
      }
      .layout {
        width: 550px;
        background-color: rgba(255, 255, 255, 1);
        display: flex;
        flex-direction: column;
        align-items: center;
        border-radius: 10px;
        margin: 50px auto;
      }
      h2 {
        height: 40px;
        margin: 20px 20px;
      }
      .list {
        width: 420px;
      }
      .list input {
        width: 350px;
        height: 40px;
        border-radius: 5px;
        padding: 10px;
        box-sizing: border-box;
      }
      .list button,
      .list-item button {
        width: 60px;
        height: 40px;
        background-color: pink;
        color: rgba(255, 255, 255, 1);
        outline-style: none;
        border: 0;
        border-radius: 5px;
      }
      .list-item button {
        width: 40px;
        height: 30px;
      }
      .result div {
        margin-top: 10px;
        display: flex;
        justify-content: space-evenly;
      }
      .list-item {
        width: 420px;
        margin-top: 10px;
        margin-bottom: 20px;
        text-align: center;
      }
      .list-item ul li {
        width: 410px;
        display: flex;
        /* justify-content: center; */
        align-items: center;
        justify-content: space-between;
        margin: 10px 0;
      }
      .list-item ul li .left {
        display: flex;
      }
      .list-item ul li p {
        margin: 0 20px 0 5px;
      }
      .list-item > p {
        font-size: 12px;
        color: gray;
        margin-top: 10px;
      }
      .isShow {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="layout">
      <h2>咸鱼茄子煲の备忘录</h2>
      <div class="list">
        <input type="text" placeholder="请输入待办" />
        <button class="add">添加</button>
        <div class="result">
          <div>
            <span>共0件</span>
            <span>已完成 [0]</span>
            <span>未完成 [0]</span>
          </div>
        </div>
      </div>
      <div class="list-item">
        <p>还没有任务哦OvO~~</p>
        <ul></ul>
      </div>
    </div>

    <script>
      //1.设置三个变量,用以数据更新
      let sum = 0;
      let completed = 0;
      let uncompleted = 0;

      //2.获取标签
      let inp = document.querySelector(".list input");
      let ul = document.querySelector(".list-item ul");
      let addBtn = document.querySelector(".add");
      let deleteBtn = document.querySelector(".delete");
      let result = document.querySelector(".result");
      let list_p = document.querySelector(".list-item p");

      //3.点击添加按钮实现添加功能
      addBtn.addEventListener("click", () => {
        let inpValue = inp.value;
        if (inpValue == "") return; //判断输入框是否为空
        let liHtml = document.createElement("li");
        liHtml.innerHTML = `
            <div class="left">
              <input type="checkbox" class='checkbox'/>
              <p>${inpValue}</p>
            </div>
            <div class="right">
              <button class="delete">删除</button>
            </div>
        `;
        ul.insertBefore(liHtml, ul.firstChild); //在顶部插入
        sum = ul.children.length; //进行数据更新
        uncompleted = ul.children.length;
        fn(); //调用渲染函数
        list_p.className = "isShow"; //使提示句消失
      });

      //4.给ul添加事件委托,因为li标签是动态生成的,获取其标签要用事件委托的方式
      ul.addEventListener("click", (e) => {
        //当点击到删除按钮
        if (e.target.classList.contains("delete")) {
          ul.removeChild(e.target.parentNode.parentNode); //删除该节点
          sum -= 1;
          completed ? (completed -= 1) : 0;
          uncompleted ? (uncompleted -= 1) : 0;
          fn(); //重新调用函数渲染
          sum == 0 ? (list_p.className = "") : sum;
        }
        //5.点击复选框
        if (e.target.classList.contains("checkbox")) {
          //   console.log(e.target.checked);
          //   if (e.target.checked) {
          //     completed += 1;
          //     console.log(completed);
          //   } else {
          //     completed -= 1;
          //     console.log(completed);
          //   }
          //进行数据修改
          e.target.checked ? (completed += 1) : (completed -= 1);
          !e.target.checked ? (uncompleted += 1) : (uncompleted -= 1);
          fn();
        }
      });
      //6.封装渲染函数
      function fn(params) {
        // console.log(sum, completed, uncompleted);
        result.innerHTML = `
            <div>
            <span>共${sum}件</span>
            <span>已完成 [${completed}]</span>
            <span>未完成 [${uncompleted}]</span>
          </div>
        `;
        inp.value = "";
      }
    </script>
  </body>
</html>

七.模拟天气查询

        和上个案例类似,都是点击获取输入框的value值,与data数据进行匹配,随后将匹配到的数据在页面进行渲染。

        代码采用的数组映射map方法,可以看做为增强的for循环,item为数组里的每一个元素,data是数组包对象结构,所以使用两次map方法进行遍历。因为data有四条数据,使用map会触发四次里面的回调函数,此时就需要进行判断结束遍历。

        map返回的是经过处理后的数组,通过join方法转成字符串形式,就可以被标签直接使用。

function getData(city) {
        let liHtml;
        //map类似for循环,会自动遍历,有多少数据就遍历多少次
        data.map((item) => {
          //如果没有找到输入城市,直接结束当前遍历
          if (city !== item.city) return;
          //获取到所有的结构
          liHtml = item.result
            .map((item) => {
              return `<li>
              <div class="image"><img src="./assets/${item.img}.png" alt="" /></div>
              <div class="data">
              <div class="date">日期:${item.date}</div>
              <div class="day_tem">白天天气:${item.day_tem}°C</div>
              <div class="night_tem">夜间天气:${item.night_tem}°C</div>
              <div class="week">${item.week}</div>
              <div class="wea">${item.wea}</div>
            </div>
            </li>`;
            })
            .join(""); //转字符串
        });
        return liHtml;
      }

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        background-color: rgba(247, 247, 247, 1);
      }
      .layout {
        width: 550px;
        background-color: rgba(255, 255, 255, 1);
        display: flex;
        flex-direction: column;
        align-items: center;
        border-radius: 10px;
        margin: 50px auto;
      }
      h2 {
        height: 40px;
        margin: 20px 20px;
      }
      .list {
        width: 420px;
      }
      .list input {
        width: 350px;
        height: 40px;
        border-radius: 5px;
        padding: 10px;
        box-sizing: border-box;
      }
      .list button,
      .list-item button {
        width: 60px;
        height: 40px;
        background-color: pink;
        color: rgba(255, 255, 255, 1);
        outline-style: none;
        border: 0;
        border-radius: 5px;
      }
      .list-item button {
        width: 40px;
        height: 30px;
      }
      .result div {
        margin-top: 10px;
        display: flex;
        justify-content: space-around;
      }
      .list-item {
        width: 420px;
        margin-top: 10px;
        margin-bottom: 20px;
        text-align: center;
      }
      .list-item ul li {
        width: 410px;
        height: 120px;
        display: flex;
        /* justify-content: center; */
        align-items: center;
        justify-content: space-around;
        margin: 10px 0;
      }
      .list-item ul li .left {
        display: flex;
      }
      .list-item ul li p {
        margin: 0 20px 0 5px;
      }
      .list-item ul li img {
        width: 100px;
      }
      .list-item > p {
        font-size: 12px;
        color: gray;
        margin-top: 10px;
      }
      .isShow {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="layout">
      <h2>咸鱼茄子煲の天气预报</h2>
      <div class="list">
        <input type="text" placeholder="请输入要查询的城市" />
        <button class="btn">查询</button>
      </div>
      <div class="list-item">
        <p>还没有天气哦OvO~~</p>
        <ul></ul>
      </div>
    </div>
    <!-- <img src="./assets/cloudy.png" alt="" /> -->

    <script src="./weather.js"></script>
    <script>
      let btn = document.querySelector(".btn");
      let inp = document.querySelector(".list input");
      let ul = document.querySelector(".list-item ul");
      let p = document.querySelector(".list-item p");

      btn.addEventListener("click", () => {
        if (!inp.value) {
          alert("还没有输入喔~~");
          return;
        }
        // console.log(inp.value);
        let city = inp.value;
        // console.log(getData(city));
        let liHtml = getData(city);
        if (!liHtml) {
          p.style["display"] = "block";
          ul.innerHTML = "";
          return;
        }
        p.style["display"] = "none";
        ul.innerHTML = liHtml;
      });

      function getData(city) {
        let liHtml;
        //map类似for循环,会自动遍历,有多少数据就遍历多少次
        data.map((item) => {
          //如果没有找到输入城市,直接结束当前遍历
          if (city !== item.city) return;
          //获取到所有的结构
          liHtml = item.result
            .map((item) => {
              return `<li>
              <div class="image"><img src="./assets/${item.img}.png" alt="" /></div>
              <div class="data">
              <div class="date">日期:${item.date}</div>
              <div class="day_tem">白天天气:${item.day_tem}°C</div>
              <div class="night_tem">夜间天气:${item.night_tem}°C</div>
              <div class="week">${item.week}</div>
              <div class="wea">${item.wea}</div>
            </div>
            </li>`;
            })
            .join(""); //转字符串
        });
        return liHtml;
      }
    </script>
  </body>
</html>

八.模拟放大镜功能

        左侧为一倍图,右侧为二倍图或其他比例大图,鼠标移入后,左侧遮罩层跟随鼠标移动,右侧大图也做出偏移,模拟放大效果。        

        要做到理想效果,最重要的是弄清楚里面各种各样的距离关系。

        首先是要得知鼠标在小图中的位置,以便引导遮罩层移动,e.pageX 是鼠标距离可视区的横向距离,减去盒子距离可视区的横向距离small.offsetLeft,就可以得到鼠标位置,但此时遮罩层是左上角跟随移动,我们需要的是遮罩层中心跟随移动。

                let x = e.pageX - small.offsetLeft;
                let y = e.pageY - small.offsetTop;

        再减去遮罩层的一半宽高,就可以让遮罩层中心跟随移动了。

                let leftX = x - mask.offsetWidth / 2;
                let topY = y - mask.offsetHeight / 2;

small.addEventListener("mousemove", (e) => {
        //获取鼠标在盒子内的坐标
        // console.log(e.pageX - small.offsetLeft, e.pageY - small.offsetTop);
        let x = e.pageX - small.offsetLeft;
        let y = e.pageY - small.offsetTop;

        let leftX = x - mask.offsetWidth / 2;
        let topY = y - mask.offsetHeight / 2;
        // console.log(leftX, topY);
      });

         接着就需要做出限制,使遮罩层只能在盒子内部进行偏移。

//判断超出位置的限制
        if (leftX <= 0) {
          leftX = 0;
        } else if (leftX >= small.offsetWidth - mask.offsetWidth) {
          leftX = small.offsetWidth - mask.offsetWidth;
        }
        if (topY <= 0) {
          topY = 0;
        } else if (topY >= small.offsetHeight - mask.offsetHeight) {
          topY = small.offsetHeight - mask.offsetHeight;
        }
        mask.style["left"] = leftX + "px";
        mask.style["top"] = topY + "px";

         最后就需要计算大图的偏移距离,这里采用的方案是,计算遮罩层移动距离的比例,大图也做相应的偏移比例。也就是说,遮罩层动百分之几,大图也移动百分之几。

//大图偏移

        //遮罩层偏移距离/遮罩层最大偏移距离 * 大盒子最大偏移距离
        //比如说,遮罩层移动到一半,就是 0.5*(800-540) = 130
        //遮罩层移动到顶,就是 1*(800-560) =260

        let big_leftX = big_img.offsetWidth - big.offsetWidth;
        let big_topY = big_img.offsetHeight - big.offsetHeight;

        let big_img_x =
          (leftX / (small.offsetWidth - mask.offsetWidth)) * big_leftX;
        let big_img_y =
          (topY / (small.offsetHeight - mask.offsetHeight)) * big_topY;

        big_img.style["left"] = -big_img_x + "px";
        big_img.style["top"] = -big_img_y + "px";

        console.log(-big_img_x, -big_img_y);

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .banner {
        display: flex;
        width: 1000px;
        margin: 0 auto;
      }
      .small {
        width: 350px;
        height: 350px;
        background: url(./images/small.jpg) no-repeat;
        position: relative;
      }
      .mask {
        width: 240px;
        height: 240px;
        background-color: rgba(200, 196, 196, 0.6);
        position: absolute;
        top: 0;
        left: 0;
      }
      .big {
        width: 540px;
        height: 540px;
        /* background: url(./images/big.jpg); */
        margin-left: 20px;
        overflow: hidden;
        position: relative;
      }
      .big img {
        position: absolute;
        top: 0;
        left: 0;
      }
      .active {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="banner">
      <div class="small">
        <div class="mask"></div>
      </div>
      <div class="big">
        <img src="./images/big.jpg" alt="" />
      </div>
    </div>
    <script>
      let small = document.querySelector(".small");
      let mask = document.querySelector(".mask");
      let big = document.querySelector(".big");
      let big_img = document.querySelector(".big img");

      small.addEventListener("mousemove", (e) => {
        //获取鼠标在盒子内的坐标
        // console.log(e.pageX - small.offsetLeft, e.pageY - small.offsetTop);
        let x = e.pageX - small.offsetLeft;
        let y = e.pageY - small.offsetTop;

        let leftX = x - mask.offsetWidth / 2;
        let topY = y - mask.offsetHeight / 2;
        // console.log(leftX, topY);

        //判断超出位置的限制
        if (leftX <= 0) {
          leftX = 0;
        } else if (leftX >= small.offsetWidth - mask.offsetWidth) {
          leftX = small.offsetWidth - mask.offsetWidth;
        }
        if (topY <= 0) {
          topY = 0;
        } else if (topY >= small.offsetHeight - mask.offsetHeight) {
          topY = small.offsetHeight - mask.offsetHeight;
        }
        mask.style["left"] = leftX + "px";
        mask.style["top"] = topY + "px";

        //大图偏移
        // console.log(big_img.offsetWidth / small.offsetWidth);
        //大图/小图 * 小图偏移距离

        //遮罩层偏移距离/遮罩层最大偏移距离 * 大盒子最大偏移距离
        //比如说,遮罩层移动到一半,就是 0.5*(800-540) = 130
        //遮罩层移动到顶,就是 1*(800-560) =260

        let big_leftX = big_img.offsetWidth - big.offsetWidth;
        let big_topY = big_img.offsetHeight - big.offsetHeight;

        let big_img_x =
          (leftX / (small.offsetWidth - mask.offsetWidth)) * big_leftX;
        let big_img_y =
          (topY / (small.offsetHeight - mask.offsetHeight)) * big_topY;

        big_img.style["left"] = -big_img_x + "px";
        big_img.style["top"] = -big_img_y + "px";

        console.log(-big_img_x, -big_img_y);
      });
    </script>
  </body>
</html>

总结时间        

        八个案例写下来,不说很有难度吧,确实也不简单,细节问题很多,尤其是对于定时器方面的操作,还是很考验细心程度的。

        最后的感受是,DOM操作挺好玩的 QAQ 。。。

        再接再厉!

        学前端开发,当赛博黑奴。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值