Files: f6b15ef0c66ef8142dc6e63a6065df61a37573a2 / lib / scroller.js
2209 bytesRaw
1 | var pull = require('pull-stream') |
2 | var Pause = require('pull-pause') |
3 | var Value = require('mutant/value') |
4 | var onceIdle = require('mutant/once-idle') |
5 | var computed = require('mutant/computed') |
6 | |
7 | module.exports = Scroller |
8 | |
9 | function Scroller (scroller, content, render, opts) { |
10 | if (typeof opts === 'function') { |
11 | opts = {onDone: opts} |
12 | } else if (!opts) { |
13 | opts = {} |
14 | } |
15 | var toRenderCount = Value(0) |
16 | var toAppendCount = Value(0) |
17 | var pendingVisible = new Set() |
18 | |
19 | var queueLength = computed([toRenderCount, toAppendCount], (a, b) => a + b) |
20 | |
21 | var pause = Pause(function () {}) |
22 | var running = true |
23 | var appendQueue = [] |
24 | |
25 | function appendLoop () { |
26 | var distanceFromBottom = scroller.scrollHeight - (scroller.scrollTop + scroller.clientHeight) |
27 | if (distanceFromBottom < scroller.clientHeight) { |
28 | while (appendQueue.length) { |
29 | var element = appendQueue.shift() |
30 | content.appendChild(element) |
31 | pendingVisible.add(element) |
32 | } |
33 | } |
34 | |
35 | toAppendCount.set(appendQueue.length) |
36 | if (queueLength() < 5) { |
37 | // queue running low, resume stream |
38 | pause.resume() |
39 | } |
40 | |
41 | if (running || queueLength()) { |
42 | window.requestAnimationFrame(appendLoop) |
43 | } |
44 | |
45 | } |
46 | |
47 | var stream = pull( |
48 | pause, |
49 | pull.drain(function (msg) { |
50 | toRenderCount.set(toRenderCount() + 1) |
51 | |
52 | onceIdle(() => { |
53 | var element = render(msg) |
54 | appendQueue.push(element) |
55 | toRenderCount.set(toRenderCount() - 1) |
56 | }) |
57 | |
58 | if (queueLength() > 5) { |
59 | pause.pause() |
60 | } |
61 | }, function (err) { |
62 | running = false |
63 | clearInterval(visibleInterval) |
64 | opts.onDone ? opts.onDone(err) : console.error(err) |
65 | }) |
66 | ) |
67 | |
68 | var visibleInterval = setInterval(() => { |
69 | // check for visible items every 2 seconds |
70 | Array.from(pendingVisible).forEach(checkVisible) |
71 | }, 2000) |
72 | |
73 | stream.queue = queueLength |
74 | |
75 | appendLoop() |
76 | return stream |
77 | |
78 | function checkVisible (element) { |
79 | var height = scroller.clientHeight |
80 | var rect = element.getBoundingClientRect() |
81 | if (height > 50 && rect.bottom < height) { |
82 | pendingVisible.delete(element) |
83 | if (opts.onItemVisible) { |
84 | onceIdle(() => opts.onItemVisible(element)) |
85 | } |
86 | } |
87 | } |
88 | } |
89 |
Built with git-ssb-web