Maison  >  Article  >  interface Web  >  Code JS pour implémenter le plug-in de flux en cascade

Code JS pour implémenter le plug-in de flux en cascade

小云云
小云云original
2018-02-07 09:25:042461parcourir

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 = &#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)
 }
}

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(&#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)
 }
}

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.

Utiliser le modèle de publication-abonnement et l'héritage pour implémenter la liaison d'écoute
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)
  },
 }

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 同步/异步添加图片
})

<.>Ensuite, pour permettre à Waterfall d'utiliser le mode publication/abonnement, laissez simplement Waterfall hériter de la fonction eventEmitter. Le code est implémenté comme suit :


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) // 调用事件监听器
 }
}

Le. La méthode d'héritage absorbe l'héritage et Basée sur les avantages des deux méthodes d'écriture de l'héritage de chaîne de prototypes et de l'utilisation de

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
Petite optimisation

Object.create

Afin d'éviter que l'événement de défilement ne déclenche des chargements multiples d'images, vous pouvez envisager d'utiliser la fonction anti-shake et throttling. Basé sur le modèle de publication-abonnement, un paramètre isLoading est défini pour indiquer s'il est en cours de chargement, et s'il doit être chargé est déterminé en fonction de sa valeur booléenne. Le code est le suivant :

.

À 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(&#39;load&#39;)
 }
}
proto.done = function () {
 this.on(&#39;done&#39;, function () {
  isLoading = false
  ...
 })
 this.emit(&#39;done&#39;)
}

<.>waterfall.doneRecommandations associées :


Explication détaillée de l'utilisation du plug-in de flux de cascade JS natif pur Macy.js
const waterfall = new Waterfall({})
waterfall.on("load", function () {
 // 异步/同步加载图片
 waterfall.done()
})


Introduction à l'utilisation du plug-in de flux de cascade Jquery_jquery

Plug-in de flux de cascade JQuery Utilisation de Wookmark example_jquery

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn