Files: 6cc8cd15764104d1cbb616e1ab246bbde516d6a9 / lib / scroller.js
2208 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 | var stream = pull( |
47 | pause, |
48 | pull.drain(function (msg) { |
49 | toRenderCount.set(toRenderCount() + 1) |
50 | |
51 | onceIdle(() => { |
52 | var element = render(msg) |
53 | appendQueue.push(element) |
54 | toRenderCount.set(toRenderCount() - 1) |
55 | }) |
56 | |
57 | if (queueLength() > 5) { |
58 | pause.pause() |
59 | } |
60 | }, function (err) { |
61 | running = false |
62 | clearInterval(visibleInterval) |
63 | opts.onDone ? opts.onDone(err) : console.error(err) |
64 | }) |
65 | ) |
66 | |
67 | var visibleInterval = setInterval(() => { |
68 | // check for visible items every 2 seconds |
69 | Array.from(pendingVisible).forEach(checkVisible) |
70 | }, 2000) |
71 | |
72 | stream.queue = queueLength |
73 | |
74 | appendLoop() |
75 | return stream |
76 | |
77 | function checkVisible (element) { |
78 | var height = scroller.clientHeight |
79 | var rect = element.getBoundingClientRect() |
80 | if (height > 50 && rect.bottom < height) { |
81 | pendingVisible.delete(element) |
82 | if (opts.onItemVisible) { |
83 | onceIdle(() => opts.onItemVisible(element)) |
84 | } |
85 | } |
86 | } |
87 | } |
88 |
Built with git-ssb-web