2023年十四届省赛大学组真题(共10道题)

目录

第一题 电影院排座位(5分)

第二题 图片水印生成(5分)

第三题 收集帛书碎片(10分)

第四题 自适应页面(10分)

第五题 全球新冠疫情数据统计(15分)

第六题 年度明星项目(15分)

第七题 视频弹幕(20分)

第八题 外卖给好评(20分)

第九题 Markdown文档解析(25分)

第十题 组课神器(25分)


第一题 电影院排座位(5分)

* {
  box-sizing: border-box;
}

body {
  background-color: #242333;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  margin: 0;
}

.container {
  perspective: 1000px;
  width: 470px;
}

.screen {
  background-color: #fff;
  height: 70px;
  width: 100%;
  transform: rotateX(-45deg);
  box-shadow: 0 3px 10px rgba(255, 255, 255, 0.7);
  color: #242333;
  text-align: center;
  line-height: 70px;
  font-size: 30px;
}

.seat {
  background-color: #444451;
  height: 40px;
  width: 45px;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
}

/* TODO:待补充代码 */
.seat-area {
  display: grid;
  margin-top: 50px;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
}

.seat-area .seat:nth-child(8n + 3) {
  /* 
:nth-child(8n + 3):这是一个结构伪类选择器,用于根据元素在其父元素中的位置来选择元素。具体规则如下:n 是一个计数器,它的取值从 0 开始,依次为 0、1、2、3……
当 n = 0 时,8n + 3 的值为 3,这意味着会选择父元素下的第 3 个子元素,前提是这个子元素的 class 属性包含 seat。
当 n = 1 时,8n + 3 的值为 11,会选择父元素下的第 11 个子元素,同样要求该元素的 class 属性包含 seat。
以此类推,n 每增加 1,就会选择间隔为 8 的子元素(因为系数是 8),并且这些元素的 class 属性都要包含 seat。 
*/
  margin-left: 20px;
}
.seat-area .seat:nth-child(8n + 7) {
  margin-left: 20px;
}


第二题 图片水印生成(5分)

/**
 * 创建一个文字水印的div
 * @param  {string} text - 水印文字
 * @param  {string} color - 水印颜色
 * @param  {number} deg - 水印旋转角度
 * @param  {number} opacity - 水印透明度
 * @param  {number} count - 水印数量
 */
function createWatermark(text, color, deg, opacity, count) {
  // 创建水印容器
  const container = document.createElement("div");
  container.className = "watermark";
  console.log(text, color, deg, opacity, count);
  // TODO: 根据输入参数创建文字水印
 
  let template = `<span style="color:${color};transform: rotate(${deg}deg); opacity:${opacity}">${text}</span>`;
  for (let i = 0; i < count; i++) {
    container.innerHTML += template;
  }
  return container;
}
 
// 以下代码不需要修改
// 调用createWatermark方法,创建图片水印
const watermark = createWatermark("WaterMark", "white", 45, 0.5, 11);
// 将水印挂载到图片容器上
const container = document.querySelector(".container");
container.appendChild(watermark);
 
// 提供图片保存功能
const button = document.querySelector("button");
button.addEventListener("click", () => {
  domtoimage.toJpeg(document.querySelector(".container")).then((dataUrl) => {
    const link = document.createElement("a");
    link.download = "image.jpeg";
    link.href = dataUrl;
    link.click();
  });
});




//方法二
/**
 * 创建一个文字水印的div
 * @param  {string} text - 水印文字
 * @param  {string} color - 水印颜色
 * @param  {number} deg - 水印旋转角度
 * @param  {number} opacity - 水印透明度
 * @param  {number} count - 水印数量
 */
function createWatermark(text, color, deg, opacity, count) {
  // 创建水印容器
  const container = document.createElement("div");
  container.className = "watermark";

  // TODO: 根据输入参数创建文字水印

  for (var i = 0; i < count; i++) {
    let SPAN = document.createElement("span");
    SPAN.innerText = text;
    SPAN.style.color = color;
    SPAN.style.transform = `rotate(${deg}deg)`; //rotate 是字符串需要``包裹起来,不能rotate(`${deg}deg`)
    SPAN.style.opacity = opacity;
    console.log(SPAN.style);
    container.appendChild(SPAN);
  }
  return container;
}


第三题 收集帛书碎片(10分)

function collectPuzzle(...puzzles) {
  // TODO:在这里写入具体的实现逻辑
  // 对所有的拼图进行收集,获取不同拼图类型的结果,并返回
  let result = puzzles.flat(Infinity);
  return [...new Set(result)];
}
 
// 检测需要,请勿删除
module.exports = collectPuzzle;


第四题 自适应页面(10分)

@media (max-width: 800px) {
  #tutorials .row {
    grid-template-columns: 1fr;
  }
  #tutorials img {
    margin: 0;
  }
  #tutorials .text .box {
    margin-bottom: 15px;
    margin-top: 20px;
  }
  label.icon-menu {
    display: block;
    color: white;
    padding: 0 20px;
 
    line-height: 54px;
  }
  .menu {
    height: 54px;
    margin-bottom: 25px;
  }
  .menu li {
    display: flex;
    flex-direction: column;
    background-color: #252525;
  }
  .collapse {
    display: none;
  }
  .menu:hover .collapse {
    display: flex;
    flex-direction: column;
  }
  .dropdown:hover ul {
    display: contents;
  }
  .dropdown:hover ul li {
    background-color: white;
  }
}


第五题 全球新冠疫情数据统计(15分)

Vue2 不考


第六题 年度明星项目(15分)

// 保存翻译文件数据的变量
let translation = {};
// 记录当前语言
let currLang = "zh-cn";

//all-data.json 文件中以数组的形式存储了明星项目的数据
let data = [];
let page = 0;
// TODO: 请在此补充代码实现项目数据文件和翻译数据文件的请求功能
window.onload = async () => {
  let res = await fetch("./js/all-data.json");
  data = await res.json();

  let res2 = await fetch("./js/translation.json");
  translation = await res2.json();

  //目标二
  data.slice(0, 15).map((v) => {
    //map映射数据到新的数组
    let objItem = {
      icon: v.icon,
      description: v.descriptionCN,
      name: v.name,
      stars: v.stars,
      tags: v.tags,
    };
    return $(".list > ul").append(createProjectItem(objItem));
  });
};
// TODO-END

// TODO: 请修改以下代码实现项目数据展示的功能
let loadMore = document.querySelector(".load-more");
loadMore.addEventListener("click", () => {
  page += 1; //页码,默认0
  data.slice(15 * page, 15 * (page + 1)).map((v) => {
    //map映射数据到新的数组
    let objItem = {
      icon: v.icon,
      description: currLang == "zh-cn" ? v.descriptionCN : v.descriptionEN,
      name: v.name,
      stars: v.stars,
      tags: v.tags,
    };
    console.log(page); //循环3次
    return $(".list > ul").append(createProjectItem(objItem));
  });
  if (page === 3) loadMore.style.display = "none";
});

// 以下代码(13-23行)为 createProjectItem 函数使用示例
// Mock一个项目的数据
// const item = {
//   icon: "bun.svg",
//   description:
//     "Incredibly fast JavaScript runtime, bundler, transpiler and package manager...",
//   name: "Bun",
//   stars: 37087,
//   tags: ["runtime", "bun"],
// };
// // 添加至页面的项目列表中,查看页面可以看到有一行 bun 的项目数据
// $(".list > ul").append(createProjectItem(item));

// TODO-END

// 用户点击切换语言的回调
$(".lang").click(() => {
  // 切换页面文字的中英文
  if (currLang === "en") {
    $(".lang").text("English");
    currLang = "zh-cn";
  } else {
    $(".lang").text("中文");
    currLang = "en";
  }
  $("body")
    .find("*")
    .each(function () {
      const text = $(this).text().trim();
      if (translation[text]) {
        $(this).text(translation[text]);
      }
    });
  // TODO: 请在此补充代码实现项目描述的语言切换
  let pList = document.querySelectorAll("ul li p");
  const result = (pList, currLang) => {
    pList.forEach((el, idx) => {
      el.innerHTML =
        currLang == "en" ? data[idx].descriptionEN : data[idx].descriptionCN;
    });
  };
  if (currLang == "en") {
    result(pList, currLang);
  } else {
    result(pList, currLang);
  }
});

// 生成列表DOM元素的函数,将该元素的返回值append至列表中即可生成一行项目数据
/**
 * @param  {string} name - 项目名称
 * @param  {string} description - 项目描述
 * @param  {string[]} tags - 项目标签
 * @param  {number} stars - 项目star数量
 * @param  {string} icon - 项目icon路径
 */
function createProjectItem({ name, description, tags, stars, icon }) {
  return `
    <li class="item">
      <img src="images/${icon}" alt="">
      <div class="desc">
        <h3>${name}</h3>
        <p>${description}</p>
        <ul class="labels">
          ${tags.map((tag) => `<li>${tag}</li>`).join("")}
        </ul>
      </div>
      <div class="stars">
        +${stars} 🌟
      </div>
    </li>
  `;
}


第七题 视频弹幕(20分)

const bullets = [
  "前方高能",
  "原来如此",
  "这么简单",
  "学到了",
  "学费了",
  "666666",
  "111111",
  "workerman",
  "学习了",
  "别走,奋斗到天明",
];
 
/**
 * @description 根据 bulletConfig 配置在 videoEle 元素最右边生成弹幕,并移动到最左边,弹幕最后消失
 * @param {Object} bulletConfig 弹幕配置
 * @param {Element} videoEle 视频元素
 * @param {boolean} isCreate 是否为新增发送的弹幕,为 true 表示为新增的弹幕
 *
 */
function renderBullet(bulletConfig, videoEle, isCreate = false) {
  const spanEle = document.createElement("SPAN");
  spanEle.classList.add(`bullet${index}`);
  if (isCreate) {
    spanEle.classList.add("create-bullet");
  }
  // TODO:控制弹幕的显示颜色和移动,每隔 bulletConfig.time 时间,弹幕移动的距离  bulletConfig.speed
  let left = getEleStyle(videoEle).width;
  let top = getRandomNum(getEleStyle(videoEle).height);
  spanEle.innerHTML = bulletConfig.value;
  spanEle.style.left = left + "px";
  spanEle.style.top = top + "px";
 
  spanEle.style.color = `rgb(${getRandomNum(255)},${getRandomNum(
    255
  )},${getRandomNum(255)})`;
 
  videoEle.appendChild(spanEle);
 
  setInterval(() => {
    // 向左移动距离为 bulletConfig.speed(弹幕配置对象)。
    left -= bulletConfig.speed;
    spanEle.style.left = left + "px";
 
    if (getEleStyle(spanEle).right <= getEleStyle(videoEle).left) {
      videoEle.removeChild(spanEle);
      clearInterval(timer);
    }
  }, bulletConfig.time);
}
 
document.querySelector("#sendBulletBtn").addEventListener("click", () => {
  // TODO:点击发送按钮,输入框中的文字出现在弹幕中
  let intVal = document.querySelector("#bulletContent").value;
  bulletConfig.value = intVal;
  document.querySelector("#bulletContent").value = "";
  renderBullet(bulletConfig, videoEle, (isCreate = true));
});
 
function getEleStyle(ele) {
  // 获得元素的width,height,left,right,top,bottom
  return ele.getBoundingClientRect();
}
 
function getRandomNum(end, start = 0) {
  // 获得随机数,范围是 从start到 end
  return Math.floor(start + Math.random() * (end - start + 1));
}
 
// 设置 index 是为了弹幕数组循环滚动
let index = 0;
const videoEle = document.querySelector("#video");
// 弹幕配置
const bulletConfig = {
  isHide: false, // 是否隐藏
  speed: 5, // 弹幕的移动距离
  time: 50, // 弹幕每隔多少ms移动一次
  value: "", // 弹幕的内容
};
let isPlay = false;
let timer; // 保存定时器
document.querySelector("#vd").addEventListener("play", () => {
  // 监听视频播放事件,当视频播放时,每隔 1000s 加载一条弹幕
  isPlay = true;
  bulletConfig.value = bullets[index++];
  renderBullet(bulletConfig, videoEle);
  timer = setInterval(() => {
    bulletConfig.value = bullets[index++];
    renderBullet(bulletConfig, videoEle);
    if (index >= bullets.length) {
      index = 0;
    }
  }, 1000);
});
 
document.querySelector("#vd").addEventListener("pause", () => {
  isPlay = false;
  clearInterval(timer);
});
 
document.querySelector("#switchButton").addEventListener("change", (e) => {
  if (e.target.checked) {
    bulletConfig.isHide = false;
  } else {
    bulletConfig.isHide = true;
  }
});


第八题 ISBN 转换与生成(20分)

// 将用户输入的带分隔符的 isbn 字符串转换只有纯数字和大写 X 字母的字符串
// 入参 str 为转换为包含任意字符的字符串
/**
 *
 * @param {String} str
 * @returns
 */
function getNumbers(str) {
  // TODO: 待补充代码
  //只有纯数字和大写 X 字母的字符串。
  // 除了 \d|X 之外的都替换成空字符串
  return str.replace(/[^\d|X]/g, "");
}

// 验证当前 ISBN10 字符串是否有效
// 入参 str 为待判断的只有纯数字和大写 X 字母的字符串
/**
 *
 * @param {String} str
 */
function validISBN10(str) {
  // TODO: 待补充代码
  //格式要求
  if (!/^\d{9}[\d|X]$/.test(str)) return false;
  //校验位计算
  let newArr = str.slice(0, 9).split("");
  let sum = 0,
    checkNum = 0;
  check = 0;
  newArr.forEach((el, idx) => {
    sum += el * (idx + 1);
  });
  checkNum = sum % 11;
  check = checkNum == 10 ? "X" : checkNum;
  //这段代码首先检查输入的字符串是否符合 ISBN - 10 的基本格式要求,然后计算前 9 位数字的加权和,最后根据加权和的余数与最后一位校验位进行比较,判断输入的 ISBN - 10 是否有效。
  return check == str[str.length - 1];
}

// 将用户输入的 ISBN-10 字符串转化为 ISBN-13 字符串
// 入参 isbn 为有效的 ISBN-10 字符串
/**
 *
 * @param {String} isbn
 */
function ISBN10To13(isbn) {
  // TODO: 待补充代码
  let isbn13 = "978" + isbn.slice(0, 9);
  let odd = 0,
    even = 0;
  check = 0;
  //计算最后一位校验位,前12位中的奇数位(是奇数的下标)
  isbn13
    .split("")
    .map(Number) // 回调自己
    .forEach((el, idx) => {
      if ((idx + 1) % 2 == 0) {
        even += 3 * el;
      } else {
        odd += 1 * el;
      }
    });
  check = 10 - ((even + odd) % 10);
  return isbn13 + String(check);
}

// 测试用例
console.log(getNumbers("7-5600-3879-4")); // 7560038794
console.log(getNumbers("7 5600 3879 4")); // 7560038794

console.log(validISBN10("7560038794")); // true
console.log(validISBN10("7560038793")); // false
console.log(validISBN10("756003879")); // false
console.log(validISBN10("756003879004")); // false

console.log(ISBN10To13("7560038794")); // 9787560038797
console.log(ISBN10To13("3598215088")); // 9783598215087


第九题 Markdown文档解析(25分)

000000


第十题 组课神器(25分)

/**
 * @description 模拟 ajax 请求,拿到树型组件的数据 treeData
 * @param {string} url 请求地址
 * @param {string} method 请求方式,必填,默认为 get
 * @param {string} data 请求体数据,可选参数
 * @return {Array}
 * */
async function ajax({ url, method = "get", data }) {
  let result;
  // TODO:根据请求方式 method 不同,拿到树型组件的数据
  // 当method === "get" 时,localStorage 存在数据从 localStorage 中获取,不存在则从 /js/data.json 中获取
  // 当method === "post" 时,将数据保存到localStorage 中,key 命名为 data
  if (method == "get") {
    let dataList = localStorage.getItem("data");
    if (dataList) {
      result = JSON.parse(dataList);
    } else {
      await axios.get(url).then((res) => {
        result = res.data.data;
      });
    }
  } else if (method == "post") {
    let newData = JSON.stringify(data);
    localStorage.setItem("data", newData);
  }
  return result;
}
 
/**
 * @description 找到元素节点的父亲元素中类选择器中含有 tree-node 的元素节点
 * @param {Element} node 传入的元素节点
 * @return {Element} 得到的元素节点
 */
const getTreeNode = (node) => {
  let curElement = node;
  while (!curElement.classList.contains("tree-node")) {
    if (curElement.classList.contains("tree")) {
      break;
    }
    curElement = curElement.parentNode;
  }
  return curElement;
};
 
/**
 * @description 根据 dragElementId, dropElementId 重新生成拖拽完成后的树型组件的数据 treeData
 * @param {number} dragGrade 被拖拽的元素的等级,值为 dragElement data-grade属性对应的值
 * @param {number} dragElementId 被拖拽的元素的id,值为当前数据对应在 treeData 中的id
 * @param {number} dropGrade 放入的目标元素的等级,值为 dropElement data-grade属性对应的值
 * @param {number} dropElementId 放入的目标元素的id,值为当前数据对应在 treeData 中的id
 */
function treeDataRefresh(
  { dragGrade, dragElementId },
  { dropGrade, dropElementId }
) {
  // TODO:根据 `dragElementId, dropElementId` 重新生成拖拽完成后的树型组件的数据 `treeData`
  let dragStr = JSON.stringify(getDragElement(treeData, dragElementId));
  let dropStr = JSON.stringify(getDragElement(treeData, dropElementId));
  let treeDataStr = JSON.stringify(treeData);
  if (dragGrade === dropGrade) {
    treeDataStr = treeDataStr.replace(dragStr, "");
    treeDataStr = treeDataStr.replace(dropStr, dropStr + "," + dragStr);
  }
  if (dragGrade - dropGrade == 1) {
    if (dropStr.includes(dragStr)) dropStr = dropStr.replace(dragStr, "");
    const newDragStr = `${dragStr},`;
    const newDropStr = dropStr.replace("[", "[" + newDragStr);
    treeDataStr = treeDataStr.replace(dragStr, "");
    treeDataStr = treeDataStr.replace(dropStr, newDropStr);
  }
  // 处理多余字符
  treeDataStr = treeDataStr
    .replace(",,", ",")
    .replace("[,", "[")
    .replace(",]", "]");
  treeData = JSON.parse(treeDataStr);
}
function getDragElement(data, id) {
  for (const obj of flatObj(data)) {
    if (obj.id == id) return obj;
  }
}
function flatObj(data) {
  return data.reduce((prev, cur) => {
    prev = [...prev, cur];
    if (cur?.children) prev = [...prev, ...flatObj(cur.children)];
    return prev;
  }, []);
}
/**
 * @description 根据 treeData 的数据生成树型组件的模板字符串,在包含 .tree-node 的元素节点需要加上 data-grade=${index}表示菜单的层级 data-index="${id}" 表示菜单的唯一id
 * @param {array} data treeData 数据
 * @param {number} grade 菜单的层级
 * @return 树型组件的模板字符串
 *
 * */
function treeMenusRender(data, grade = 0) {
  let treeTemplate = "";
  // TODO:根据传入的 treeData 的数据生成树型组件的模板字符串
  grade++;
  for (obj of data) {
    treeTemplate +=
      grade === 3
        ? `<div class="tree-node" data-index="${obj.id}" data-grade="${grade}">
              <div class="tree-node-content" style="margin-left: 30px">
                <div class="tree-node-content-left">
                  <img src="./images/dragger.svg" alt="" class="point-svg" />
                  <span class="tree-node-tag">${obj.tag}</span>
                  <span class="tree-node-label">${obj.label}</span>
                </div>
                <div class="tree-node-content-right">
                  <div class="students-count">
                    <span class="number"> 0人完成</span>
                    <span class="line">|</span>
                    <span class="number">0人提交报告</span>
                  </div>
                  <div class="config">
                    <img class="config-svg" src="./images/config.svg" alt="" />
                    <button class="doc-link">编辑文档</button>
                  </div>
                </div>
              </div>`
        : `<div class="tree-node" data-index="${obj.id}" data-grade="${grade}">
              <div class="tree-node-content" style="margin-left: ${
                grade === 2 && 15
              }px">
                <div class="tree-node-content-left">
                  <img src="./images/dragger.svg" alt="" class="point-svg" />
                  <span class="tree-node-label">${obj.label}</span>
                  <img class="config-svg" src="./images/config.svg" alt="" />
                </div>
              </div>`;
 
    if (obj?.children)
      treeTemplate += `<div class="tree-node-children">${treeMenusRender(
        obj.children,
        grade
      )}</div>`;
    treeTemplate += `</div>`;
  }
  return treeTemplate;
}
 
let treeData; // 树型组件的数据 treeData
 
// 拖拽到目标元素放下后执行的函数
const dropHandler = (dragElement, dropElement) => {
  let dragElementId = dragElement.dataset.index;
  let dragGrade = dragElement.dataset.grade;
  if (dropElement) {
    let dropElementId = dropElement.dataset.index;
    let dropGrade = dropElement.dataset.grade;
 
    treeDataRefresh({ dragGrade, dragElementId }, { dropGrade, dropElementId });
    document.querySelector(".tree").innerHTML = treeMenusRender(treeData);
    document.querySelector("#test").innerText = treeData
      ? JSON.stringify(treeData)
      : "";
    ajax({ url: "./js/data.json", method: "post", data: treeData });
  }
};
// 初始化
ajax({ url: "./js/data.json" }).then((res) => {
  treeData = res;
  document.querySelector("#test").innerText = treeData
    ? JSON.stringify(treeData)
    : "";
  let treeEle = document.querySelector(".tree");
  treeEle.dataset.grade = 0;
  let treeTemplate = treeMenusRender(treeData);
  treeTemplate && (treeEle.innerHTML = treeTemplate);
  const mDrag = new MDrag(".tree-node", dropHandler);
  // 事件委托,按下小图标记录得到被拖拽的元素,该元素 class 包含 .tree-node
  document.querySelector(".tree").addEventListener("mousedown", (e) => {
    e.preventDefault();
    if (
      e.target.nodeName.toLowerCase() === "img" &&
      e.target.classList.contains("point-svg")
    ) {
      let dragElement = getTreeNode(e.target);
      // MDrag类的drag方法实现拖拽效果
      mDrag.drag(e, dragElement);
    }
  });
});
 
/**
 * @description 实现拖拽功能的类,该类的功能为模拟 HTML5 drag 的功能
 *              鼠标按下后,监听 document 的 mousemove 和 mouseup 事件
 *              当开始拖拽一个元素后会在 body 内插入对应的克隆元素,并随着鼠标的移动而移动
 *              鼠标抬起后,移除克隆元素和 mousemove 事件,如果到达目标触发传入的 dropHandler 方法
 */
class MDrag {
  constructor(dropElementSelector, dropHandler) {
    // 目标元素的选择器
    this.dropElementSelector = dropElementSelector;
    // 拖拽到目标元素放下后执行的函数
    this.dropHandler = dropHandler;
 
    // 保存所有的目标元素
    this.dropBoundingClientRectArr = [];
    // 被拖拽的元素
    this._dragElement = null;
    // 拖拽中移动的元素
    this._dragElementClone = null;
    // 目标元素
    this._dropElement = null;
    // 拖拽移动事件
    this._dragMoveBind = null;
    // 拖拽鼠标抬起事件
    this._dragUpBind = null;
 
    this.init();
  }
  init() {
    const dropElements = document.querySelectorAll(this.dropElementSelector);
    this.dropBoundingClientRectArr = Array.from(dropElements).map((el) => {
      return { boundingClientRect: el.getBoundingClientRect(), el };
    });
  }
  dragMove(e) {
    const { pageX, pageY } = e;
    this._dragElementClone.style.left = `${e.pageX}px`;
    this._dragElementClone.style.top = `${e.pageY}px`;
    this.setMouseOverElementStyle(pageX, pageY);
  }
  dragend(e) {
    // 移动到目标元素后mouseup事件触发,删除 this._dragElementClone 元素和解除mousemove/mouseup事件
    const { pageX, pageY } = e;
    document.removeEventListener("mousemove", this._dragMoveBind);
    document.removeEventListener("mouseup", this._dragUpBind);
    if (
      Array.from(document.body.children).indexOf(this._dragElementClone) != -1
    ) {
      document.body.removeChild(this._dragElementClone);
    }
    this._dropElement = this.getActualDropElement(pageX, pageY);
    this.drop();
  }
  drag(e, dragElement) {
    this._dragElement = dragElement;
    this._dragElementClone = dragElement.cloneNode(true);
    this._dragElementClone.style.position = "absolute";
    this._dragElementClone.style.left = `${e.pageX - 20}px`;
    this._dragElementClone.style.top = `${e.pageY - 20}px`;
    this._dragElementClone.style.opacity = 0.5;
    this._dragElementClone.style.width = "800px";
    document.body.appendChild(this._dragElementClone);
    // 绑定mousemove和mouseup事件
    this._dragMoveBind = this.dragMove.bind(this);
    this._dragUpBind = this.dragend.bind(this);
    document.addEventListener("mousemove", this._dragMoveBind);
    document.addEventListener("mouseup", this._dragUpBind);
    return this;
  }
  getActualDropElement(pageX, pageY) {
    const dropAttributeArr = this.dropBoundingClientRectArr.filter(
      (obj) =>
        pageY >= obj.boundingClientRect.top &&
        pageY <= obj.boundingClientRect.top + obj.boundingClientRect.height
    );
    if (dropAttributeArr.length == 1) {
      return dropAttributeArr[0].el;
    } else if (dropAttributeArr.length > 1) {
      let temp = dropAttributeArr.reduce((prev, next) => {
        if (
          Math.abs(pageY - prev.boundingClientRect.top) <=
          Math.abs(pageY - next.boundingClientRect.top)
        ) {
          return prev;
        } else {
          return next;
        }
      });
      return temp.el;
    } else {
      return null;
    }
  }
  setMouseOverElementStyle(pageX, pageY) {
    let mousemoveEle = this.getActualDropElement(pageX, pageY);
    if (mousemoveEle) {
      this.dropBoundingClientRectArr.forEach((obj) => {
        obj.el.classList.contains("mouseover-active") &&
          obj.el.classList.remove("mouseover-active");
      });
      mousemoveEle.classList.add("mouseover-active");
    }
  }
  drop() {
    this.dropHandler && this.dropHandler(this._dragElement, this._dropElement);
    this.init();
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值