Files: a0d45464afed54dcb36fdf48278c914c2204a62e / lib / pull-scroll.js
3022 bytesRaw
1 | // forked because of weird non-filling and over-resuming problems |
2 | // should really PR this, but Dominic might not accept it :D |
3 | |
4 | var pull = require('pull-stream') |
5 | var Pause = require('pull-pause') |
6 | var Value = require('mutant/value') |
7 | |
8 | var next = 'undefined' === typeof setImmediate ? setTimeout : setImmediate |
9 | var buffer = Math.max(window.innerHeight * 2, 1000) |
10 | |
11 | var u = require('pull-scroll/utils'), |
12 | assertScrollable = u.assertScrollable, |
13 | isEnd = u.isEnd, |
14 | isVisible = u.isVisible |
15 | |
16 | module.exports = Scroller |
17 | |
18 | |
19 | function Scroller(scroller, content, render, isPrepend, isSticky, cb) { |
20 | assertScrollable(scroller) |
21 | var obs = Value(0) |
22 | |
23 | //if second argument is a function, |
24 | //it means the scroller and content elements are the same. |
25 | if('function' === typeof content) { |
26 | cb = isSticky |
27 | isPrepend = render |
28 | render = content |
29 | content = scroller |
30 | } |
31 | |
32 | if(!cb) cb = function (err) { if(err) throw err } |
33 | |
34 | scroller.addEventListener('scroll', scroll) |
35 | var pause = Pause(function () {}) |
36 | var queue = [] |
37 | |
38 | //apply some changes to the dom, but ensure that |
39 | //`element` is at the same place on screen afterwards. |
40 | |
41 | function add () { |
42 | if(queue.length) { |
43 | var m = queue.shift() |
44 | var r = render(m) |
45 | append(scroller, content, r, isPrepend, isSticky) |
46 | obs.set(queue.length) |
47 | } |
48 | } |
49 | |
50 | function scroll (ev) { |
51 | if(isEnd(scroller, buffer, isPrepend)) { |
52 | pause.resume() |
53 | } |
54 | } |
55 | |
56 | pause.pause() |
57 | |
58 | //wait until the scroller has been added to the document |
59 | next(function next () { |
60 | if(scroller.parentElement) pause.resume() |
61 | else setTimeout(next, 100) |
62 | }) |
63 | |
64 | var stream = pull( |
65 | pause, |
66 | pull.drain(function (e) { |
67 | queue.push(e) |
68 | obs.set(queue.length) |
69 | |
70 | if(content.clientHeight < window.innerHeight) |
71 | add() |
72 | |
73 | if (isVisible(content)) { |
74 | if (isEnd(scroller, buffer, isPrepend)) |
75 | add() |
76 | } |
77 | |
78 | if(queue.length > 5) { |
79 | pause.pause() |
80 | } |
81 | |
82 | }, function (err) { |
83 | if(err) console.error(err) |
84 | cb ? cb(err) : console.error(err) |
85 | }) |
86 | ) |
87 | |
88 | stream.visible = add |
89 | stream.queue = obs |
90 | return stream |
91 | } |
92 | |
93 | |
94 | function append(scroller, list, el, isPrepend, isSticky) { |
95 | if(!el) return |
96 | var s = scroller.scrollHeight |
97 | var st = scroller.scrollTop |
98 | if(isPrepend && list.firstChild) |
99 | list.insertBefore(el, list.firstChild) |
100 | else |
101 | list.appendChild(el) |
102 | |
103 | //scroll down by the height of the thing added. |
104 | //if it added to the top (in non-sticky mode) |
105 | //or added it to the bottom (in sticky mode) |
106 | if(isPrepend !== isSticky) { |
107 | var d = (scroller.scrollHeight - s) |
108 | var before = scroller.scrollTop |
109 | //check whether the browser has moved the scrollTop for us. |
110 | //if you add an element that is not scrolled into view |
111 | //it no longer bumps the view down! but this check is still needed |
112 | //for firefox. |
113 | //this seems to be the behavior in recent chrome (also electron) |
114 | if(st === scroller.scrollTop) { |
115 | scroller.scrollTop = scroller.scrollTop + d |
116 | } |
117 | } |
118 | } |
119 |
Built with git-ssb-web