瀑布流的实现和拖曳 div

663 阅读1分钟

瀑布流

前几天做一个类似便利贴的项目,我想了很多页面的设计,但是最后还是敲定使用瀑布流来展示,可以充分利用空间,而且给人浏览的欲望,但是做起来可没那么简单,我们先试一试吧

由于项目中使用 jQuery,故演示也使用 jQuery

const waterfall=(container)=>{
    const childs=container.children()
    const arr=[]
    // 存放每列高度的数组,依据此来确定每次放置节点的位置
    const cols=parseInt(($('#root').outerWidth(true)) / ($(childs).outerWidth(true)))
    // 确定列数,因为第一行的放置的其余行不相同,所以要分开处理,而且要向下取整
    for(let i=0;i<childs.length;i++){
         // 放置第一行图片
         if(i<cols){
             $(childs[i]).css({
                 top:0,
                 // 我们取元素的最大宽度,也就是包括了 margin,因为有可能用户设置了 margin,我们把它看成一个盒子的整体
                 left:`${i*$(childs).outerWidth(true)}px`
             })
             // 把第一列的高度 push 进去,以后循环需要使用
             arr.push($(childs[i]).outerHeight(true))
         }else{
             let minHeight=arr[0]
             let index=0
             for(let j=0;j<arr.length;j++){
                 if(arr[j]<minHeight){
                     // 从第二行元素开始,每轮循环找到高度最小的上一行元素,记录其高度和列的下标,也就是 index(下面有用)
                     minHeight=arr[j]
                     index=j
                 }
             }
             // 设置 top 为我们找到的最小高度,index 为最小高度的的列数的下标
             // 这个 childs[index] 不一定是那个最小高度的元素,但是列是一样的,我们只需要取他的 offsetLeft 即可
             $(childs[i]).css({
                 top:`${minHeight}px`,
                 left:`${childs[index].offsetLeft}px`
             })
             // arr 是我们用来判定每次最小高度的数组,它的长度永远只有 cols 那么长
             // 所以我们每次放置元素后,需要修改刚刚用过的最小高度,变成刚刚放置的元素的高度加上原来的高度
             arr[index]=arr[index]+$(childs[i]).outerHeight(true)
         }
    }
}

我认为,瀑布流比较核心的一点就是最小高度数组的处理,在最后一步,我们嫁接了高度,这样每次的最小高度在使用完之后,也就是放置一个元素之后,它将不会是最小高度了,那么第二小高度便开始生效,转换成视图就是每次先在高度最小的列放置节点

注意最后一步嫁接不要写成

arr[index]=arr[index]+minHeight

是因为 minHeight 其实和对应的 arr[index] 相等,可以说,它是旧的每一列的最小高度,所以应该加上该放置元素的高度完成嫁接

可以拖曳的 div

那就要先熟悉三个鼠标事件,鼠标按下(mousedown),鼠标移动(mousemove),鼠标松开(mouseup)

思路基本是,我们要知道鼠标移动的距离,然后把这个改变利用事件同步到元素上

let drag
let position
xxx.addEventListener('mousedown',(e)=>{
  drag=true
  position=[e.clientX,e.clientY]
})

document.addEventListener('mousemove',(e)=>{
  if(!drag)return
  let moveX=e.clientX-position[0]
  let moveY=e.clientY-position[1]
  let x=moveX+parseInt(xxx.getBoundingClientRect().left||0)
  let y=moveY+parseInt(xxx.getBoundingClientRect().top||0)
  xxx.style.transform=`translate(${x}px,${y}px)`
  position=[e.clientX,e.clientY]
})

document.addEventListener('mouseup',()=>{
  drag=false
})

试玩地址:JS Bin - JS Bin

我们在鼠标按下时,确定鼠标的位置,我们把移动分割为帧,每一帧我们先算出当前鼠标的落点,然后减去上一次的鼠标落点,得出 moveX 和 moveY

不过我们只拿到了变化量,我们还需要通过 getBoundingClientRect 得到元素距离视口的位置,进行相加才得到最终位置

总结

使用瀑布流要注意最小高度数组的设置

拖曳 div 的核心就是拿到变化量,然后对鼠标落点进行重置,便于下一次移动

现在可以用瀑布流和拖曳 div 做出很好看的效果啦