I have the following WordCloud.
The original Code is from Jason Davies D3-JavaScript-WordCloud Example.
https://github.com/jasondavies/d3-cloud/blob/master/examples/simple.html
Most of the Code below is from this really helpfull tutorial (in German):
http://www.advitum.de/blog/2012/04/tagcloud-mit-php-und-javascript-erstellen-word-cloud-d3/
Thank You Lars Ebert!
EDIT: Now I have learned a little bit more. I have cleared nonsensical code.
My goal is to center the first word from array horizontally.
The first word is now centered horizontally, but now there is a gap in the Cloud.
Just at the x,y point where the word would be without positioning.
My new question is: How do I remove this gap?
Thank You.
var wordcloud, size = [800, 800]; //Cloud Size
var fillColor = d3.scale.category20b();
function loaded() {
d3.layout.cloud()
.size(size)
.words(words)
.font("Impact")
.fontSize(function(d) { return d.size;})
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.on("end", draw)
.start();
}
function draw(words) {
wordcloud = d3.select("body")
.append("svg")
.attr("width", size[0])
.attr("height", size[1])
.append("g")
.attr("transform", "translate(" + (size[0]/2) + "," + (size[1]/2) + ")");
wordcloud.selectAll("text")
.data(words)
.enter()
.append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("fill", function(d) { return fillColor(d.text.toLowerCase()); })
.attr("text-anchor", "middle")
//Edit
.attr("transform", function(d, i) {
if(i == 0){
return "translate(" + [0, 0] + ")rotate(" + 0 + ")"; //handle first element
}else{
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; //handle the rest
}
})
//--------------
.text(function(d) { return d.text; });
}
Use a negative margin equal to one of the size calculations:
wordcloud.selectAll("text")
...
...
...
.style("margin-left", function(d) {return d.size + "px"})
Related
I want to highlight a new array of words like "salmon" & "prey" that I want to provide to my word cloud, so how should I do it because I tried to use mark.js or Javascript with CSS but couldn't succeed, but now I think it is only possible here when I am drawing the word cloud. So can someone help me to provide me with a function or maybe some changes in my code to highlight the array (arrayToBeHighlight) of words:
var width = 750, height = 500;
var words = [["whales", 79], ["salmon", 56], ["Chinook", 30], ["book", 70],
["prey", 51]].map(function(d) {
return {text: d[0], size: d[1]};
});
var arrayToBeHighlight = [ ["salmon", 56], ["prey", 51] ];
**OR**
var arrayToBeHighlight = ["salmon", "prey"];
maxSize = d3.max(words, function(d) { return d.size; });
minSize = d3.min(words, function(d) { return d.size; });
var fontScale = d3.scale.linear().domain([minSize, maxSize]).range([10,70]);
var fill = d3.scale.category20();
d3.layout.cloud().size([width, height]).words(words).font("Impact")
.fontSize(function(d) { return fontScale(d.size) })
.on("end", drawCloud).start();
function drawCloud(words) {
d3.select("#wordCloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) +")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.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; });
}
HTML Code
<div style="margin-left:20px" id="wordCloud"></div>
Things like mark.js work by creating a span around the word and setting a background color to mimic a highlighter. This doesn't work in SVG because text elements don't have a background color. Instead, you can fake it by inserting a rect before the text element:
texts.filter(function(d){
return arrayToBeHighlight.indexOf(d.text) != -1;
})
.each(function(d){
var bbox = this.getBBox(),
trans = d3.select(this).attr('transform');
g.insert("rect", "text")
.attr("transform", trans)
.attr("x", -bbox.width/2)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
});
Running code;
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://rawgit.com/jasondavies/d3-cloud/master/build/d3.layout.cloud.js"></script>
</head>
<body>
<div style="margin-left:20px" id="wordCloud"></div>
<script>
var width = 750,
height = 500;
var words = [
["whales", 79],
["salmon", 56],
["Chinook", 30],
["book", 70],
["prey", 51]
].map(function(d) {
return {
text: d[0],
size: d[1]
};
});
var arrayToBeHighlight = ["salmon", "prey"];
maxSize = d3.max(words, function(d) {
return d.size;
});
minSize = d3.min(words, function(d) {
return d.size;
});
var fontScale = d3.scale.linear().domain([minSize, maxSize]).range([10, 70]);
var fill = d3.scale.category20();
d3.layout.cloud().size([width, height]).words(words).font("Impact")
.fontSize(function(d) {
return fontScale(d.size)
})
.on("end", drawCloud).start();
function drawCloud(words) {
var g = d3.select("#wordCloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var texts = g.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) {
return d.size + "px";
})
.style("font-family", "Impact")
.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;
});
texts.filter(function(d){
return arrayToBeHighlight.indexOf(d.text) != -1;
})
.each(function(d){
var bbox = this.getBBox(),
trans = d3.select(this).attr('transform');
g.insert("rect", "text")
.attr("transform", trans)
.attr("x", -bbox.width/2)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
});
}
</script>
</body>
</html>
The answer depends on your definition of highlight and how you want to highlight them.
One possibility is comparing the arrayToBeHighlight array with the datum when painting the words. For instance, turning them red:
.style("fill", function(d, i) {
return arrayToBeHighlight.indexOf(d.text) > -1 ? "red" : fill(i);
})
Here is the bl.ocks: http://bl.ocks.org/anonymous/d38d1fbb5919c04783934d430fb895c2/b42582053b03b178bb155c2bbaec5242374d051b
I am using Jason Davies's sample word cloud generator code to create a word cloud. I am able to draw it but the shape of the word cloud I would like is oval/circular. I am aware that there have been questions regarding the circular shape of Jason Davies's word cloud but they all were talking about making changes in the build code function place() and were also unanswered. I am wondering if there is a way to achieve the oval/circular shape modifying the following code:
var fill = d3.scale.category20();
var layout = cloud()
.size([500, 500])
.words([
"Hello", "world", "normally", "you", "want", "more", "words",
"than", "this"].map(function(d) {
return {text: d, size: 10 + Math.random() * 90, test: "haha"};
}))
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw);
layout.start();
function draw(words) {
d3.select("body").append("svg")
.attr("width", layout.size()[0])
.attr("height", layout.size()[1])
.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", "Impact")
.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; });
}
In the readme of Jason Davies's Github, there is a mention of spiralfor word positioning and I am unable to understand since I am very new to the JavaScript. Does anyone has any suggestions? Thanks a lot.
Add the following: .spiral("archimedean")
I just got the word cloud template from T3. Now I added a youtube link to every word in the word cloud, but I want the video pop up while I clicked the words in the word cloud. How should I modify my code? Thanks a lot.
Here is my Javascript:
<script>
var fill = d3.scale.category20();
var words = [{"text":"Worry", "url":"http://google.com/"},
{"text":"Choices", "url":"http://bing.com/"},
]
var width = 1080;
var height = 500;
for (var i = 0; i < words.length; i++) {
words[i].size = 10 + Math.random() * 90;
}
d3.layout.cloud()
.size([width, height])
.words(words)
.padding(5)
.rotate(function() { return ~~ ((Math.random() * 6) - 3) * 30 + 8; })
.font("Impact")
.fontSize(function(d) { return d.size;})
.on("end", draw)
.start();
function draw(words) {
d3.select("#word-cloud")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate("+ width/2 +","+ height/2 +")")
.selectAll("text")
.data(words)
.enter()
.append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.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; })
.on("click", function (d, i){
window.open(d.url, "_blank");
});
}
My proposed approach:
Create a modal window
Open the modal when a word is clicked
Dynamically populate the embed code into the modal based on which word is clicked (I didn't do this part for you, but should be easily doable as it's part of the data to access).
I created a JSFiddle that gets you most of the way, with additional work needed here:
.on("click", function (d, i){
// Dynamically populate embed
// Open the modal
document.getElementById('myModal').style.display = "block";
});
I am creating a wordcloud by modifying code from : https://github.com/jasondavies/d3-cloud. I can change the size by modifying w & h but I want to scale the word cloud as the browser window changes. What would be the best method to achieve this?
Code also posted at http://plnkr.co/edit/AZIi1gFuq1Vdt06VIETn?p=preview
<script>
myArray = [{"text":"First","size":15},{"text":"Not","size":29},{"text":"Bird","size":80}, {"text":"Hello","size":40},{"text":"Word","size":76},{"text":"Marketplaces","size":75}]
var fillColor = d3.scale.category20b();
var w = 400, // if you modify this also modify .append("g") .attr -- as half of this
h = 600;
d3.layout.cloud().size([w, h])
.words(myArray) // from list.js
.padding(5)
.rotate(0)
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", drawCloud)
.start();
function drawCloud(words) {
d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return (d.size) + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fillColor(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d,i) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
}
)
.text(function(d) { return d.text; });
}
</script>
Solution # 1:
On line 37:
.style("font-size", function(d) { return (d.size) + "px"; })
Replace
.style("font-size", function(d) { return (d.size/3) + "vh"; }) // "d.size/3" is an assumption use your appropriate relative width or height.
Instead of using px use vw which is view port width. It is a css3 feature that will resize the text according to the viewport. However, you will need to adjust the real width and height properly.
Try reading this article: http://css-tricks.com/viewport-sized-typography/
Solution # 2:
On line 37:
.style("font-size", function(d) { return (d.size) + "px"; })
Use
.attr("class", nameOfClass) // use class names here like 'big-font', 'med-font', 'small-font'
and in the CSS define the styles using media queries, the classes will be assigned depending upon the d.size in the condition so do it like if (d.size > 10) nameOfClass = "big-font" etc.
Instead of giving words width and height using JS, allocate classes to them using media queries breakpoints.
Read : http://www.w3schools.com/cssref/css3_pr_mediaquery.asp
I recommend solution 2 as i believe vw and vh is not supported by all the browsers. http://caniuse.com/#feat=viewport-units. There are some issues reported related to that.
Solution # 3:
To calculate the font-size, you have to create this scale:
var fontSizeScale = d3.scale.pow().exponent(5).domain([0,1]).range([ minFont, maxFont]);
and call it in fontSize function:
var maxSize = d3.max(that.data, function (d) {return d.size;});
.fontSize(function (d) {
return fontSizeScale(d.size/maxSize);
})
To fit the bounds to your screen/div:
in the .on("end", drawCloud) function, call this function:
function zoomToFitBounds() {
var X0 = d3.min( words, function (d) {
return d.x - (d.width/2);
}),
X1 = d3.max( words, function (d) {
return d.x + (d.width/2);
});
var Y0 = d3.min( words, function (d) {
return d.y - (d.height/2);
}),
Y1 = d3.max( words, function (d) {
return d.y + (d.height/2);
});
var scaleX = (X1 - X0) / (width);
var scaleY = (Y1 - Y0) / (height);
var scale = 1 / Math.max(scaleX, scaleY);
var translateX = Math.abs(X0) * scale;
var translateY = Math.abs(Y0) * scale;
cloud.attr("transform", "translate(" +
translateX + "," + translateY + ")" +
" scale(" + scale + ")");
}
I am using the word cloud library in D3 by Jason Davies. This is the normal code which I am using and works fine for creating the word clouds.
d3.layout.cloud().size([width, height])
.words(d3.zip(vis_words, vis_freq).map(function(d) {
return {text: d[0], size: wordScale(d[1]) };
}))
.padding(1)
.rotate(function() { return ~~(Math.random() * 2) * 0; })
.font("times")
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
function draw(words) {
d3.select(curr_id).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")")
.selectAll("text")
.data(words)
.enter()
.append("text")
.transition()
.delay(function(d,i){
return i*100;
})
.duration(1000)
.ease("elastic")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "times")
.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; });
}
I have a time slider to select a specific value, based on which words in the word cloud have different frequency(given by size) or some of the words are not there at all. I need to update without redrawing the entire word cloud which I am currently doing. In a way, I want to keep the position of words fixed and just updating their size and whether they exist based on the value selected on a slider?
Should I enter an update function in the function draw for this? Am certainly new to D3 and any help would be great?
To do this, you would select the existing text elements and set the font-size property for them. The code to do this looks like this:
d3.select("svg").selectAll("text")
.style("font-size", function(d, i) { // do something });