AutoPagerを実装した。つぶやきを20件ずつ表示し、スクロール末尾まで到達すると次の20件を追加する。それを最後まで繰り返す。
ブツ
インストール&実行
NAME='Electron.MyLog.20220916094224'
git clone https://github.com/ytyaru/$NAME
cd $NAME
npm install
npm start
やったこと
AutoPagerを実装した。つぶやきを20件ずつ表示する。スクロール末尾まで到達すると次の20件を表示する。それを最後まで繰り返す。
これまでは最初に全件表示していた。だが、それだともし1000件あったらとても長い時間かかってしまう。初回表示を高速化するため最新順に20件ずつ表示するようにした。
- SQLite3で20件ずつ取得する
- スクロールバーの末尾到達判定
- つぶやきを20件ずつHTML表示する
最大20件ずつ取得するには以下。
select * from comments order by created desc limit ${limit} offset ${offset};
スクロールバーの末尾到達判定は以下。
#isFullScrolled(event) {
const adjustmentValue = 60 // ブラウザ設定にもよる。一番下までいかずとも許容する
const positionWithAdjustmentValue = event.target.clientHeight + event.target.scrollTop + adjustmentValue
return positionWithAdjustmentValue >= event.target.scrollHeight
}
ほか、細かい計算、イベントやDOM、パフォーマンス調整処理を実装した。
1. SQLite3で20件ずつ取得する
select * from comments order by created desc limit ${limit} offset ${offset};
項目 | 意味 | 例 |
---|---|---|
limit |
最大取得件数 | 20 |
offset |
先頭からすっ飛ばす件数 | 0 |
Electron IPC通信インタフェース化する
main.js
ipcMain.handle('getPage', async(event, limit, offset) => {
const res = lib.get(`DB`).exec(`select * from comments order by created desc limit ${limit} offset ${offset};`)
console.log(res)
return (0 === res.length) ? res : res[0].values
})
preload.js
getPage:async(limit, offset)=>await ipcRenderer.invoke('getPage', limit, offset),
2. スクロールバーの末尾到達判定
計算式は以下。
#isFullScrolled(event) {
const adjustmentValue = 60 // ブラウザ設定にもよる。一番下までいかずとも許容する
const positionWithAdjustmentValue = event.target.clientHeight + event.target.scrollTop + adjustmentValue
return positionWithAdjustmentValue >= event.target.scrollHeight
}
これをつぶやき一覧表示する親要素のスクロールイベント時に実行する。
this.ui.addEventListener('scroll', async(event) => {
if (this.#isFullScrolled(event)) { ... }
})
ただしイベント発火回数が多くなりすぎて処理が重くなる。そのため200ms以内に完了した最後の一回のみ実行するようにする。
this.ui.addEventListener('scroll', async(event) => {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(async()=>{
if (this.#isFullScrolled(event)) { ... }
}, 200);
})
3. つぶやきを20件ずつHTML表示する
スクロール最下端に達するたび#next()
を呼び出す。こいつで20件ずつ取得する。
async #next() {
console.log('AutoPager.next()')
if (this.offset < this.count) {
this.page++;
this.offset = this.limit * this.page
console.log(this.limit, this.offset)
return await window.myApi.getPage(this.limit, this.offset)
} else { return [] }
}
すでに取得済みの件数だけすっ飛ばす。その値offset
はlimit * page
で計算する。limit
は一回あたりの取得件数20件のこと。page
はスクロール最下端に到達した回数。
項目 | 意味 | 例 |
---|---|---|
limit |
最大取得件数 | 20 |
page |
スクロール最下端に到達した回数 | 0 |
offset |
先頭からすっ飛ばす件数 | 0 |
該当するレコードを取得したら全件HTML化する。すべて文字列として作成したあと末尾に追記する。insertAdjacentHTMLを使って。
#toHtml(records) {
console.log(records)
this.ui.insertAdjacentHTML('beforeend', records.map(r=>TextToHtml.toHtml(r[0], r[1], r[2], this.setting.mona.address)).join(''))
あとはそれをスクロールイベント時に呼び出すようにすれば完成。
this.ui.addEventListener('scroll', async(event) => {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(async()=>{
if (this.#isFullScrolled(event)) {
this.#toHtml(await this.#next())
}
}, 200);
})
auto-pager.js
20件ずつ表示するAutoPager処理をまとめたクラスを書いた。
class AutoPager {
constructor(setting) {
this.limit = 20
this.page = -1
this.offset = this.limit * this.page
this.count = 0
this.setting = setting
this.ui = document.querySelector('#post-list')
this.timeoutId = 0
console.log('AutoPager.count:', this.count, this.offset)
}
async setup() {
console.log('AutoPager.setup()')
this.count = await window.myApi.count()
this.ui.addEventListener('scroll', async(event) => {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(async()=>{
if (this.#isFullScrolled(event)) {
this.#toHtml(await this.#next())
}
}, 200);
})
this.#toHtml(await this.#next())
}
#isFullScrolled(event) {
const adjustmentValue = 60 // ブラウザ設定にもよる。一番下までいかずとも許容する
const positionWithAdjustmentValue = event.target.clientHeight + event.target.scrollTop + adjustmentValue
console.log(`isFullScrolled: ${positionWithAdjustmentValue >= event.target.scrollHeight}`)
return positionWithAdjustmentValue >= event.target.scrollHeight
}
async #next() {
console.log('AutoPager.next()')
if (this.offset < this.count) {
this.page++;
this.offset = this.limit * this.page
console.log(this.limit, this.offset)
return await window.myApi.getPage(this.limit, this.offset)
} else { return [] }
}
#toHtml(records) {
console.log(records)
this.ui.insertAdjacentHTML('beforeend', records.map(r=>TextToHtml.toHtml(r[0], r[1], r[2], this.setting.mona.address)).join(''))
}
}
あとはこれをrenderer.js
で以下のように呼び出せばいい。
const pager = new AutoPager(setting)
await pager.setup()
以前までは以下のように全件取得していた。これを削除しておく。
document.getElementById('post-list').innerHTML = await db.toHtml(document.getElementById('address').value)