实现无限图片的瀑布流加载

index.html 代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>无限图片瀑布流</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div id="one" class="image-grid"></div>
    <script src="./index.js"></script>
  </body>
</html>

index.css

* {
	border: 0;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
:root {
	--bg: #e5e5e5;
	--cardBg: #f1f1f1;
	--fg: #171717;
	--linkFg: #2762f3;
	--transDur: 0.25s;
	font-size: calc(16px + (20 - 16)*(100vw - 320px)/(2560 - 320));
}
body {
	background: var(--bg);
	color: var(--fg);
	font: 1em/1.5 "Hind", sans-serif;
}
a {
	color: var(--linkFg);
}
/* Grid */
.image-grid {
	display: grid;
	grid-gap: 1.5em;
	margin: 1.5em 1.5em 13.5em 1.5em;
}
.image-grid__card {
	animation: flyIn var(--transDur) ease-in;
	background: var(--cardBg);
	border-radius: 0.25em;
	box-shadow: 0 0 0.375em #0003;
	width: 100%;
}
.image-grid__card a {
	background: #0003;
	border-radius: 0.25em 0.25em 0 0;
	display: block;
	overflow: hidden;
	position: relative;
	height: 9.75em;
	will-change: transform;
}
.image-grid__card a:focus {
	outline: transparent;
}
.image-grid__card a:focus .image-grid__card-thumb,
.image-grid__card a:hover .image-grid__card-thumb {
	opacity: 0.5;
}
.image-grid__card-thumb {
	display: block;
	position: absolute;
	top: 50%;
	left: 50%;
	width: auto;
	height: 100%;
	transition: all var(--transDur) linear;
	transform: translate(-50%,-50%);
}
.image-grid__card-thumb--portrait {
	width: 100%;
	height: auto;
}
.image-grid__card-title {
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;  
	overflow: hidden;
	margin: 0.375em 0.75em;
}
.image-grid__status {
	animation: fadeIn var(--transDur) linear;
	text-align: center;
	position: absolute;
	top: 50%;
	left: 0;
	transform: translateY(-50%);
	width: 100%;
}
/* Preloader */
.pl, .pl:before, .pl:after {
	animation-duration: 2s;
	animation-timing-function: linear;
	animation-iteration-count: infinite;
}
.pl {
	margin: 0 auto 1.5em auto;
	position: relative;
	width: 3em;
	height: 3em;
}
.pl:before, .pl:after {
	background: currentColor;
	content: "";
	display: block;
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 50%;
	transform-origin: 50% 100%;	
	clip-path: polygon(0 0,100% 0,50% 100%);
	-webkit-clip-path: polygon(0 0,100% 0,50% 100%);
}
.pl-fade:before {
	animation-name: fadeA;
}
.pl-fade:after {
	animation-name: fadeB;
}
/* Animations */
@keyframes flyIn {
	from {
		opacity: 0;
		transform: translateY(3em);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}
@keyframes fadeIn {
	from { opacity: 0; }
	to { opacity: 1; }
}
@keyframes fadeA {
	from, to { opacity: 1; transform: rotate(0deg) }
	25%, 75.1% { opacity: 0; transform: rotate(0deg) }
	25.1%, 75% { opacity: 0; transform: rotate(180deg) }
	50% { opacity: 1; transform: rotate(180deg) }
}
@keyframes fadeB {
	from, 50% { opacity: 0; transform: rotate(90deg) }
	25% { opacity: 1; transform: rotate(90deg) }
	50.1%, to { opacity: 0; transform: rotate(270deg) }
	75% { opacity: 1; transform: rotate(270deg) }
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #171717;
		--cardBg: #242424;
		--fg: #f1f1f1;
		--linkFg: #5785f6;
	}
}
@media (min-width: 512px) {
	.image-grid {
		grid-template-columns: repeat(2,1fr);
	}
}
@media (min-width: 768px) {
	.image-grid {
		grid-template-columns: repeat(3,1fr);
	}
}
@media (min-width: 1024px) {
	.image-grid {
		grid-template-columns: repeat(4,1fr);
	}
}
@media (min-width: 1280px) {
	.image-grid {
		grid-template-columns: repeat(5,1fr);
	}
}
@media (min-width: 1536px) {
	.image-grid {
		grid-template-columns: repeat(6,1fr);
	}
}

index.js

// 构造函数
class ImageGrid {
  constructor(args) {
    this.container = document.querySelector(`#${args.id}`)
    this.blockClass = 'image-grid'
    this.cardContent = null
    this.status = null
    this.page = 1
    this.imagesPerRequest = 100
    this.observer = new IntersectionObserver(
      (entries, self) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            self.unobserve(entry.target)
            let cards = this.getCards(),
              cardCount = cards.length,
              cardsAtPageStart = (this.page - 1) * this.imagesPerRequest,
              contentIndex = cardCount - cardsAtPageStart
            if (cardCount < cardsAtPageStart + this.cardContent.length) {
              this.addCard(this.cardContent[contentIndex])
              cards = this.getCards()
              self.observe(cards[cardCount])
            } else {
              ++this.page
              this.requestImages(this.imagesPerRequest, this.page)
            }
          }
        })
      },
      { root: null, rootMargin: '0px 0px 0px 0px', threshold: 0.99 }
    )
    this.createStatus()
    this.requestImages(this.imagesPerRequest)
  }
  createStatus() {
    // 获取页面容器
    this.status = document.createElement('div')
    this.status.className = `${this.blockClass}__status`
    this.container.appendChild(this.status)
    // 占位Loading
    let preloader = document.createElement('div'),
      statusMsg = document.createElement('p'),
      statusMsgText = document.createTextNode('Loading…')
    preloader.className = 'pl pl-fade'
    this.status.appendChild(preloader)
    this.status.appendChild(statusMsg)
    statusMsg.appendChild(statusMsgText)
  }
  setStatus(msg) {
    if (this.status !== null) {
      let preloader = this.status.querySelector('.pl'),
        statusMsg = this.status.querySelector('p')
      // 移除占位
      this.status.removeChild(this.status.firstChild)
      // 设置返回提示
      statusMsg.innerHTML = msg
    }
  }
  killStatus() {
    // 移除提示和占位
    if (this.status !== null) {
      let parent = this.status.parentElement
      parent.removeChild(parent.firstChild)
      this.status = null
    }
  }
  //   请求图片
  requestImages(perPage = 20, page = 1) {
    // Pixabay API设置的硬限制
    if (perPage < 3) perPage = 3
    else if (perPage > 200) perPage = 200
    if (page < 1) page = 1
    // 请求参数
    let APIKey = '2913992-c926292594f754c09b7f796ad',
      query = 'castle',
      minWidth = 270,
      minHeight = 180,
      url = `https://pixabay.com/api/?key=${APIKey}&q=${query}&image_type=photo&min_width=${minWidth}&min_height=${minHeight}&per_page=${perPage}&page=${page}&safesearch=true`
    // 发送请求
    this.requestJSON(url)
      .then(items => {
        this.cardContent = items
        // 第一次请求移除占位和提示
        if (this.cardContent !== null && this.cardContent.length) {
          this.killStatus()
          this.addCard(this.cardContent[0])
          let firstCard = this.container.lastChild
          this.observer.observe(firstCard)
        } else {
          this.setStatus('Nothing to show here…')
        }
      })
      .catch(msg => {
        this.setStatus(msg)
      })
  }
  //   定义请求方法
  requestJSON(resource) {
    return new Promise((resolve, reject) => {
      let request = new XMLHttpRequest()
      request.open('GET', resource, true)
      request.onload = function () {
        let items = null
        try {
          let response = JSON.parse(this.response)
          items = [...response.hits]
        } catch (err) {
          items = []
        }
        resolve(items)
      }
      request.onerror = () => {
        reject('网络断开,请重试!')
      }
      request.send()
    })
  }
  addCard(content) {
    let data = {
        title: content.tags || 'untitled',
        link: content.pageURL || '#0',
        thumbnail:
          content.webformatURL.replace('640.jpg', '180.jpg') ||
          'https://i.ibb.co/6Whjrmx/placeholder.png',
        thumbWidth: content.webformatWidth / 2 || 1,
        thumbHeight: content.webformatHeight / 2 || 1,
      },
      card = document.createElement('div'),
      thumbLink = document.createElement('a'),
      thumb = document.createElement('img'),
      cardTitle = document.createElement('span'),
      cardTitleText = document.createTextNode(data.title)
    card.className = `${this.blockClass}__card`
    this.container.appendChild(card)
    thumbLink.href = data.link
    thumbLink.rel = 'noopener noreferrer'
    thumbLink.target = '_blank'
    card.appendChild(thumbLink)
    thumb.className = `${this.blockClass}__card-thumb`
    if (data.thumbWidth < data.thumbHeight)
      thumb.classList.add(`${this.blockClass}__card-thumb--portrait`)
    thumb.src = data.thumbnail
    thumb.width = data.thumbWidth
    thumb.height = data.thumbHeight
    thumb.alt = data.title
    thumbLink.appendChild(thumb)
    // card title
    cardTitle.className = `${this.blockClass}__card-title`
    cardTitle.title = data.title
    card.appendChild(cardTitle)
    cardTitle.appendChild(cardTitleText)
  }
  getCards() {
    return this.container.querySelectorAll(`.${this.blockClass}__card`)
  }
}

document.addEventListener('DOMContentLoaded', () => {
  let imgGrid = new ImageGrid({ id: 'one' })
})

效果:
在这里插入图片描述

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值