From 2af6ae9c7bcab3593436c7b240f6ea214cd26efe Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 15:56:17 -0500 Subject: [PATCH 1/8] syntax highlighting --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 43fe8c6..c5c06ae 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Simple scrolling events for [d3](https://github.com/mbostock/d3) graphs. Based o `graphScroll` takes a selection of explanatory text sections and dispatches `active` events as different sections are scrolled into to view. These `active` events can be used to update a chart's state. -``` +```js graphScroll() .sections(d3.selectAll('#sections > div')) .on('active', function(i){ console.log(i + 'th section active') }) @@ -14,7 +14,7 @@ graphScroll() The top most element scrolled fully into view is classed `graph-scroll-active`. This makes it easy to highlight the active section with css: -``` +```css #sections > div{ opacity: .3 } @@ -26,7 +26,7 @@ The top most element scrolled fully into view is classed `graph-scroll-active`. To support headers and intro images/text, we use a container element containing the explanatory text and graph. -``` +```html

Page Title
@@ -42,7 +42,7 @@ To support headers and intro images/text, we use a container element containing If these elements are passed to graphScroll as selections with `container` and `graph`, every element in the graph selection will be classed `graph-scroll-graph` if the top of the container is out of view. -``` +```js graphScroll() .graph(d3.selectAll('#graph')) .container(d3.select('#container')) @@ -54,7 +54,7 @@ graphScroll() With a little bit of css, the graph element snaps to the top of the page while the text scrolls by. -``` +```css #container{ position: relative; overflow: auto; @@ -81,7 +81,7 @@ With a little bit of css, the graph element snaps to the top of the page while t As the bottom of the container approaches the top of the page, the graph is classed with `graph-scroll-below`. A little more css allows the graph slide out of view gracefully: -``` +```css #graph.graph-scroll-below{ position: absolute; bottom: 0px; From 9052f203b4e7546fd751143bab54b8692a1ac386 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 15:56:48 -0500 Subject: [PATCH 2/8] Return `this` as active section --- graph-scroll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph-scroll.js b/graph-scroll.js index 4e07b8f..f6f8a99 100644 --- a/graph-scroll.js +++ b/graph-scroll.js @@ -23,7 +23,7 @@ function graphScroll() { if (i != i1){ sections.classed('graph-scroll-active', function(d, i){ return i === i1 }) - dispatch.active(i1, i) + dispatch.active.call(sections[0][i1], i1, i) i = i1 } @@ -155,4 +155,4 @@ function graphScroll() { d3.rebind(rv, dispatch, "on") return rv -} \ No newline at end of file +} From 19b77e25b820d6a745c46b037634b2ff2bf202df Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 16:02:29 -0500 Subject: [PATCH 3/8] Work in browser + nodejs possibly --- graph-scroll.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/graph-scroll.js b/graph-scroll.js index f6f8a99..6852582 100644 --- a/graph-scroll.js +++ b/graph-scroll.js @@ -1,3 +1,12 @@ +if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = graphScroll; + } + exports.graphScroll = graphScroll; +} else { + root.graphScroll = graphScroll; +} + function graphScroll() { var windowHeight, dispatch = d3.dispatch("scroll", "active"), From 6b6ff5b7b6012d7bfb73a4c0b9d2e77522ad6db0 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 16:08:24 -0500 Subject: [PATCH 4/8] add package --- .gitignore | 1 + package.json | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .gitignore create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..cf8202c --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "graph-scroll", + "version": "1.0.0", + "description": "experiments using scrolling instead of steppers", + "main": "graph-scroll.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/1wheel/graph-scroll.git" + }, + "author": "Adam Pearce", + "license": "MIT", + "bugs": { + "url": "https://github.com/1wheel/graph-scroll/issues" + }, + "homepage": "https://github.com/1wheel/graph-scroll#readme", + "dependencies": { + "d3": "^3.5.12" + } +} From 4afae350b441953651e56dd5a351357926e56337 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 16:13:30 -0500 Subject: [PATCH 5/8] Update package.json --- package.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index cf8202c..9dbe7ed 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,26 @@ { "name": "graph-scroll", - "version": "1.0.0", - "description": "experiments using scrolling instead of steppers", + "version": "0.0.1", + "description": "Simple scrolling events for d3 graphs", "main": "graph-scroll.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", - "url": "git+https://github.com/1wheel/graph-scroll.git" + "url": "https://github.com/1wheel/graph-scroll.git" }, + "keywords": [ + "d3", + "scroll", + "graph" + ], "author": "Adam Pearce", "license": "MIT", "bugs": { "url": "https://github.com/1wheel/graph-scroll/issues" }, - "homepage": "https://github.com/1wheel/graph-scroll#readme", + "homepage": "https://github.com/1wheel/graph-scroll", "dependencies": { "d3": "^3.5.12" } From f9d05e3f92edf12c8733472c6c4e59cd20cb52e5 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 16:15:00 -0500 Subject: [PATCH 6/8] root > this --- graph-scroll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph-scroll.js b/graph-scroll.js index 6852582..70508b9 100644 --- a/graph-scroll.js +++ b/graph-scroll.js @@ -4,7 +4,7 @@ if (typeof exports !== 'undefined') { } exports.graphScroll = graphScroll; } else { - root.graphScroll = graphScroll; + this.graphScroll = graphScroll; } function graphScroll() { From 97f0fed21de32e6622300be4f37fc96022c5614e Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 15 Jan 2016 16:18:23 -0500 Subject: [PATCH 7/8] wrap all in anon --- graph-scroll.js | 277 ++++++++++++++++++++++++------------------------ 1 file changed, 140 insertions(+), 137 deletions(-) diff --git a/graph-scroll.js b/graph-scroll.js index 70508b9..983e0cc 100644 --- a/graph-scroll.js +++ b/graph-scroll.js @@ -1,167 +1,170 @@ -if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = graphScroll; - } - exports.graphScroll = graphScroll; -} else { - this.graphScroll = graphScroll; -} - -function graphScroll() { - var windowHeight, - dispatch = d3.dispatch("scroll", "active"), - sections = d3.select('null'), - i = -1, - sectionPos = [], - n, - graph = d3.select('null'), - isFixed = null, - isBelow = null, - container = d3.select('body'), - containerStart = 0, - belowStart, - eventId = Math.random(), - stickyTop - - function reposition(){ - var i1 = 0 - sectionPos.forEach(function(d, i){ - if (d < pageYOffset - containerStart + 180) i1 = i - }) - i1 = Math.min(n - 1, i1) - if (i != i1){ - sections.classed('graph-scroll-active', function(d, i){ return i === i1 }) - - dispatch.active.call(sections[0][i1], i1, i) - - i = i1 - } - - var isBelow1 = pageYOffset > belowStart - 120 - if (isBelow != isBelow1){ - isBelow = isBelow1 - graph.classed('graph-scroll-below', isBelow) - } - - var isFixed1 = !isBelow && pageYOffset > containerStart - 120 - if (isFixed != isFixed1){ - isFixed = isFixed1 - graph - .classed('graph-scroll-fixed', isFixed) +(function(){ + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = graphScroll; } + exports.graphScroll = graphScroll; + } else { + this.graphScroll = graphScroll; + } - if (stickyTop){ - graph.style('padding-top', (isBelow || isFixed ? stickyTop : 0)+ 'px') + function graphScroll() { + var windowHeight, + dispatch = d3.dispatch("scroll", "active"), + sections = d3.select('null'), + i = -1, + sectionPos = [], + n, + graph = d3.select('null'), + isFixed = null, + isBelow = null, + container = d3.select('body'), + containerStart = 0, + belowStart, + eventId = Math.random(), + stickyTop + + function reposition(){ + var i1 = 0 + sectionPos.forEach(function(d, i){ + if (d < pageYOffset - containerStart + 180) i1 = i + }) + i1 = Math.min(n - 1, i1) + if (i != i1){ + sections.classed('graph-scroll-active', function(d, i){ return i === i1 }) + + dispatch.active.call(sections[0][i1], i1, i) + + i = i1 + } + + var isBelow1 = pageYOffset > belowStart - 120 + if (isBelow != isBelow1){ + isBelow = isBelow1 + graph.classed('graph-scroll-below', isBelow) + } + + var isFixed1 = !isBelow && pageYOffset > containerStart - 120 + if (isFixed != isFixed1){ + isFixed = isFixed1 + graph + .classed('graph-scroll-fixed', isFixed) + } + + if (stickyTop){ + graph.style('padding-top', (isBelow || isFixed ? stickyTop : 0)+ 'px') + } } - } - function resize(){ - sectionPos = [] - var startPos - sections.each(function(d, i){ - if (!i) startPos = this.getBoundingClientRect().top - sectionPos.push(this.getBoundingClientRect().top - startPos) }) + function resize(){ + sectionPos = [] + var startPos + sections.each(function(d, i){ + if (!i) startPos = this.getBoundingClientRect().top + sectionPos.push(this.getBoundingClientRect().top - startPos) }) - var containerBB = container.node().getBoundingClientRect() - var graphBB = graph.node().getBoundingClientRect() + var containerBB = container.node().getBoundingClientRect() + var graphBB = graph.node().getBoundingClientRect() - containerStart = containerBB.top + pageYOffset - belowStart = containerBB.bottom - graphBB.height + pageYOffset - } + containerStart = containerBB.top + pageYOffset + belowStart = containerBB.bottom - graphBB.height + pageYOffset + } - function keydown() { - if (!isFixed) return - var delta - switch (d3.event.keyCode) { - case 39: // right arrow - if (d3.event.metaKey) return - case 40: // down arrow - case 34: // page down - delta = d3.event.metaKey ? Infinity : 1 ;break - case 37: // left arrow - if (d3.event.metaKey) return - case 38: // up arrow - case 33: // page up - delta = d3.event.metaKey ? -Infinity : -1 ;break - case 32: // space - delta = d3.event.shiftKey ? -1 : 1 - ;break - default: return + function keydown() { + if (!isFixed) return + var delta + switch (d3.event.keyCode) { + case 39: // right arrow + if (d3.event.metaKey) return + case 40: // down arrow + case 34: // page down + delta = d3.event.metaKey ? Infinity : 1 ;break + case 37: // left arrow + if (d3.event.metaKey) return + case 38: // up arrow + case 33: // page up + delta = d3.event.metaKey ? -Infinity : -1 ;break + case 32: // space + delta = d3.event.shiftKey ? -1 : 1 + ;break + default: return + } + + var i1 = Math.max(0, Math.min(i + delta, n - 1)) + rv.scrollTo(i1) + + d3.event.preventDefault() } - var i1 = Math.max(0, Math.min(i + delta, n - 1)) - rv.scrollTo(i1) - d3.event.preventDefault() - } + var rv ={} + rv.scrollTo = function(_x){ + if (isNaN(_x)) return rv - var rv ={} + d3.select(document.documentElement) + .interrupt() + .transition() + .duration(500) + .tween("scroll", function() { + var i = d3.interpolateNumber(pageYOffset, sectionPos[_x] + containerStart) + return function(t) { scrollTo(0, i(t)) } + }) + return rv + } - rv.scrollTo = function(_x){ - if (isNaN(_x)) return rv - d3.select(document.documentElement) - .interrupt() - .transition() - .duration(500) - .tween("scroll", function() { - var i = d3.interpolateNumber(pageYOffset, sectionPos[_x] + containerStart) - return function(t) { scrollTo(0, i(t)) } - }) - return rv - } + rv.container = function(_x){ + if (!_x) return container + container = _x + return rv + } - rv.container = function(_x){ - if (!_x) return container + rv.graph = function(_x){ + if (!_x) return graph - container = _x - return rv - } + graph = _x + return rv + } - rv.graph = function(_x){ - if (!_x) return graph + rv.eventId = function(_x){ + if (!_x) return eventId - graph = _x - return rv - } + eventId = _x + return rv + } - rv.eventId = function(_x){ - if (!_x) return eventId + rv.stickyTop = function(_x){ + if (!_x) return stickyTop - eventId = _x - return rv - } + stickyTop = _x + return rv + } - rv.stickyTop = function(_x){ - if (!_x) return stickyTop + rv.sections = function (_x){ + if (!_x) return sections - stickyTop = _x - return rv - } + sections = _x + n = sections.size() - rv.sections = function (_x){ - if (!_x) return sections + d3.select(window) + .on('scroll.gscroll' + eventId, reposition) + .on('resize.gscroll' + eventId, resize) + .on('keydown.gscroll' + eventId, keydown) - sections = _x - n = sections.size() + resize() + d3.timer(function() { + reposition() + return true + }) - d3.select(window) - .on('scroll.gscroll' + eventId, reposition) - .on('resize.gscroll' + eventId, resize) - .on('keydown.gscroll' + eventId, keydown) + return rv + } - resize() - d3.timer(function() { - reposition() - return true - }) + d3.rebind(rv, dispatch, "on") return rv } - - d3.rebind(rv, dispatch, "on") - - return rv -} + +}).call(this) \ No newline at end of file From 92bf66bdce1d4cc83135c6422f0047b2e588058a Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 19 Feb 2016 17:47:11 -0500 Subject: [PATCH 8/8] add middle scroll option; direction;fix sticky top --- README.md | 13 +++++++---- graph-scroll.js | 60 ++++++++++++++++++++++++++++++++++++++++++++----- index.html | 16 ++++++++----- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c5c06ae..91b6788 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,20 @@ Simple scrolling events for [d3](https://github.com/mbostock/d3) graphs. Based o ### [Demo/Documentation](http://1wheel.github.io/graph-scroll/) -`graphScroll` takes a selection of explanatory text sections and dispatches `active` events as different sections are scrolled into to view. These `active` events can be used to update a chart's state. +`graphScroll` takes a selection of explanatory text sections and dispatches `active` events as different sections are scrolled into to view. These `active` events can be used to update a chart's state. + +A `direction` is also passed on active. It is either `up`, `down` or `jump` (fired when you load a page from not at the time, like when you refresh). + +Set an `offset` to determine how far from the top the next section will be before it becomes active. ```js graphScroll() .sections(d3.selectAll('#sections > div')) - .on('active', function(i){ console.log(i + 'th section active') }) + .offset(180) + .on('active', function(i, direction){ console.log(i + 'th section active', 'Moving ' + direction) }) ``` -The top most element scrolled fully into view is classed `graph-scroll-active`. This makes it easy to highlight the active section with css: +You can also set `.triggerAt('middle')` to activate the next section when it reaches the middle of the viewport. The top most element scrolled fully into view is classed `graph-scroll-active`. This makes it easy to highlight the active section with css: ```css #sections > div{ @@ -47,7 +52,7 @@ graphScroll() .graph(d3.selectAll('#graph')) .container(d3.select('#container')) .sections(d3.selectAll('#sections > div')) - .on('active', function(i){ console.log(i + 'th section active') }) + .on('active', function(i, direction){ console.log(i + 'th section active', 'Moving ' + direction) }) ``` diff --git a/graph-scroll.js b/graph-scroll.js index 983e0cc..f390197 100644 --- a/graph-scroll.js +++ b/graph-scroll.js @@ -1,6 +1,8 @@ (function(){ + var d3 = this.d3 || undefined if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { + d3 = require('d3') exports = module.exports = graphScroll; } exports.graphScroll = graphScroll; @@ -22,38 +24,70 @@ containerStart = 0, belowStart, eventId = Math.random(), - stickyTop + stickyTop, + lastPageY = -Infinity, + triggerAt = 'top', + offset = 0; function reposition(){ var i1 = 0 + var viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + + // If our lastPageY variable is our default but we're farther than the scroll top, then we are entering the page from somewhere not the top + if (lastPageY == -Infinity && pageYOffset > 0) { + direction = 'jump' + } else if (pageYOffset > lastPageY) { + direction = 'down' + } else { + direction = 'up' + } + sectionPos.forEach(function(d, i){ - if (d < pageYOffset - containerStart + 180) i1 = i + // Trigger active section when it gets to the middle of the viewport + if (triggerAt == 'middle' && d < (pageYOffset - containerStart + viewportHeight / 2 + offset) ) { + i1 = i + // Or at the top of the viewport + } else if (triggerAt == 'top' && d < pageYOffset - containerStart + offset) { + i1 = i + } }) + i1 = Math.min(n - 1, i1) if (i != i1){ sections.classed('graph-scroll-active', function(d, i){ return i === i1 }) - dispatch.active.call(sections[0][i1], i1, i) + dispatch.active.call(sections[0][i1], i1, direction) i = i1 } + var yStickyOffset = stickyTop || 0 - var isBelow1 = pageYOffset > belowStart - 120 + var isBelow1 = pageYOffset + yStickyOffset > belowStart if (isBelow != isBelow1){ isBelow = isBelow1 graph.classed('graph-scroll-below', isBelow) } - var isFixed1 = !isBelow && pageYOffset > containerStart - 120 + var isFixed1 = !isBelow && pageYOffset > containerStart - yStickyOffset if (isFixed != isFixed1){ isFixed = isFixed1 graph .classed('graph-scroll-fixed', isFixed) } + var top if (stickyTop){ - graph.style('padding-top', (isBelow || isFixed ? stickyTop : 0)+ 'px') + if (isBelow) { + top = 'auto' + } else if (isFixed) { + top = stickyTop + 'px' + } else { + top = '0px' + } + graph.style('top', top) } + + lastPageY = pageYOffset } function resize(){ @@ -128,6 +162,13 @@ return rv } + rv.triggerAt = function(_x){ + if (!_x) return triggerAt + + triggerAt = _x + return rv + } + rv.eventId = function(_x){ if (!_x) return eventId @@ -142,6 +183,13 @@ return rv } + rv.offset = function(_x){ + if (!_x) return offset + + offset = _x + return rv + } + rv.sections = function (_x){ if (!_x) return sections diff --git a/index.html b/index.html index 1ddfb4d..e33354e 100644 --- a/index.html +++ b/index.html @@ -82,19 +82,21 @@

Simple scrolling events for d3 graphics.

Connect text and graphics

graph-scroll - takes a selection of explanatory text sections and dispatches active events as different sections are scrolled into to view. These active events are used to update a graph's state. + takes a selection of explanatory text sections and dispatches active events as different sections are scrolled into to view. These active events are used to update a graph's state.

A direction is also passed on active. It is either up, down or jump (fired when you load a page from not at the time, like when you refresh).

Set an offset to determine how far from the top the next section will be before it becomes active.
 graphScroll()
   .sections(d3.selectAll('#sections > div'))
-  .on('active', function(i){
-    console.log(i + 'th section active') })
+  .offset(180)
+  .on('active', function(i, direction){
+    console.log(i + 'th section active')
+    console.log('Moving ' + direction)
       

Highlight active text

- The top most text section scrolled into view is classed graph-scroll-active. This makes it easy to highlight the active section with css: + You can also set .triggerAt('middle') to activate the next section when it reaches the middle of the viewport. The top most text section scrolled into view is classed graph-scroll-active. This makes it easy to highlight the active section with css:
 #sections > div{
   opacity: .3
@@ -234,7 +236,9 @@ 

contribute/view .container(d3.select('#container')) .graph(d3.selectAll('#graph')) .sections(d3.selectAll('#sections > div')) - .on('active', function(i){ + .offset(180) + .triggerAt('top') + .on('active', function(i, direction){ var pos = [ {cx: width - r, cy: r}, {cx: r, cy: r}, {cx: width - r, cy: height - r}, @@ -257,7 +261,7 @@

contribute/view .container(d3.select('#container2')) .graph(d3.selectAll('#graph2')) .sections(d3.selectAll('#sections2 > div')) - .on('active', function(i){ + .on('active', function(i, direction){ var h = height var w = width var dArray = [