Maison  >  Article  >  interface Web  >  Comment implémenter le plug-in de flux en cascade à l'aide de JS

Comment implémenter le plug-in de flux en cascade à l'aide de JS

亚连
亚连original
2018-06-07 14:28:281265parcourir

Cet article vous donne une analyse détaillée d'un plug-in de cascade JS natif et des explications liées au code. Les lecteurs qui sont intéressés par cela peuvent s'y référer.

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, petals.com, etc. Ensuite, sur la base de cette fonctionnalité, nous commencerons le voyage d’exploration des flux de cascades.

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)
 }
}

Des amis attentifs ont peut-être découvert le code Le offsetHeight. L'attribut est utilisé pour obtenir la hauteur de l'image. 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 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 aussi simple que 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 avoir implémenté la fonction d'initialisation init, l'étape suivante consiste à implémenter l'événement scroll Listen, afin d'obtenir l'effet d'un flux constant d'images chargées lors du défilement vers le bas du nœud parent. 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 > 🎜> car le nœud parent peut fournir 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)
 }
}

La surveillance des événements de redimensionnement est similaire à la surveillance des événements de défilement. appelez simplement la fonction init pour réinitialiser.

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

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, c'était le cas. introduit auparavant dans Node.js Asynchronous Asynchronous Record. 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, et 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) // 调用事件监听器
 }
}

La méthode d'héritage absorbe les avantages des deux méthodes d'écriture basées sur l'héritage du constructeur et l'héritage de la chaîne de prototypes , et utilise

pour isoler les classes enfants 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.createPetite optimisation

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 Go to

pour notifier 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.doneCe qui précède est ce que j'ai compilé pour tout le monde, j'espère qu'il sera utile. à tout le monde à l'avenir.

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

Cycle de vie des composants Vue et Route (tutoriel détaillé)

Utiliser SpringMVC pour résoudre les requêtes inter-domaines Vue

Nouvelles fonctionnalités de la version webpack 4.0.0-beta.0 (tutoriel détaillé)

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