d3.js - reset position variable per sentences

Viewed 41

This sample draw sentence line by line. the position variable currently always increase, how to reset it to 10 when new sentence start!

console.clear()
var data = `Hello word,d3.js.
Drawing with d3 is funny! 
I like to play with it.`

var root = []
var sentences = data.split('\n')
sentences.forEach(d => {
  root.push(d.split(' '))
})

var svg = d3.select('body').append('svg')
.attr('width',300)
.attr('hegith',300)
.style('border','5px solid red')

var pos = 10
var gbox = d3.local()
var lines = svg.selectAll('lines')
.data(root).enter()
.append('g')
.attr('transform',(d,i) => {
  return `translate(0,${i*20})`
})

var words = lines.selectAll('text')
.data(d => d).enter()
.append('text')
.text(d => d)
.attr('fill','black')
.each((d,i,n) => {
  var bbox = d3.select(n[i]).node().getBBox()
  gbox.set(n[i],bbox)
})
.attr('x',(d,i,n) => {
  var bbox = gbox.get(n[i])
  var curr = pos
  pos += bbox.width + 2
  return curr
})
.attr('y',20)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

2 Answers

With minimal changes to your code, if you set the max number of lines before resetting (here maxNumber) you can get the index of the parent group (here thisIndex) and use the remainder operator to set the pos variable:

pos = !((thisIndex + 1) % maxNumber) ? 10 : pos + bbox + 2

Here is your code using 5 as the maximum:

console.clear()
var data = `Foo
Bar
Baz
Foo
Bar
Baz
Foo
Bar
Baz
Foo
Bar
Baz
Foo
Bar
Baz`

var root = []
var sentences = data.split('\n')
sentences.forEach(d => {
  root.push(d.split(' '))
})

var svg = d3.select('body').append('svg')
  .attr('width', 300)
  .attr('hegith', 300)
  .style('border', '5px solid red')

var pos = 10
var maxNumber = 5
var gbox = d3.local()
var index = d3.local()
var lines = svg.selectAll('lines')
  .data(root).enter()
  .append('g')
  .attr('transform', (d, i, n) => {
    index.set(n[i], i);
    return `translate(0,${i*15})`
  })
var words = lines.selectAll('text')
  .data(d => d).enter()
  .append('text')
  .text(d => d)
  .attr('fill', 'black')
  .attr('x', (_, i, n) => {
    var bbox = n[i].parentNode.previousSibling ? n[i].parentNode.previousSibling.firstChild.getComputedTextLength() : n[i].getComputedTextLength();
    var curr = pos
    var thisIndex = index.get(n[i])
    pos = !((thisIndex + 1) % maxNumber) ? 10 : pos + bbox + 2
    return curr
  })
  .attr('y', 20)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

Not related, here I'm changing that getBBox() in your code for a getComputedTextLength().

If index changed, then reset current position.

console.clear()

console.clear()
var data = `Hello word,d3.js.
Drawing with d3 is funny! 
I like to play with it.`

var root = []
var sentences = data.split('\n')
sentences.forEach(d => {
  root.push(d.split(' '))
})

var svg = d3.select('body').append('svg')
.attr('width',300)
.attr('hegith',300)
.style('border','5px solid red')

var pos = 10
var index = d3.local()
var lines = svg.selectAll('lines')
.data(root).enter()
.append('g')
.attr('transform',(d,i,n) => {
  index.set(n[i], i);
  return `translate(0,${i*20})`
})

var prevIdx = -1
var maxNumber = 5
var words = lines.selectAll('text')
.data(d => d).enter()
.append('text')
.text(d => d)
.attr('fill','black')
.attr('x',(d,i,n) => {
  var thisIndex = index.get(n[i])
  if (prevIdx != -1) {
    if (prevIdx != thisIndex) {
      pos = 10
    }
  }
  prevIdx = thisIndex
  var curr = pos
  var w = d3.select(n[i]).node().getComputedTextLength()
  pos += w + 2
  return curr
})
.attr('y',20)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

Related