jasondavies / d3-cloud

Create word clouds in JavaScript.
https://www.jasondavies.com/wordcloud/
Other
3.84k stars 1.07k forks source link

Collision Not Detected when Font not yet Loaded #108

Open speakerjohnash opened 8 years ago

speakerjohnash commented 8 years ago

I ran into this problem and have seen it in two online examples as well:

http://bl.ocks.org/ericcoopey/6382449 https://www.pubnub.com/blog/2014-10-09-quick-word-cloud-from-a-chatroom-with-d3js/

Basically if you have a particular font, like something from Google fonts, if the font hasn't yet loaded to the page, the first draw will have words overlapping. The solution is to ensure that the css has been used via CSS prior to js execution. However it would be nice if there was an in library fix to this as it seems that it's a pretty common problem.

brightrain commented 7 years ago

👍 this. It helped me resolve an overlapping issue that popped up seemingly out of nowhere. Was using Google fonts and perhaps they changed the way they are retrieved? So I just removed the goog fonts and changed from .font("Open Sans', sans-serif") to just .font("sans-serif") when initializing and from .style("font-family", "'Open Sans', sans-serif") to just .style("font-family", "sans-serif") in the draw function.

Whole thing looks like this if it's helpful:

               var layout = d3.layout.cloud()
                        .size([800, 600])
                        .words(data)
                        .padding(3)
                        .rotate(function () { return (~~(Math.random() * 6) - 3) * 30; })
                        .font("sans-serif")
                        .fontSize(function (d) { return d.size / 10; })
                        .on("end", draw);
                    layout.start();

                    function draw(words) {
                        var container = d3.select("#wordCloudContainer").append("div");
                        container.append("svg")
                            .attr("width", layout.size()[0])
                            .attr("height", layout.size()[1])
                            // leaving this transparent for now but could set background color
                            //.style("background-color", "white")
                          .append("g")
                            .attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
                          .selectAll("text")
                            .data(words)
                          .enter().append("text")
                            .style("font-size", function (d) { return d.size + "px"; })
                            .style("font-family", "sans-serif")
                            .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; });
                    }
michaelByrne commented 7 years ago

Hello,

I wanted to raise this again. I'm trying to get the same effect as on the interactive example page, but I don't see any way to add in the Impact font (or any other named font) without breaking the positioning. Are there any other workarounds?

Thanks,

Whoops, looks like I figured it out as below.

d3.layout.cloud()
            .size([this._width, this._height])
            .words(this.words)
            .rotate(() => this._rotations[Math.floor(Math.random() * this._rotations.length)])
            .font("Impact")
            .fontWeight("normal")
            .padding(1)
            .fontSize(function(d) { return d.size; })
            .spiral("archimedean")
            .on('end', () => {
                this._drawWordCloud(this.words);
            })
            .start();
    }
private _drawWordCloud(words) {
    this._svg
        .selectAll('text')
        .data(words)
        .enter()
        .append('text')
        .style('font-size', d => d.size + 'px')
        .style("font-family", "Impact")
        .style('fill', (d, i) => {
            return this._fillScale(i);
        })
        .attr('text-anchor', 'middle')
        .attr('transform', d => 'translate(' + [d.x, d.y] + ')rotate(' + d.rotate + ')')
        .attr('class', 'word-cloud')
        .text(d => {
            return d.text;
        });`
mootinator commented 5 years ago

I had to deal with issues caused by loading a web-font when the word cloud is the only thing on the page and described my (different) solution here