>웹 프론트엔드 >JS 튜토리얼 >JS를 사용하여 Waterfall Flow 플러그인을 구현하는 방법

JS를 사용하여 Waterfall Flow 플러그인을 구현하는 방법

亚连
亚连원래의
2018-06-07 14:28:281335검색

이 글에는 네이티브 JS 워터폴 플러그인에 대한 자세한 분석과 코드 관련 설명이 나와 있으니 관심 있는 독자분들은 참고하시기 바랍니다.

폭포 흐름 레이아웃의 그림에는 동일한 너비와 가변 높이라는 핵심 기능이 있습니다. 폭포 흐름 레이아웃은 Pinterest, Huaban.com 등과 같은 국내 웹사이트에서 특정 규모로 사용됩니다. 그런 다음 이 기능을 기반으로 폭포 흐름 탐색 여정을 시작합니다.

기본 함수 구현

먼저 20개의 그림이 포함된 컨테이너를 정의합니다.

<body>
 <style>
  #waterfall {
   position: relative;
  }
  .waterfall-box {
   float: left;
   width: 200px;
  }
 </style>
</body>
<p id="waterfall">
  <img src="images/1.png" class="waterfall-box">
  <img src="images/2.png" class="waterfall-box">
  <img src="images/3.png" class="waterfall-box">
  <img src="images/4.png" class="waterfall-box">
  <img src="images/5.png" class="waterfall-box">
  <img src="images/6.png" class="waterfall-box">
  ...
 </p>
由于未知的 css 知识点,丝袜最长的妹子把下面的空间都占用掉了。。。
接着正文,假如如上图,每排有 5 列,那第 6 张图片应该出现前 5 张图片哪张的下面呢?当然是绝对定位到前 5 张图片高度最小的图片下方。
那第 7 张图片呢?这时候把第 6 张图片和在它上面的图片当作是一个整体后,思路和上述是一致的。代码实现如下:
Waterfall.prototype.init = function () {
 ...
 const perNum = this.getPerNum() // 获取每排图片数
 const perList = []       // 存储第一列的各图片的高度
 for (let i = 0; i < perNum; i++) {
  perList.push(imgList[i].offsetHeight)
 }
 let pointer = this.getMinPointer(perList) // 求出当前最小高度的数组下标
 for (let i = perNum; i < imgList.length; i++) {
  imgList[i].style.position = &#39;absolute&#39; // 核心语句
  imgList[i].style.left = `${imgList[pointer].offsetLeft}px`
  imgList[i].style.top = `${perList[pointer]}px`

  perList[pointer] = perList[pointer] + imgList[i].offsetHeight // 数组最小的值加上相应图片的高度
  pointer = this.getMinPointer(perList)
 }
}

주의 깊은 친구들은 offsetHeight 속성이 코드에서 그림의 높이를 얻는 데 사용된다는 것을 발견했을 것입니다. 이는 속성 높이의 합이 이미지 높이 + 패딩 + 테두리와 동일하기 때문에 이미지 사이의 거리를 설정하기 위해 여백 대신 패딩을 사용합니다. offsetHeight 속성 외에도 offsetHeight, clientHeight, offsetTopscrollTop도 이해해야 합니다. 속성의 차이점을 비교해야만 이 프로젝트를 더 잘 이해할 수 있습니다. CSS 코드는 다음과 같이 간단합니다. offsetHeight 这个属性,这个属性的高度之和等于图片高度 + 内边距 + 边框,正因为此,我们用了 padding 而不是 margin 来设置图片与图片之间的距离。此外除了offsetHeight 属性,此外还要理解 offsetHeightclientHeightoffsetTopscrollTop 等属性的区别,才能比较好的理解这个项目。css 代码简单如下:

.waterfall-box {
 float: left;
 width: 200px;
 padding-left: 10px;
 padding-bottom: 10px;
}

scroll、resize 事件监听的实现

实现了初始化函数 init 以后,下一步就要实现对 scroll 滚动事件进行监听,从而实现当滚到父节点的底部有源源不断的图片被加载出来的效果。这时候要考虑一个点,是滚动到什么位置时触发加载函数呢?这个因人而异,我的做法是当满足 父容器高度 + 滚动距离 > 最后一张图片的 offsetTop 这个条件,即橙色线条 + 紫色线条 > 蓝色线条时触发加载函数,代码如下:

window.onscroll = function() {
 // ...
 if (scrollPX + bsHeight > imgList[imgList.length - 1].offsetTop) {// 浏览器高度 + 滚动距离 > 最后一张图片的 offsetTop
  const fragment = document.createDocumentFragment()
  for(let i = 0; i < 20; i++) {
   const img = document.createElement(&#39;img&#39;)
   img.setAttribute(&#39;src&#39;, `images/${i+1}.png`)
   img.setAttribute(&#39;class&#39;, &#39;waterfall-box&#39;)
   fragment.appendChild(img)
  }
  $waterfall.appendChild(fragment)
 }
}

因为父节点可能自定义节点,所以提供了对监听 scroll 函数的封装,代码如下:

proto.bind = function () {
  const bindScrollElem = document.getElementById(this.opts.scrollElem)
  util.addEventListener(bindScrollElem || window, &#39;scroll&#39;, scroll.bind(this))
 }
 const util = {
  addEventListener: function (elem, evName, func) {
   elem.addEventListener(evName, func, false)
  },
 }

resize 事件的监听与 scroll 事件监听大同小异,当触发了 resize 函数,调用 init 函数进行重置就行。

使用发布-订阅模式和继承实现监听绑定

既然以开发插件为目标,不能仅仅满足于功能的实现,还要留出相应的操作空间给开发者自行处理。联想到业务场景中瀑布流中下拉加载的图片一般都来自 Ajax 异步获取,那么加载的数据必然不能写死在库里,期望能实现如下调用(此处借鉴了 waterfall 的使用方式),

const waterfall = new Waterfall({options})
waterfall.on("load", function () {
 // 此处进行 ajax 同步/异步添加图片
})

观察调用方式,不难联想到使用发布/订阅模式来实现它,关于发布/订阅模式,之前在 Node.js 异步异闻录 有介绍它。其核心思想即通过订阅函数将函数添加到缓存中,然后通过发布函数实现异步调用,下面给出其代码实现:

function eventEmitter() {
 this.sub = {}
}
eventEmitter.prototype.on = function (eventName, func) { // 订阅函数
 if (!this.sub[eventName]) {
  this.sub[eventName] = []
 }
 this.sub[eventName].push(func) // 添加事件监听器
}
eventEmitter.prototype.emit = function (eventName) { // 发布函数
 const argsList = Array.prototype.slice.call(arguments, 1)
 for (let i = 0, length = this.sub[eventName].length; i < length; i++) {
  this.sub[eventName][i].apply(this, argsList) // 调用事件监听器
 }
}

接着,要让 Waterfall 能使用发布/订阅模式,只需让 Waterfall 继承 eventEmitter 函数,代码实现如下:

function Waterfall(options = {}) {
 eventEmitter.call(this)
 this.init(options) // 这个 this 是 new 的时候,绑上去的
}
Waterfall.prototype = Object.create(eventEmitter.prototype)
Waterfall.prototype.constructor = Waterfall

继承方式的写法吸收了基于构造函数继承和基于原型链继承两种写法的优点,以及使用 Object.create 隔离了子类和父类,关于继承更多方面的细节,可以另写一篇文章了,此处点到为止。

小优化

为了防止 scroll 事件触发多次加载图片,可以考虑用函数防抖与节流实现。在基于发布-订阅模式的基础上,定义了个 isLoading 参数表示是否在加载中,并根据其布尔值决定是否加载,代码如下:

let isLoading = false
const scroll = function () {
 if (isLoading) return false // 避免一次触发事件多次
 if (scrollPX + bsHeight > imgList[imgList.length - 1].offsetTop) { // 浏览器高度 + 滚动距离 > 最后一张图片的 offsetTop
  isLoading = true
  this.emit(&#39;load&#39;)
 }
}
proto.done = function () {
 this.on(&#39;done&#39;, function () {
  isLoading = false
  ...
 })
 this.emit(&#39;done&#39;)
}

这时候需要在调用的地方加上 waterfall.done

const waterfall = new Waterfall({})
waterfall.on("load", function () {
 // 异步/同步加载图片
 waterfall.done()
})

Scroll, resize 이벤트 모니터링 구현

초기화 함수 init를 구현한 후 다음 단계는 스크롤 이벤트 모니터링을 구현하는 것입니다. 스크롤이 도달하면 인식합니다. 상위 노드의 맨 아래에는 그림이 꾸준히 로드되는 효과가 있습니다. 이때 고려해야 할 점은 스크롤할 때 로딩 기능이 어느 위치에서 실행되는지입니다. 이는 사람마다 다릅니다. 내 접근 방식은 상위 컨테이너 높이 + 스크롤 거리> 마지막 이미지의 offsetTop 조건, 즉 주황색 선 + 보라색 선 > 파란색이 충족될 때 로드를 트리거하는 것입니다. line 함수의 코드는 다음과 같습니다.

rrreee

상위 노드가 노드를 맞춤화할 수 있으므로 스크롤 모니터링 기능의 캡슐화를 제공합니다. rrreeeResize 이벤트 모니터링은 스크롤 이벤트 모니터링과 유사합니다. 크기 조정 기능이 실행되면 init를 호출하여 기능을 재설정하면 됩니다.

게시-구독 모델과 ​​상속을 사용하여 리스닝 바인딩 구현플러그인 개발이 목표이기 때문에 기능 구현에만 만족할 수 없고 개발자가 스스로 처리할 수 있도록 해당 운영 공간을 남겨둡니다. . 비즈니스 시나리오에서 워터폴 흐름의 그림 드롭다운 로드를 생각하면 일반적으로 Ajax를 통해 비동기적으로 가져오므로 로드된 데이터를 라이브러리에 하드 코딩하면 안 됩니다(여기에서는). 우리는 폭포수 사용에서 교훈을 얻었습니다),

rrreee

호출 방법을 살펴보면 게시/구독 모델을 사용하여 구현하는 것을 생각하는 것은 어렵지 않습니다. .js 비동기 비동기 레코드. 핵심 아이디어는 구독 기능을 통해 캐시에 함수를 추가한 다음 게시 기능을 통해 비동기 호출을 구현하는 것입니다. 코드 구현은 다음과 같습니다. rrreee 다음으로 Waterfall이 게시/구독 모드를 사용하도록 하려면 Waterfall이 eventEmitter 함수를 상속하도록 하기만 하면 코드는 다음과 같이 구현됩니다.

rrreee

상속 방법은 생성자 상속과 프로토타입 체인 상속을 기반으로 하는 두 가지 작성 방법의 장점을 흡수하고 Object.create 이제 하위 클래스와 상위 클래스가 있으므로 상속에 대한 자세한 내용을 설명하는 또 다른 기사를 작성할 수 있으므로 여기서 멈추겠습니다. 🎜🎜🎜작은 최적화🎜🎜🎜스크롤 이벤트로 인해 이미지가 여러 번 로드되는 것을 방지하려면 흔들림 방지 및 조절 기능을 사용하는 것을 고려할 수 있습니다. 게시-구독 모델을 기반으로 로드 여부를 나타내는 isLoading 매개변수가 정의되어 있으며, 해당 부울 값에 따라 로드 여부가 결정됩니다. 🎜rrreee

여기서 현재 이미지가 로드되었음을 알리려면 waterfall.done을 추가하세요. 코드는 다음과 같습니다. 🎜rrreee🎜위는 제가 모두를 위해 컴파일한 것입니다. . 앞으로 모든 사람에게 도움이 되기를 바랍니다. 🎜🎜관련 기사: 🎜🎜🎜Vue 구성 요소 및 Route의 수명 주기(자세한 튜토리얼) 🎜🎜🎜🎜SpringMVC를 사용하여 vue 도메인 간 요청 해결🎜🎜🎜🎜webpack 4.0.0-beta.0 버전의 새로운 기능( 자세한 튜토리얼) 🎜 🎜

위 내용은 JS를 사용하여 Waterfall Flow 플러그인을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.