Repository navigation
Expand file tree
/
Copy pathscroller.js
More file actions
executable file
·148 lines (130 loc) · 3.65 KB
/
Copy pathscroller.js
File metadata and controls
executable file
·148 lines (130 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* scroller - handles the details
* of figuring out which section
* the user is currently scrolled
* to.
*
*/
d3.rebind = function(target, source) {
var i = 1, n = arguments.length, method;
while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
return target;
};
// Method is assumed to be a standard D3 getter-setter:
// If passed with no arguments, gets the value.
// If passed with arguments, sets the value and returns the target.
function d3_rebind(target, source, method) {
return function() {
var value = method.apply(source, arguments);
return value === source ? target : value;
};
}
function scroller() {
var windowHeight;
var container = d3.select('body');
// event dispatcher
var dispatch = d3.dispatch("active", "progress");
// d3 selection of all the
// text sections that will
// be scrolled through
var sections = null;
// array that will hold the
// y coordinate of each section
// that is scrolled through
var sectionPositions = [];
var currentIndex = -1;
// y coordinate of
var containerStart = 0;
/**
* scroll - constructor function.
* Sets up scroller to monitor
* scrolling of els selection.
*
* @param els - d3 selection of
* elements that will be scrolled
* through by user.
*/
function scroll(els) {
sections = els;
// when window is scrolled call
// position. When it is resized
// call resize.
d3.select(window)
.on("scroll.scroller", position)
.on("resize.scroller", resize);
// manually call resize
// initially to setup
// scroller.
resize();
// hack to get position
// to be called once for
// the scroll position on
// load.
d3.timer(function() {
position();
return true;
});
}
/**
* resize - called initially and
* also when page is resized.
* Resets the sectionPositions
*
*/
function resize() {
// sectionPositions will be each sections
// starting position relative to the top
// of the first section.
sectionPositions = [];
var startPos;
sections.each(function(d,i) {
var top = this.getBoundingClientRect().top;
if(i === 0) {
startPos = top;
}
sectionPositions.push(top - startPos);
});
containerStart = container.node().getBoundingClientRect().top + window.pageYOffset;
}
/**
* position - get current users position.
* if user has scrolled to new section,
* dispatch active event with new section
* index.
*
*/
function position() {
var pos = window.pageYOffset - 10 - containerStart;
var sectionIndex = d3.bisect(sectionPositions, pos);
sectionIndex = Math.min(sections.size() - 1, sectionIndex);
if (currentIndex !== sectionIndex) {
dispatch.call("active",this,sectionIndex);
currentIndex = sectionIndex;
}
var prevIndex = Math.max(sectionIndex - 1, 0);
var prevTop = sectionPositions[prevIndex];
var progress = (pos - prevTop) / (sectionPositions[sectionIndex] - prevTop);
dispatch.call("progress",this,currentIndex,progress);
}
/**
* container - get/set the parent element
* of the sections. Useful for if the
* scrolling doesn't start at the very top
* of the page.
*
* @param value - the new container value
*/
scroll.container = function(value) {
if (arguments.length === 0) {
return container;
}
container = value;
return scroll;
};
// allows us to bind to scroller events
// which will interally be handled by
// the dispatcher.
// Copies a variable number of methods from source to target.
d3.rebind(scroll, dispatch, "on");
return scroll;
}