文章目录
项目介绍
利用原生js实现,主要分为三个部分。
一,导入图片 定义层级数 格式
第一部分为格式的生成,定义层数,行数,列数,元素组数以及图片数组,动态生成div,把利用模板字符串图片插入到动态生成的div里,再利用随机数打乱排列。
//导入图片 定义层级数 格式 const imagesDate = [ { img: './images/yingtao.png' }, { img: './images/putao.png' }, { img: './images/lizi.png' }, { img: './images/菠萝.png' }] const size = 80//图片大小 const rows = 6//行数 const cols = 6//列数 const delCount = 3//三个就消除 const group = 4//一共有四组 const cengCount = 5//层数有五层 const cunArr = []//存储数据的数组 const showDate = Array.from(new Array(delCount * group)).map(v => { return imagesDate.map(v => ({ ...v })) }).flat().sort(v => Math.random() - 0.5)// 算出图片总数然后用随机数打乱,flat把[[]]数组展开 随机0-0.5 缩小范围 sort() 方法对数组的项目进行排序。 //1.利用多层for循环形成格式 for (let cc = cengCount - 1; cc >= 0; cc--) { for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { let pianyi = (cc + 1) % 2 === 0 ? size / 2 : 0 //设置层数偏移量,偶数层进行偏移 //进行图片渲染 let item = (Math.random() > 0.7 && showDate.pop()) //pop用完就删,当随机数大于0.7时,从renderData中取数组的最后一项,.pop的意思是取最后一项并从数组中移除该项 item && cunArr.push(`<div class="item" id="m${cc}-${i}-${j}" style="width:${size}px;height:${size}px;left:${size * j + pianyi}px;top:${size * i + pianyi}px;"><img src="${item.img}"/></div>`) } } }
二,计算遮罩部分
第二部分是给盖着的元素加阴影遮盖,利用for循环取出所有偶数层,判断此层的上一层是否有元素,如果有,就加遮盖,如果此层处于2,4,6 对比的处于1,3,5 判断四周是否有图片 如果有,就加遮盖 没有 就把遮盖移除
//2.计算遮罩部分:在css定义一个class类,能标注暗色,判断是否需要添加class属性,利用classlist中的方法进行删除与添加class const checkDisabled = (items) => { (items || main.querySelectorAll('.item')).forEach((v, i) => { const arr = v.id.substring(1).split('-').map(v => Number(v))//把id取出来 const isPy = (arr[0] + 1) % 2 === 0//还是偶数层偏移 for (let i = arr[0] + 1; i <= cengCount - 1; i++) {//for循环,还是找 const isPyB = (i + 1) % 2 === 0//所在的上一层处于第几层 if (isPy === isPyB) {//身在不同层,但是坐标一样 const el = main.querySelector(`#m${i}-${arr[1]}-${arr[2]}`)//得到层数,后面依次为数组第二项,第三项 if (el) {//若是存在,则加遮盖 v.classList.add('disabled') break; } } else if (isPy && !isPyB) {//元素本身处于2,4,6 对比的处于1,3,5 if (![ `${i}-${arr[1]}-${arr[2]}`, `${i}-${arr[1]}-${arr[2] + 1}`, `${i}-${arr[1] + 1}-${arr[2]}`, `${i}-${arr[1] + 1}-${arr[2] + 1}`//都为偏移量,就是周围的一圈图片 ].every(k => {//every一true全true 一false全false return !main.querySelector('#m' + k) //k即为上述偏移量 (被遮住为false 加!) })) { v.classList.add('disabled')//如果为false 就加上disabled break; } else { v.classList.remove('disabled') } } else if (!isPy && isPyB) {//元素本身处于1,3,5 对比的处于2,4,6 if (![ `${i}-${arr[1]}-${arr[2]}`, `${i}-${arr[1]}-${arr[2] - 1}`, `${i}-${arr[1] - 1}-${arr[2]}`, `${i}-${arr[1] - 1}-${arr[2] - 1}` ].every(k => { return !main.querySelector('#m' + k) })) { v.classList.add('disabled') break; } else { v.classList.remove('disabled') } } } }) }
三,点击卡片进行消除计算
第三部分为元素的偏移与消除,当点击图片时,图片偏移到消除框里,同时要防止框内的元素重叠,当框内有三个相同的元素时,就消除,其他剩余的元素自动对齐
et move = (me) => { //下边框的右边距离 和上边距离 let left = show.offsetLeft - main.offsetLeft let top = show.offsetTop - main.offsetTop if (!canMove || me.className.indexOf('disabled') >= 0) { return //禁用的不能点击移动 } canMove = false if (show.children.length > 0) { let el = show.lastElementChild left = el.offsetLeft + size //防止框内的元素重叠 } me.style.top = `${top + 4}px` me.style.left = `${left + 1}px` me.transitionNamesCount = 0 //计数,有俩个动画,执行两次 me.ontransitionend = (e) => { me.transitionNamesCount++ if (me.transitionNamesCount === 2) { moveEnd(me) canMove = true } } } for (let i = 0; i < cunArr.length; i++) { const me = main.children[i] me.addEventListener('click', () => { move(me) }) } //转移节点 const moveEnd = (me) => { me.ontransitionend = null me.setAttribute('onclick', '') me.style.top = 0 show.appendChild(me) //这就把上面的节点转移到下边 const findResult = [...show.children].filter(v => v.innerHTML === me.innerHTML)//...先转为数组 filter(函数) 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。 if (findResult.length === 3) { findResult.forEach(v => { v.ontransitionend = () => { show.removeChild(v); //动画完了以后 清除元素 但是 这样虽然清除了 元素 元素不能自动归位 [...show.children].forEach((v, i) => {//先转为数组再遍历 v.style.left = `${i * size + show.offsetLeft - main.offsetLeft}px`//归位 对齐 }) } setTimeout(() => v.style.transform = 'scale(0)' )//动画消除 }) } setTimeout(() => {// setTimeout 多少秒后调用函数 if (show.children.length > 6) { alert('小笨蛋,输了吧,重新开始吧') return location.reload() } else if (main.children.length === 0) { alert('恭喜你赢了') return location.reload() } }, 100) checkDisabled() }
遇到的问题以及解决方法
1,图片插入不成功
<img src="${item.img}"/>
在模板字符串最后边加个/
2,多个点击事件创建
先移除模板字符串中的点击事件,用addEventListener()方法进行创建点击事件
for (let i = 0; i < cunArr.length; i++) { const me = main.children[i] me.addEventListener('click', () => { move(me) }) }
for (let i = 0; i < cunArr.length; i++) { const me1 = main.children[i] me1.addEventListener('click', mksound) }
3,函数无法调用
利用箭头函数进行调用
for (let i = 0; i < cunArr.length; i++) { const me = main.children[i] me.addEventListener('click', () => { move(me) }) }
4,元素点击时偏移出错
设置相对于元素所在框的位置