Help us understand the problem. What is going on with this article?

今時の画像遅延読み込み(lazyload-image)と無限スクロール(infinite-scroll)

More than 1 year has passed since last update.

動作URLリポジトリ

ezgif-1-d445c4ef9afa.gif

下記のPolyfillを使ってES5変換・gzip後でindex.jsのサイズは11K程度。IE11、Safariでも動いてます。

index.html
<script src="https://unpkg.com/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="https://unpkg.com/intersection-observer/intersection-observer.js"></script>

※ 画像はここから借りました。

画像遅延読み込み(lazy-image)

将来的にはlazyload属性Blink LazyLoad(参考: img要素とiframe要素のlazyload属性)の普及に期待しつつ、Let's Build Web Components! Part 5: LitElementの記事を参考に書くと、

index.ts#LazyImage
const isInters = ({isIntersecting}:IntersectionObserverEntry) => isIntersecting

@customElement('lazy-image' as any)
export class LazyImage extends LitElement {

  @property() load = false

  @property() src = ''

  constructor(){
    super()
    new IntersectionObserver((entries, observer)=>{
      if(entries.some(isInters)){
        this.load = true
        observer.unobserve(this)
      }
    }).observe(this)
  }

  render() {
    return html`<img src=${this.load ? this.src : ''}>`
  }

}

IntersectionObserverが自身の要素を監視し、画面に表示されたらthis.loadtrueとなって、画像が表示されます。

const isInters = ({isIntersecting}:IntersectionObserverEntry) => isIntersecting
...
new IntersectionObserver((entries, observer)=>{
  if(entries.some(isInters)){
     ...

new IntersectionObserver((entries, observer)=>{
  entries.forEach( entry => {
    if(entry.isIntersecting){
      ...

と基本同じような処理なんですが、使い方かっこいい。。。

無限スクロール(infinite-scroll)

<virtual-scroller>を使ってみたかったのですが、Chromeでしか動かなかったのでlazy-imageを参考に書くと、

index.ts#infiniteScroll
@customElement('infinite-scroll' as any)
export class InfiniteScroll extends LitElement {

  @property({ type: { fromAttribute: (attr:string) => JSON.parse(attr) } })
  items: string[] = []

  @query('button') next?: HTMLButtonElement

  firstUpdated() {
    new IntersectionObserver( entries =>{
      if(entries.some(isInters)){
        this.items = [...this.items, ...this.items]
      }
    }).observe(this.next!)
  }

  render() {
    return html`
      <style>lazy-image { display: block; width: 100%; height: 480px; }</style>
      ${this.items.map(i=>html`<lazy-image src=${i}></lazy-image>`)}
      <button>next</button>
    `
  }

}

ここではページの最下部にある<button>を監視し、画面に表示されたらitems属性に指定されたデータを読み込み続けます。

index.html
<infinite-scroll items='["URL1","URL2","URL3"...]'></infinite-scroll>

LitElementでは属性に渡された値を配列として使いたい場合にfromAttributeを使って変換処理を定義できます(JSONを渡す場合は'シングルクォートを使わないといけない)。

this.items = [...this.items, ...this.items]

this.items.push( ...this.items)

でもよいのですが、配列に値が追加されただけではLitElement側で感知してくれないので、

this.items.push( ...this.items)
this.requestUpdate // or this.requestUpdate('item')

で再描画をリクエストする必要があります。

あと、実際に無限ループするページはSEO的によくないらしいので、

new IntersectionObserver((entries, observer)=>{
  if(entries.some(isInters)){
    ...
    if(/* isItemsLast? */){
       this.next!.hidden = true
       observer.unobserve(this.next!)
    }

などで、Observerを止めます。

ビルドについて

rollupを試してみたのですが、結局polymer-cliが一番楽でした、、、

package.json
  "dependencies": {
    "@polymer/lit-element": "^0.6.4"
  },
  "devDependencies": {
    "polymer-cli": "^1.9.1",
    "typescript": "^3.1.3"
  }
polymer.json
{
  "entrypoint": "index.html",
  "shell": "index.js",
  "builds": [{
    "name": "default",
    "preset": "es5-bundled",
    "addServiceWorker": false
  }],
  "moduleResolution": "node",
  "npm": true
}

あまり単体で使うものじゃなさそう(デフォルトでServiceWorkerを作ってくれたりとか)ですが、index.htmlも含めたPolyfillやminifyが便利です。

以上です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away