Why some cells do not move entirely

I installed this jsfiddle: http://jsfiddle.net/386er/dhzq6q6f/14/

var moveCell = function(direction) { var cellToBeMoved = pickRandomCell(); var currentX = cellToBeMoved.x.baseVal.value; var currentY = cellToBeMoved.y.baseVal.value; var change = getPlusOrMinus() * (cellSize + 1 ); var newX = currentX + change; var newY = currentY + change; var selectedCell = d3.select(cellToBeMoved); if (direction === 'x') { selectedCell.transition().duration(1500) .attr('x', newX ); } else { selectedCell.transition().duration(1500) .attr('y', newY ); } 

}

In the moveCell function, I select a random cell, request its current x and y coordinates, and then add or subtract its width or height to move it to the neighboring cell.

What is interesting to me: if you observe the movement of cells, some will only partially move to the next cell. Can anoyne tell me why this is so?

+6
source share
1 answer

The first thing to do in this situation is to put .each("interrupt", function() { console.log("interrupted!") }); to your transitions. Then you will see the problem.
It is supposed to be corrected if you named transitions of type selection.transition("name") , but this is not corrected.
This means that you should do as @jcuenod suggested and exclude those that are moving. One way to do what is idiomatic is this ...

 if (direction === 'x') { selectedCell.transition("x").duration(1500) .attr('x', newX) .each("start", function () { lock.call(this, "lockedX") }) .each("end", function () { unlock.call(this, "lockedX") }); } else { selectedCell.transition("y").duration(1500) .attr('y', newY) .each("start", function () { lock.call(this, "lockedX") }) .each("end", function () { unlock.call(this, "lockedX") }); } function lock(lockClass) { var c = { cell: false }; c[lockClass] = true; d3.select(this).classed(c) }; function unlock(lockClass) { var c = { cell: this.classList.length == 1 }; c[lockClass] = false; d3.select(this).classed(c); }; 

Here is a violin to prove the concept.


Pure and idiomatic version of d3

Just for completeness, there is a way for d3 to do this.
I tried to make it as idiomatic as possible. Basic moments...

  • Pure data management
    The data is updated, and viz-manipulation is completely left on d3 ads.
  • Use d3 to detect and accept changes to svg element attributes
    This is done using the combined key function in the selection.data() method and using the fact that the changed nodes (squares, where the x , y or fill attributes do not correspond to the updated data) are captured by the output selection.
  • Split changed elements in the data array, so d3 can detect changes
    Since the link to the elements of the data array is bound to the elements of the DOM, any change to the data will also be reflected in selection.datum() . d3 uses the key function to compare data values โ€‹โ€‹with datum to classify nodes as updating, input, or output. If key is executed, that is, a function of data / data values, data changes will not be detected. By splice changing the changes to the data array, the value referenced by selection.datum() will be different from the data array, so data changes will mark the output nodes.
    By simply manipulating the attributes and placing the transitions on the choice of output, rather than deleting it, it essentially becomes a โ€œchangedโ€ choice.
    this only works if the data values โ€‹โ€‹are objects.
  • Parallel transitions
    Named transitions are used to ensure that the transitions x and y do not interrupt each other, but you must also use the attributes of the tag class to block the transition elements. This is done using the start and end transition events.
  • Animation frames
    d3.timer used to smooth the animation and resources of the marshal. d3Timer refers to updating data before updating transitions before each animation frame.
  • Use d3.scale.ordinal() to control positioning
    This is great because you do it every time, and you donโ€™t even have to cheat it.

 $(function () { var container, svg, gridHeight = 800, gridWidth = 1600, cellSize, cellPitch, cellsColumns = 100, cellsRows = 50, squares, container = d3.select('.svg-container'), svg = container.append('svg') .attr('width', gridWidth) .attr('height', gridHeight) .style({ 'background-color': 'black', opacity: 1 }), createRandomRGB = function () { var red = Math.floor((Math.random() * 256)).toString(), green = Math.floor((Math.random() * 256)).toString(), blue = Math.floor((Math.random() * 256)).toString(), rgb = 'rgb(' + red + ',' + green + ',' + blue + ')'; return rgb; }, createGrid = function (width, height) { var scaleHorizontal = d3.scale.ordinal() .domain(d3.range(cellsColumns)) .rangeBands([0, width], 1 / 15), rangeHorizontal = scaleHorizontal.range(), scaleVertical = d3.scale.ordinal() .domain(d3.range(cellsRows)) .rangeBands([0, height]), rangeVertical = scaleVertical.range(), squares = []; rangeHorizontal.forEach(function (dh, i) { rangeVertical.forEach(function (dv, j) { var indx; squares[indx = i + j * cellsColumns] = { x: dh, y: dv, c: createRandomRGB(), indx: indx } }) }); cellSize = scaleHorizontal.rangeBand(); cellPitch = { x: rangeHorizontal[1] - rangeHorizontal[0], y: rangeVertical[1] - rangeVertical[0] } svg.selectAll("rect").data(squares, function (d, i) { return d.indx }) .enter().append('rect') .attr('class', 'cell') .attr('width', cellSize) .attr('height', cellSize) .attr('x', function (d) { return dx }) .attr('y', function (d) { return dy }) .style('fill', function (d) { return dc }); return squares; }, choseRandom = function (options) { options = options || [true, false]; var max = options.length; return options[Math.floor(Math.random() * (max))]; }, pickRandomCell = function (cells) { var l = cells.size(), r = Math.floor(Math.random() * l); return l ? d3.select(cells[0][r]).datum().indx : -1; }; function lock(lockClass) { var c = { cell: false }; c[lockClass] = true; d3.select(this).classed(c) }; function unlock(lockClass) { var c = { cell: this.classList.length == 1 }; c[lockClass] = false; d3.select(this).classed(c); }; function permutateColours() { var samples = Math.min(50, Math.max(~~(squares.length / 50),1)), s, ii = [], i, k = 0, cells = d3.selectAll('.cell'); while (samples--) { do i = pickRandomCell(cells); while (ii.indexOf(i) > -1 && k++ < 5 && i > -1); if (k < 10 && i > -1) { ii.push(i); s = squares[i]; squares.splice(i, 1, { x: sx, y: sy, c: createRandomRGB(), indx: s.indx }); } } } function permutatePositions() { var samples = Math.min(20, Math.max(~~(squares.length / 100),1)), s, ss = [], d, m, p, k = 0, cells = d3.selectAll('.cell'); while (samples--) { do s = pickRandomCell(cells); while (ss.indexOf(s) > -1 && k++ < 5 && s > -1); if (k < 10 && s > -1) { ss.push(s); d = squares[s]; m = { x: dx, y: dy, c: dc, indx: d.indx }; m[p = choseRandom(["x", "y"])] = m[p] + choseRandom([-1, 1]) * cellPitch[p]; squares.splice(s, 1, m); } } } function updateSquares() { //use a composite key function to transform the exit selection into // an attribute update selection //because it the exit selection, d3 doesn't bind the new data // that done manually with the .each var changes = svg.selectAll("rect") .data(squares, function (d, i) { return d.indx + "_" + dx + "_" + dy + "_" + dc; }) .exit().each(function (d, i, j) { d3.select(this).datum(squares[i]) }) changes.transition("x").duration(1500) .attr('x', function (d) { return dx }) .each("start", function () { lock.call(this, "lockedX") }) .each("end", function () { unlock.call(this, "lockedX") }) changes.transition("y").duration(1500) .attr('y', function (d) { return dy }) .each("start", function () { lock.call(this, "lockedY") }) .each("end", function () { unlock.call(this, "lockedY") }); changes.attr("stroke", "white") .style("stroke-opacity", 0.6) .transition("fill").duration(800) .style('fill', function (d, i) { return dc }) .style("stroke-opacity", 0) .each("start", function () { lock.call(this, "lockedFill") }) .each("end", function () { unlock.call(this, "lockedFill") }); } squares = createGrid(gridWidth, gridHeight); d3.timer(function tick() { permutateColours(); permutatePositions(); updateSquares(); }); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <div class="svg-container"></div> 

NOTE. To perform position transitions, version d3 version 3.5.5 is required.

EDIT: fixed lock and lock issue. Most likely, it is better to mark the data, rather than writing classes in the DOM, but anyway ... so much fun.

+3
source

Source: https://habr.com/ru/post/987494/


All Articles