Д3 объясняя база-10 анимация нотации


Это мой первый код Н3. Я самоучка, поэтому я не уверен, что цепочки перехода соответствует оптимальной общей практики. Также, есть ли способ, чтобы избежать дублирования "группа" и "аним", сохраняя при этом эффект скольжения я (ср. add 4 time the text "you choose : abc")?

<!DOCTYPE html>
<meta charset="utf-8">
<title>Input test</title>
 <div id="div1"> 

<form onSubmit="return false;">
  Choose a number less than 100.000: <input type="text" name="number" id="number"><br>

  </form>
  <svg width="900" height="300" id="pack1">
  </svg>

</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>

<script>
// get user number and start anim()
var inputElem = d3.select("#number");
inputElem.on("change", anim);

function anim() {
// remove previous animation
d3.selectAll("g").remove();

var number= d3.select(this).property("value");

if (number<100000){
    // general var
    var color = ["red","blue","orange", "green", "darkkhaki"]   



    //  get choices from user : number saved as array 
    var number_as_array = number.toString().split(''); 


    // add 4 time the text "you choose  : abc" 
    for (var i=0; i<4; i++){
    d3.select("svg").append('g')
        .attr('id', function(){return "group"+i})
        .append("text")
        .attr('x', 10)
        .attr('y', 30)
        .attr("font-family", "sans-serif")
        .attr("font-size", "20px")
        .attr("fill", "black")
        .html(function(){
                var text=["You choose :"]
                for (var index=0; index< number_as_array.length; index++){
                var x_position = (index+1)*30+100
                text.push("<tspan fill=\""+ color[index]+" \"y=\"30\"  x=\"" + x_position +"\" class=\"anim" + i+"\"  >" +number_as_array[index]+"</tspan>")
                        }
                return text.join(' ')
            })
    } 

    // start animation 
    d3.select("svg")
    .transition()
        .delay(1000)
        .duration(5000)
        .on("start", function anim1() { 
            // take abc and move them the line below before the =
            d3.selectAll(".anim1").transition().duration(2000)
                .attr("y", 60)
                .attr("x", function (d,i){
                        return 30+30*i
                })
            // take abc and move them the line below after the =
            d3.selectAll(".anim2").transition().duration(2000)
                .attr("y", 60)
                .attr("x", function (d,i){
                        return 50*number_as_array.length+100*i
                })

            d3.selectAll(".anim3").transition().duration(2000)
                .attr("y", 60)
                .attr("x", function (d,i){
                        return 50*number_as_array.length+100*i
                })

            // add "=" with opacity 0
            d3.select("#group1").append("text")
                .attr("id", "egal")
                .style("opacity", 0.0)
                .append("tspan")
                .attr("id", "egaltspan")
                .attr("y", 60)
                .attr("x", function(){
                return (50*number_as_array.length-30-30*(number_as_array.length+1))/2
                +30+30*number_as_array.length
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", "20px")
                .attr("fill", "black")
                .text("=")
                .transition().duration(1000).on("start", function addtext() {

                    //change opacity of = to 1
                    d3.select("#egal").transition().delay(2000).duration(1000).style("opacity", 1.0)})

                    // change the abc on the right to a*100+b*10+c
                    d3.selectAll(".anim2").transition().delay(3000).duration(2000)
                        .text(function (d,i){
                            if (i <(number_as_array.length-1)){
                                return number_as_array[i] + "*" + 10**(number_as_array.length-i-1) +"    +"
                            } else {
                                return number_as_array[i] + "*" + 10**(number_as_array.length-i-1)
                            }
                        })


                    d3.selectAll(".anim3").transition().delay(3000).duration(2000)
                        .text(function (d,i){
                            if (i <(number_as_array.length-1)){
                                return number_as_array[i] + "*" + 10**(number_as_array.length-i-1) +"    +"
                            } else {
                                return number_as_array[i] + "*" + 10**(number_as_array.length-i-1)
                            }
                        })
        })  // end anim1

    // change the abc on the right to  a*10^2+b*10^1+c10^0
    .transition()
        .delay(0)
        .duration(2000)
        .on("start", function anim2() {
                d3.selectAll(".anim3").transition().delay(0).duration(1000)
                .attr("y", 90)
                .transition().delay(0).duration(1000)
                .text(function (d,i){
                    if (i <(number_as_array.length-1)){
                        return number_as_array[i] + "*10^" +(number_as_array.length-i-1)+ "    +"
                    } else {
                        return number_as_array[i]+"*10^"+(number_as_array.length-i-1)       
                    }
                })
        })

    // add circle
    .transition()
        .delay(0)
        .duration(1000)
        .on("start", function () {

        //add the circle : for each digits in the user's number  
        var g_width = 900/number_as_array.length
        var circle_size = g_width/30
        for (var index=0; index< number_as_array.length; index++){

            // create an array which length = digit
            var data = [];
            var length = number_as_array[index];

            for(var i = 0; i < length; i++) {
                circle= {};
                circle.i = i;
                circle.cx = (circle_size*2*i+3)%g_width;
                circle.cy = (120*(circle_size*2*i+3))%g_width;
                data.push(circle);
            };

            // calculate the position of g representing that digit
            var g_position_x= g_width*index + 50

            //create  g for the digit
            var g = d3.select('svg')
                    .append('g')
                    .attr("transform", "translate(" + g_position_x + "," + 120 + ")")
                    .attr("id", function(d){return number_as_array.length-index-1})

                // display the circle
                g.selectAll('circle')
                    .data(data)
                    .enter()
                    .append('circle')
                    .attr('cx', function(d,i) { return d.cx; })
                    .attr('cy', function(d,i) { return d.cy; })
                    .attr('r', circle_size)
                    .attr('fill', color[index])
                    .attr('name', function(d,i) { return index+"-"+i; });           
            };    


        })  // end add circle   


}} // end if number <10000  + end anim()

</script>



119
4
задан 27 февраля 2018 в 09:02 Источник Поделиться
Комментарии
1 ответ

Это очень хороший код для первой попытки с Д3. Хотя есть некоторые соображения.

Прежде всего, если у вас есть if условие испытаний размер входного, это хорошая идея, писать else. Например:

else {
d3.select("svg").append('g')
.append("text")
.attr("y", 50)
.text("Number out of the range, please choose a new one.")
}

Теперь перейдем к большим проблемам. Первый-это:

d3.selectAll("g").remove();

Любой опытный Д3 застройщик не одобряют этого. Это обычно не является хорошей практикой удаление элементов на перекраску их: вы должны обновить их вместо. Однако, как это требует очень большого рефакторинга в коде, я оставлю это просто как Совет.

Второй вопрос, который показывает, что ваш код не идиоматическое Д3 применение for петли для добавления элементов. Как это:

for (var i = 0; i < 4; i++) {
d3.select("svg").append('g')
//etc...

Не используйте for петли для добавления элемента. Использовать привязки данных подход, который является идиоматических Д3. Итак, приведенный выше код может быть:

d3.select("svg").selectAll(null)
.data(d3.range(4))
.enter()
.append('g')
//etc...

Лучше, чем название ваш выбор:

var groups = d3.select("svg").selectAll(null)
.data(d3.range(4))
.enter()
.append('g')
//etc...

Например, вместо того, чтобы делать...

d3.select("svg")

... все время, вы можете сделать:

var svg = d3.select("svg")

И просто использовать svg всякий раз, когда вам нужно. Это хорошая практика в коде Д3, так что вы можете последний на СМ ваш выбор, не полагаясь на классы или идентификаторы.

Кроме того, удалить "You choose" из выборки в примере выше: вы не хотите показать его в 4 раза.

Наконец, вы используете start слушатель переход выбор для управления другими переходами. Это тоже не идиоматические подход Д3, и все может быстро станет сложнее контролировать и понимать. Вместо этого используйте простой d3.timeoutили даже ваниль с JS setTimeout:

d3.timeout(function(){
//do your transitions here
}, 5000);
// ^----- time in milliseconds

Вот ваш код с этими изменениями:



<!DOCTYPE html>
<meta charset="utf-8">
<title>Input test</title>
<div id="div1">

<form onSubmit="return false;">
Choose a number less than 100.000: <input type="text" name="number" id="number"><br>

</form>
<svg width="900" height="300" id="pack1">
</svg>

</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>

<script>
// get user number and start anim()
var inputElem = d3.select("#number");
inputElem.on("change", anim);

function anim() {
// remove previous animation
d3.selectAll("g").remove();

var number = d3.select(this).property("value");

if (number < 100000) {
// general var
var color = ["red", "blue", "orange", "green", "darkkhaki"]

// get choices from user : number saved as array
var number_as_array = number.toString().split('');

d3.select("svg").append("g")
.append("text")
.text("You choose :")
.attr('x', 10)
.attr('y', 30)
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "black");

// add 4 time the text "you choose : abc"
var groups = d3.select("svg").selectAll(null)
.data(d3.range(4))
.enter()
.append('g')
.attr('id', function(d) {
return "group" + d
})
.append("text")
.attr('x', 10)
.attr('y', 30)
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "black")
.html(function(d) {
var text = [""]
for (var index = 0; index < number_as_array.length; index++) {
var x_position = (index + 1) * 30 + 100
text.push("<tspan fill=\"" + color[index] + " \"y=\"30\" x=\"" + x_position + "\" class=\"anim" + d + "\" >" + number_as_array[index] + "</tspan>")
}
return text.join(' ')
})

// start animation
d3.timeout(function() {
// take abc and move them the line below before the =
d3.selectAll(".anim1").transition().duration(2000)
.attr("y", 60)
.attr("x", function(d, i) {
return 30 + 30 * i
})
// take abc and move them the line below after the =
d3.selectAll(".anim2").transition().duration(2000)
.attr("y", 60)
.attr("x", function(d, i) {
return 50 * number_as_array.length + 100 * i
})

d3.selectAll(".anim3").transition().duration(2000)
.attr("y", 60)
.attr("x", function(d, i) {
return 50 * number_as_array.length + 100 * i
})

// add "=" with opacity 0
d3.select("#group1").append("text")
.attr("id", "egal")
.style("opacity", 0.0)
.append("tspan")
.attr("id", "egaltspan")
.attr("y", 60)
.attr("x", function() {
return (50 * number_as_array.length - 30 - 30 * (number_as_array.length + 1)) / 2 +
30 + 30 * number_as_array.length
})
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "black")
.text("=")
.transition().duration(1000).on("start", function addtext() {

//change opacity of = to 1
d3.select("#egal").transition().delay(2000).duration(1000).style("opacity", 1.0)
})

// change the abc on the right to a*100+b*10+c
d3.selectAll(".anim2").transition().delay(3000).duration(2000)
.text(function(d, i) {
if (i < (number_as_array.length - 1)) {
return number_as_array[i] + "*" + 10 ** (number_as_array.length - i - 1) + " +"
} else {
return number_as_array[i] + "*" + 10 ** (number_as_array.length - i - 1)
}
})

d3.selectAll(".anim3").transition().delay(3000).duration(2000)
.text(function(d, i) {
if (i < (number_as_array.length - 1)) {
return number_as_array[i] + "*" + 10 ** (number_as_array.length - i - 1) + " +"
} else {
return number_as_array[i] + "*" + 10 ** (number_as_array.length - i - 1)
}
})
}, 1000) // end anim1

// change the abc on the right to a*10^2+b*10^1+c10^0
d3.timeout(function() {
d3.selectAll(".anim3").transition().delay(0).duration(1000)
.attr("y", 90)
.transition().delay(0).duration(1000)
.text(function(d, i) {
if (i < (number_as_array.length - 1)) {
return number_as_array[i] + "*10^" + (number_as_array.length - i - 1) + " +"
} else {
return number_as_array[i] + "*10^" + (number_as_array.length - i - 1)
}
})
}, 4000)

// add circle
d3.timeout(function() {

//add the circle : for each digits in the user's number
var g_width = 900 / number_as_array.length
var circle_size = g_width / 30
for (var index = 0; index < number_as_array.length; index++) {

// create an array which length = digit
var data = [];
var length = number_as_array[index];

for (var i = 0; i < length; i++) {
circle = {};
circle.i = i;
circle.cx = (circle_size * 2 * i + 3) % g_width;
circle.cy = (120 * (circle_size * 2 * i + 3)) % g_width;
data.push(circle);
};

// calculate the position of g representing that digit
var g_position_x = g_width * index + 50

//create g for the digit
var g = d3.select('svg')
.append('g')
.attr("transform", "translate(" + g_position_x + "," + 120 + ")")
.attr("id", function(d) {
return number_as_array.length - index - 1
})

// display the circle
g.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d, i) {
return d.cx;
})
.attr('cy', function(d, i) {
return d.cy;
})
.attr('r', circle_size)
.attr('fill', color[index])
.attr('name', function(d, i) {
return index + "-" + i;
});
};

}, 6000) // end add circle

} else {
d3.select("svg").append('g')
.append("text")
.attr("y", 50)
.text("Number out of the range, please choose a new one.")
}
}

</script>



ЗЫ: стоит отметить, что математика для позиции ваших кругах есть некоторые проблемы.

4
ответ дан 4 марта 2018 в 01:03 Источник Поделиться