Multipage pagination/scrolling/stacking or sliding applied across diverse svg artifacts

957 views
Skip to first unread message

Thug

unread,
Jul 14, 2011, 8:44:52 AM7/14/11
to d3...@googlegroups.com
Hi all

Paging/scrolling/sliding/stacking

Despite potentially wide application, pagination/scrolling/stacking/sliding of pages does not seem to have attracted much attention yet in the forum. Searches for these or (external) examples of CSS3 paging transitions suggested in https://groups.google.com/forum/?hl=en#!topic/d3-js/vs7_FlNIHLk have so far more or less drawn a blank. Many existing slider/pagination libraries seem primarily more concerned with feats of black-box juggling than with continued data transparency.

Object positioning

SVG objects such as lines and paths are often scaled or indexed differently. Path positioning is, for example, often governed by transitions working on an index rather than (say) the linear scales used to place tick lines. How best are could they be harmonised?

Range limits and paging

Mutual limits on the visible and total ranges would, for effective scrolling or paging or individual translations, seem to be a must. If viable, how would they interact?

                                                                               ~ ~ + ~ ~

Though it doesn't touch on paging or scrolling, I've found the following example suggestive of solutions in at least some of the above respects : https://github.com/mbostock/d3/blob/master/examples/dot/dot.html. In practice, however, when I come to pagination, I keep painting myself into a corner.

Brainstorming

Rather than labouring alone on this, some gathered "best practice" guidelines and relevant examples might make more sense. Can you help? These might touch on:
  • easily applied d3-native, CSS3 and other pagination/stacking/scrolling/sliding mechanisms allowing sequential presentation of a data range, but leaving each DOM object's properties fully exposed
  • the issue of visible versus total data ranges, and how best to handle them
  • the establishment of mutual range limits and addressing across different types of svg artifact
  • relative speed of execution
Background

My particular focus at the moment is in the area of multi-page timelines exploiting multiple svg artifacts and positioning techniques.

As a focus for discussion, in the following (a display recording gaming wins), I've set up a series of vertical "time_interval" lines on a horizontal chart, the number of these shown being limited by  "x_vis_intervals". I can then map winnings ("tokens_horiz") onto this.

Though parent data and scales generally seem accessible to HTML DOM children, here,  tokens_horiz ignores .data(x_vis_tokens.ticks(max_tokens_vis)) definition in the containing g tag, with the result that tokens shoot happily off the right edge of the screen...

Another is that the larger (read "entire") data range (let's call it x_total_intervals) is far greater than that used here and actually seen (x_vis_intervals). Though not implemented here, experiment suggests I can define two linear scales, one for the entire, background data range and one for the visible subsection only. The former would be applied to the containing definitions, the latter to the actual screen representations.

  game_horiz = player.selectAll("g.x_game")
.data(x_vis_tokens.ticks(max_tokens_vis))
.enter()
.append("svg:g")
.attr("class", "x_game");

var time_interval = game_horiz.append("svg:g")
.attr("class", function(d,i) {
return "time_interval" + i.toString()
});

// Vertical timelines
time_interval.append("svg:line")
.attr("class", "barline" )
.attr("x1", x_vis_intervals)
.attr("x2", x_vis_intervals)
.attr("y1", off_game_margin)
.attr("y2", off_game_margin + game_height_vis)
.attr("stroke", "#999");

// Success tokens
var tokens_horiz = game_horiz.append("svg:g")
.attr("class", function(d,i) {
return i.toString()
});

tokens_horiz.append("svg:path") 
.attr("class", function(d, i) {
return "note." + i.toString();
})
.attr("d", function(d, i) {
return (all_token_glyphs[i].token)
})
.attr("transform", function(d, i) {
......
});

Insights, examples and suggestions appreciated. :-)

Thanks
Thug

Thug

unread,
Jul 14, 2011, 2:00:39 PM7/14/11
to d3...@googlegroups.com
In retrospect, as a functioning demo showing both svg paths and lines integrated with a mutually shared scale, the dots example  https://github.com/mbostock/d3/blob/master/examples/dot/dot.html would -with a much extended (and hidden) x axis range- form the best basis for a discussion on paging/scrolling. Please ignore my own example above.

What interests me is, where extended in this way, how features such as paging or scrolling might best be approached in d3.

The triggering of paging or scrolling through event handling is far less a problem than understanding how d3 might best be used to update the display with little or no latency.

Thanks and regards
Thug

Thug

unread,
Jul 21, 2011, 4:02:06 PM7/21/11
to d3...@googlegroups.com
Hi all

As touched on a little clumsily above, I've been trying to merge the bar chart and dots examples, the idea being that -for the latter- instead of just one "static" page of dots, several would be queued from the right - analogously to the bar chart example, except that they would all be present on start of the animation, rather than being generated on the fly.

I envisage this ultimately being triggered -and paused- by mouseclick, with automatic, timer-controlled page advance.

The crux of the problem is to replace the simple rect with more complex "dot" structures in two critical blocks of code imported to the dots example from the bar chart example:

1)    var t = 1297110663,
 v = 70,
 data = d3.range(33).map(next);
 function next() {
  return {
    time: ++t,
    value: v = ~~Math.max(10, Math.min(90, v + 10 * (Math.random() - .5)))
  };
} 
:

2)       redraw();
 
function redraw() {
 
  var rect = chart3.selectAll("rect")
      .data(data, function(d) { return d.time; });
 
  rect.enter().insert("svg:rect", "line") 
:
-obsolete code removed- 
 :
    .transition()
      .duration(1000)
      .attr("x", function(d, i) { return x(i) - .5; });
 
  rect.transition()
      .duration(1000)
      .attr("x", function(d, i) { return x(i) - .5; });
 
  rect.exit().transition()
      .duration(1000)
      .attr("x", function(d, i) { return x(i - 1) - .5; })
      .remove();
}

setInterval(function() {
  data.shift();
  data.push(next());
  redraw();
}, 1500);

Simple as this may seem:
  1. as their indexes apply to different ranges, next()'s activities need to be well separated from those of redraw() - the former handling update from the right, while the latter is taxed with presentation of the current rectangle of dots.
  2. as the full dots data structure exists in advance (instead of being periodically added to the far end), redraw() must stop on reaching it's end.
  3. the contributing x-axis-related component structures differ quite radically between the two source examples
I've now made several uniformly unsuccessful trial implementations for the second of the two desired behaviours, and would certainly welcome other's thoughts or suggestions.

Incidentally, implementation of forward and backward paging using keyboard events linked to +ve and -ve transitions acting on the background range of dots turned out to be trivial, and so -at least for the purposes of this thread- can be ignored.

Thanks
Thug
 

Thug

unread,
Jul 28, 2011, 3:18:04 PM7/28/11
to d3...@googlegroups.com

Hi all

Over the last two weeks I've made many, many attempts at combining the bar chart and dots examples.

The cleanest approach attempted was -instead of a painstaking code merge - to convert the dots code from an html to a js script, and then to "hang" it's contents onto svg:g elements replacing the bar chart blocks. The nitty gritty of the slightly modified bar chart code is as follows:

:
var container = chart.selectAll("container")
    .data(bar_data)
  .enter().append("svg:g")
  .attr("class", "container")
    .attr("x", function(d, i) { return x(i) - .5; })
    .attr("y", function(d) { return h - y(d.value) - .5; })
    .attr("width", w)
    .attr("height", function(d) { return y(d.value); });

    get_payload(pageCount, w, h, container);

redraw();
 
function redraw() {
  pageCount++;
  var container = chart.selectAll("container")
      .data(bar_data, function(d) { return d.time; });
 
  container.enter().insert("svg:g")
  .attr("class", "container")
      .attr("x", function(d, i) { return x(i + 1) - .5; })
      .attr("y", function(d) { return h - y(d.value) - .5; })
      .attr("width", w)
      .attr("height", function(d) { return y(d.value); })
    .transition()
      .duration(1000)
      .attr("x", function(d, i) { return x(i) - .5; });

  getPayload(page_count, w, h, container);
  container.transition()
      .duration(1000)
      .attr("x", function(d, i) { return x(i) - .5; });
:
: 
}

getPayload()  just adds the dot element structures to the bar chart's container element, which is passed in as carrier. The dots w and h definitions are replaced by those of the bar chart:

function getPayload(pageCount, w, h, carrier){
var dotsData = d3.range(100).map(function(i) {
  return {x: i / 99, y: Math.random()};
});

var x = d3.scale.linear().range([0, w]),
    y = d3.scale.linear().range([h, 0]),
    symbol = d3.scale.ordinal().range(d3.svg.symbolTypes),
    color = d3.scale.category10();

// Hidden x grid
var xrule = carrier.selectAll("g.x")
    .data(x.ticks(10))
  .enter().append("svg:g")
    .attr("class", "x");
:
// Hidden y grid
var yrule = carrier.selectAll("g.y")
    .data(y.ticks(10))
  .enter().append("svg:g")
    .attr("class", "y");
:
    // dots
    carrier.selectAll("path.dot")
    .data(dotsData)
    .enter().append("svg:path")
    .attr("class", "dot")
    .attr("stroke", function(d, i) { return color(i); })
    .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; })
    .attr("d", d3.svg.symbol()
    .type(function(d, i) { return symbol(i); }));

// Symbol numbers
carrier.selectAll("path.num")
.data(dotsData)
.enter().append("svg:text")
.attr("class", "num")
.attr("stroke", function(d, i) { return color(i); })
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; })
.text( function(d, i) { return i.toString(); });
}

Nevertheless, I suspect a call to getPayload() may not be the best way to hang these "foreign" elements onto an existing svg:g block: has anyone a better (more d3-oriented) suggestion?

The initial data display (nominally dependent on the data length) does not work : the dots display, in as far as they are created, are overlaid. This is clearly because the dots positioning should be relative to the current container, and not the screen. I'm unclear about how best to achieve this. I tried to reposition the dots elements using an offset built using page_count and width applied to all horizonal positionings, but this -rather surprisingly- had no effect.

Then, only those dots pages configured (num_pages_vis) in

bar_data = d3.range(num_pages_vis).map(next); );

are being generated: later calls to redraw() no longer work. I would have expected at least remove() to have effect, but no...

Any suggestions are welcome. 
Thug


Bill White

unread,
May 17, 2014, 5:20:38 PM5/17/14
to d3...@googlegroups.com
I used Jason Davie's longscroll.js plugin to create a paging visualization that I detailed here for those who are interested.
Reply all
Reply to author
Forward
0 new messages