폭포 흐름 레이아웃의 그림에는 동일한 너비와 가변 높이라는 핵심 기능이 있습니다. 폭포 흐름 레이아웃은 Pinterest, Huaban.com 등과 같은 국내 웹사이트에서 어느 정도 사용됩니다. 본 글은 주로 네이티브 JS 워터폴 플로우 플러그인과 코드 관련 설명을 자세하게 분석한 글이므로 관심 있는 독자분들은 참고하셔서 공부하시는데 도움이 되었으면 좋겠습니다.
기본 함수 구현
먼저 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 = 'absolute' // 核心语句 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
, offsetTop
및 scrollTop도 이해해야 합니다. </code > 속성의 차이점을 비교해야만 이 프로젝트를 더 잘 이해할 수 있습니다. CSS 코드는 다음과 같이 간단합니다. <code>offsetHeight
这个属性,这个属性的高度之和等于图片高度 + 内边距 + 边框
,正因为此,我们用了 padding 而不是 margin 来设置图片与图片之间的距离。此外除了offsetHeight
属性,此外还要理解 offsetHeight
、clientHeight
、offsetTop
、scrollTop
等属性的区别,才能比较好的理解这个项目。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('img') img.setAttribute('src', `images/${i+1}.png`) img.setAttribute('class', 'waterfall-box') fragment.appendChild(img) } $waterfall.appendChild(fragment) } }
因为父节点可能自定义节点,所以提供了对监听 scroll 函数的封装,代码如下:
proto.bind = function () { const bindScrollElem = document.getElementById(this.opts.scrollElem) util.addEventListener(bindScrollElem || window, 'scroll', 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('load') } } proto.done = function () { this.on('done', function () { isLoading = false ... }) this.emit('done') }
这时候需要在调用的地方加上 waterfall.done
const waterfall = new Waterfall({}) waterfall.on("load", function () { // 异步/同步加载图片 waterfall.done() })
스크롤 및 크기 조정 이벤트 모니터링 구현
초기화 기능 init을 구현한 후 다음 단계는 스크롤 이벤트 모니터링을 구현하는 것입니다. 상위 노드의 맨 아래에 도달합니다. 그림이 꾸준히 로드되는 효과가 있습니다. 이때 고려해야 할 점은 스크롤할 때 로딩 기능이 어느 위치에서 실행되는지입니다. 이는 사람마다 다릅니다. 내 접근 방식은 상위 컨테이너 높이 + 스크롤 거리> 마지막 이미지의 offsetTop
조건, 즉 주황색 선 + 보라색 선 > 파란색이 충족될 때 로드를 트리거하는 것입니다. line 함수의 코드는 다음과 같습니다.
rrreee상위 노드가 노드를 맞춤화할 수 있으므로 스크롤 모니터링 기능의 캡슐화를 제공합니다.
rrreeeResize 이벤트 모니터링은 다음과 같습니다. 스크롤 이벤트 모니터링과 유사합니다. 트리거될 때 크기 조정 기능이 없으면 초기화 기능을 호출하여 재설정하면 됩니다.
🎜게시-구독 모델과 상속을 사용하여 리스닝 바인딩 구현🎜🎜플러그인 개발이 목표이기 때문에 기능 구현에만 만족할 수 없고 개발자가 스스로 처리할 수 있도록 해당 운영 공간을 남겨둡니다. . 비즈니스 시나리오에서 폭포 흐름의 그림 드롭다운 로드를 떠올려 보면 일반적으로 Ajax를 통해 비동기식으로 가져오므로 로드된 데이터를 라이브러리에 하드 코딩해서는 안 됩니다(여기에서는). 우리는 폭포수 사용에서 교훈을 얻습니다), 🎜🎜🎜 🎜rrreee🎜호출 방법을 살펴보면 게시/구독 모델을 사용하여 구현하는 것을 생각하는 것은 어렵지 않습니다. Node.js 비동기 비동기 레코드. 핵심 아이디어는 구독 함수를 통해 캐시에 함수를 추가한 다음 게시 함수를 통해 비동기 호출을 구현하는 것입니다. 코드 구현은 다음과 같습니다. 🎜🎜🎜🎜rrreee🎜다음으로 Waterfall이 게시/구독을 사용하도록 합니다. 모드에서는 Waterfall이 eventEmitter 함수를 상속하도록 하면 코드는 다음과 같이 구현됩니다. 🎜🎜🎜🎜rrreee🎜상속 방법은 생성자 상속과 프로토타입 체인 상속을 기반으로 하는 두 가지 작성 방법의 장점을 흡수하고Object를 사용합니다. .create
를 사용하여 분리 하위 클래스와 상위 클래스에 대해서는 상속에 대한 자세한 내용을 다루는 다른 기사를 작성할 수 있으므로 여기에서 멈추겠습니다. 🎜🎜🎜작은 최적화🎜🎜🎜스크롤 이벤트로 인해 이미지가 여러 번 로드되는 것을 방지하려면 흔들림 방지 및 조절 기능을 사용하는 것을 고려할 수 있습니다. 게시-구독 모델을 기반으로 로딩 여부를 나타내는 isLoading 매개변수가 정의되어 있으며, Boolean 값에 따라 로딩 여부가 결정됩니다. 🎜🎜🎜🎜rrreee🎜 이때 필요한 것은 다음과 같습니다. waterfall.done
을 추가하여 현재 이미지가 로드되었음을 알리는 코드는 다음과 같습니다. 🎜🎜🎜🎜rrreee🎜관련 권장사항: 🎜🎜🎜🎜자세한 설명 순수 네이티브 JS 워터폴 플러그인 Macy.js🎜🎜🎜🎜Jquery 사용 워터폴 플로우 플러그인_jquery🎜🎜🎜🎜jQuery 워터폴 플로우 플러그인 Wookmark 사용법 소개 example_jquery🎜🎜위 내용은 폭포수 흐름 플러그인을 구현하는 JS 코드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!