Les images de la disposition en cascade ont une caractéristique essentielle : une largeur égale et une hauteur variable. La disposition en cascade est utilisée dans une certaine mesure sur les sites Web nationaux, tels que Pinterest, Huaban.com, etc. Cet article analyse principalement en détail un plug-in de flux de cascade JS natif et les explications liées au code. Les lecteurs qui sont intéressés peuvent s'y référer et l'étudier. J'espère qu'il pourra vous aider.
Implémentation des fonctions de base
Nous définissons d'abord un conteneur avec 20 images,
<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) } }
Amis prudents J'ai peut-être découvert que l'attribut offsetHeight
est utilisé pour obtenir la hauteur de l'image dans le code. La somme des hauteurs de cet attribut est égale à 图片高度 + 内边距 + 边框
Pour cette raison, nous utilisons le remplissage au lieu de la marge pour définir la valeur. distance entre les images. En plus de l'attribut offsetHeight
, vous devez également comprendre les différences entre offsetHeight
, clientHeight
, offsetTop
, scrollTop
et d'autres attributs, afin de mieux comprendre ce projet. Le code CSS est simple comme suit :
.waterfall-box { float: left; width: 200px; padding-left: 10px; padding-bottom: 10px; }
Mise en œuvre de la surveillance des événements de défilement et de redimensionnement
Après la fonction d'initialisation init est implémenté, la première étape consiste à surveiller l'événement de défilement, de sorte que lorsque le défilement atteint le bas du nœud parent, un flux constant d'images soit chargé. Un point à considérer à ce stade est : à quelle position la fonction de chargement est-elle déclenchée lors du défilement ? Cela varie d'une personne à l'autre. Mon approche consiste à déclencher la fonction de chargement lorsque la condition 父容器高度 + 滚动距离 > 最后一张图片的 offsetTop
est remplie, c'est-à-dire ligne orange + ligne violette > 🎜>
Le nœud parent pouvant personnaliser le nœud, il fournit une encapsulation de la fonction de surveillance du défilement. Le code est le suivant :
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) } }
Surveillance des événements de redimensionnement et défilement La surveillance des événements est similaire lorsque la fonction de redimensionnement est déclenchée, appelez simplement la fonction init pour réinitialiser.
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) }, }
L'objectif étant de développer des plug-ins, on ne peut pas se contenter de la réalisation des fonctions, mais aussi laisser les opérations correspondantes espace à gérer pour les développeurs. Rappelant le chargement déroulant des images dans les flux en cascade dans les scénarios commerciaux, elles sont généralement obtenues de manière asynchrone via Ajax, les données chargées ne doivent donc pas être codées en dur dans la bibliothèque. Il est prévu que les appels suivants puissent être implémentés (ici). nous tirons les leçons de l'utilisation de Waterfall),
En observant la méthode d'appel, il n'est pas difficile de penser à utiliser le modèle de publication/abonnement pour l'implémenter. Concernant le modèle de publication/abonnement, il a été introduit auparavant dans l'enregistrement asynchrone asynchrone Node.js. L'idée principale est d'ajouter la fonction au cache via la fonction d'abonnement, puis d'implémenter l'appel asynchrone via la fonction de publication. L'implémentation du code est donnée ci-dessous :
const waterfall = new Waterfall({options}) waterfall.on("load", function () { // 此处进行 ajax 同步/异步添加图片 })
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) // 调用事件监听器 } }
pour isoler les sous-classes et les classes parents, vous pouvez écrire un autre article pour plus de détails sur l'héritage, donc je vais m'arrêter ici.
function Waterfall(options = {}) { eventEmitter.call(this) this.init(options) // 这个 this 是 new 的时候,绑上去的 } Waterfall.prototype = Object.create(eventEmitter.prototype) Waterfall.prototype.constructor = Waterfall
Object.create
. À ce moment, vous devez ajouter
au lieu d'appel pour informer que l'image actuelle a été chargée. Le code est le suivant :
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
Recommandations associées :
const waterfall = new Waterfall({}) waterfall.on("load", function () { // 异步/同步加载图片 waterfall.done() })
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!