En d3
, podemos cambiar el orden de los elementos en una selección, por ejemplo, usando raise
.
Sin embargo, cuando volvemos a vincular los datos y usamos join
, este orden se descarta.
Esto no sucede cuando usamos "la forma antigua" de vincular datos, usando enter
y merge
.
Vea el siguiente violín donde puede hacer clic en un círculo (por ejemplo, el azul) para traerlo al frente. Cuando haces clic en " redibujar ", los círculos vuelven a su orden z original cuando usas join
, pero no cuando usas enter
y merge
.
¿Puedo lograr que los círculos mantengan su orden z y aún usen join
?
const data = [{ id: 1, v: 10, c: 'red' }, { id: 2, v: 30, c: 'blue' }, { id: 3, v: 60, c: 'green' }] let nDrawCall = 0 function redraw() { nDrawCall++ //svg1 with old enter-merge pattern that works const circles = d3.select('#svg1') .selectAll('circle') .data(data, d => d.id) circles .enter() .append('circle') .on('click', function() { d3.select(this).raise() }) .merge(circles) .attr('cx', d => dv * nDrawCall) .attr('cy', d => dv) .attr('r', d => dv) .attr('fill', d => dc) //svg2 with new join pattern that sadly reorders d3.select('#svg2') .selectAll('circle') .data(data, d => d.id) .join(enter => enter .append('circle') .on('click', function() { d3.select(this).raise() }) ) .attr('cx', d => dv * nDrawCall) .attr('cy', d => dv) .attr('r', d => dv) .attr('fill', d => dc) } function reset() { nDrawCall = 0 redraw() } redraw() /* while (true) { iter++ console.log(iter) sleepFor(500) } */
svg { height: 100px; width: 100%; }
<html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script> </head> <body> <button onclick="redraw()"> Redraw </button> <button onclick="reset()"> Reset </button> <div> <svg id="svg1" /> <svg id="svg2" /> </div> </body> </html>
join
realiza un order
implícito después de fusionar la selección enter y update, consulte https://github.com/d3/d3-selection/blob/91245ee124ec4dd491e498ecbdc9679d75332b49/src/selection/join.js#L14 .
El orden de selección después del enlace de datos en su ejemplo sigue siendo rojo, azul, verde incluso si se cambia el orden del documento. Entonces, los círculos se reordenan al orden original usando join
.
Puede evitar eso cambiando el enlace de datos que refleja el cambio en el orden del documento. Lo hice aquí, moviendo el dato del círculo en el que se hizo clic al final de la matriz de datos.
let data = [{ id: 1, v: 10, c: 'red' }, { id: 2, v: 30, c: 'blue' }, { id: 3, v: 60, c: 'green' }] let nDrawCall = 0 function redraw() { nDrawCall++ d3.select('#svg2') .selectAll('circle') .data(data, d => d.id) .join(enter => enter .append('circle') .on('click', function() { const circle = d3.select(this).raise(); data.push(data.splice(data.indexOf(circle.datum()), 1)[0]); }) ) .attr('cx', d => dv * nDrawCall) .attr('cy', d => dv) .attr('r', d => dv) .attr('fill', d => dc) } function reset() { nDrawCall = 0 redraw() } redraw()
svg { height: 100px; width: 100%; }
<html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script> </head> <body> <button onclick="redraw()"> Redraw </button> <button onclick="reset()"> Reset </button> <div> <svg id="svg2" /> </div> </body> </html>