简体   繁体   中英

Need help on updating a D3.js wordcloud

I am not a programmer at all, though I'm trying to write a wordcloud generator using d3. I was digging into examples online and everything works fine so far, but currently if I click "Make Wordcloud" button it simply adds another wordcloud, and I want it to update the existing one. Though, I believe I am lacking knowledge to accomplish it. Can you guys help? Here's the code:

$('#btn-wordcloud').click(function() {
    if (codebtn_click_counter < 1) {
        alert("please hit Code Data button first");
    } else {

        // Get all of the words
        words = [];
        wordscnt = [];
        var data = hot.getData();
        for (i = 0; i < data.length; i++) {
            for (j = 1; j < data[i].length; j++) {
                if (data[i][j]) {
                    if (words[data[i][j]]) {
                        words[data[i][j]]++;
                    } else {
                        words[data[i][j]] = 1;
                    }
                }
            }
        }

        for (word in words) {
            if (word != "None" && words[word] > 2) {
                var row = {
                    "text": word.toUpperCase(),
                    "size": words[word] * 15
                }
                wordscnt.push(row)
            }
        }

        if (wordscnt.length > 0) {
            $('#data').hide();
            var fill = d3.scale.category20();
            maxSize = d3.max(wordscnt, function(d) {
                return d.size;
            });
            minSize = d3.min(wordscnt, function(d) {
                return d.size;
            });

            var fontScale = d3.scale.linear() // scale algo which is used to map the domain to the range
            .domain([minSize, maxSize]) //set domain which will be mapped to the range values
            .range([15, 80]); // set min/max font size (so no matter what the size of the highest word it will be set to 40 in this case)

            if (codebtn_click_counter >= 1 && click_counter == 0) {
                click_counter = ++click_counter;
                d3.layout.cloud().size([1000, 500])
                    .words(wordscnt.sort(sortWordCountObject))
                //.rotate(function() { return ~~(Math.random() * 2) * 90; })
                .padding(5)
                    .rotate(0)
                    .font("Impact")
                //.fontSize(function(d) { return d.size; })
                .fontSize(function(d) {
                    return fontScale(d.size)
                })
                    .on("end", draw)
                    .start();
            } else {
                //* How do I update the svg created?

            };

            function draw(words) {
                d3.select("#drawing").append("svg")
                    .attr("width", 1000)
                    .attr("height", 500)
                    .append("g")
                    .attr("transform", "translate(500,250)")
                    .selectAll("text")
                    .data(words)
                    .enter().append("text")
                    .style("font-size", function(d) {
                        return d.size + "px";
                    })
                    .style("font-family", "Expressway")
                //* .style("fill", function(d, i) { return fill(i); }) *//
                .attr("text-anchor", "middle")
                    .attr("transform", function(d) {
                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                    })
                    .text(function(d) {
                        return d.text;
                    });
            }
        }
    }
});

The key lies in your draw function. Notice the line d3.select("#drawing").append("svg") . This means that every time the function is called, it adds another SVG. Now, while you can create another function to do this, it's also completely possible to do it within the same function by using the functions associated with the .data function: .enter() and .exit() .

function draw(words) {
  var words = d3.select("#drawing").selectAll("svg").data([0]).enter().append("svg")
    .attr("width", 1000)
    .attr("height", 500)
    .append("g")
    .attr("transform", "translate(500,250)")
    .selectAll("text")
    .data(words);

  words.enter().append("text")
    .style("font-family", "Expressway")
    //* .style("fill", function(d, i) { return fill(i); }) *//
    .attr("text-anchor", "middle")

  words.style("font-size", function(d) {
      return d.size + "px";
    })
    .attr("transform", function(d) {
      return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
    })
    .text(function(d) {
      return d.text;
    });

  words.exit().remove();
}

The important points about .data() :

  • This function consumes an array and attempts to match that array to the objects provided by the selection ( selectAll("text") in this case).
  • .enter() only affects new objects, those elements in the array which do not have a matching object (more array elements than objects)
  • Conversely, .exit() affects objects which do not have a matching array (more objects than array elements)
  • Simply calling words.<function> will affect all objects.

So what this does is creates the words and applies the font-family and text-anchor settings, then updates all of the text with their associated font-size , transform , and text . Finally, it removes any exiting words.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM