i saw this example here: https://jsfiddle.net/gruc1vod/4/
and i want to add these dots over my bar chart using mask.
Here is my JavaScript code:
var svg = d3.select("body").append("svg");
var dotsPatternDefs = svg.append('defs');
dotsPatternDefs.append('pattern')
.attr('id', 'dotsPattern')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 10)
.attr('height', 10)
.append('circle')
.attr('cx', 5)
.attr('cy', 5)
.attr('r', 3)
.style('fill', 'white');
dotsPatternDefs.append('mask')
.attr('id', 'mask-dots')
.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('x', 0)
.attr('y', 0)
.style('fill', 'url(#dotsPattern)');
svg.append('rect')
.attr('class', 'dotsPattern')
.attr('width', '200')
.attr('height', '200')
.attr('x', 0)
.attr('y', 0)
.style('fill', '#F189b2');
Here is my CSS code:
rect.dotsPattern {
mask: url(#mask-dots);
}
and here my live example: https://jsfiddle.net/uao5yfhm/6/
Where is the problem and i cannot see this outcome correct outcome but i see this one wrong outcome?
Solution:
just change the circle color into black and add one more white rectangle in mask.
var svg = d3.select("body").append("svg");
var dotsPatternDefs = svg.append('defs');
dotsPatternDefs.append('pattern')
.attr('id', 'dotsPattern')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 10)
.attr('height', 10)
.append('circle')
.attr('cx', 5)
.attr('cy', 5)
.attr('r', 3)
.style('fill', 'black');
let mask = dotsPatternDefs.append('mask').attr('id', 'mask-dots')
mask.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('x', 0)
.attr('y', 0)
.style('fill', 'white');
mask.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('x', 0)
.attr('y', 0)
.style('fill', 'url(#dotsPattern)');
svg.append('rect')
.attr('class', 'dotsPattern')
.attr('width', '200')
.attr('height', '200')
.attr('x', 0)
.attr('y', 0)
.style('fill', '#F189b2');
rect.dotsPattern {
mask: url(#mask-dots);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.1/d3.min.js"></script>
working example: https://jsfiddle.net/gspn7a3o/35/
I think you have misunderstood the usage of mask. If you fill the pattern circle into white, it means that "Everything under a white pixel will be visible"(See MDN). So the pink rectangle will be seen through these white circles.
So if you are trying to not see through circles, put them in black("Everything under a black pixel will be invisible") and also give a white rectangle mask to make sure the pink can be seen.
My first answer here, ask me if you have any more questions.
Related
Can someone explain to me how to use clipping mask on the circle arc.
To explain what I'm doing. I have a circle and on this circle I add an arc that can be moved around. Now I would like to add like a linear gradient to this circle from left to right. But the gradient should only be seen on the arc, not the circle itself. Also I found one solution here https://www.freshconsulting.com/d3-js-gradients-the-easy-way/ but it's not what I want to do as my gradient should always be the same, but only the arc should display it. For example like this:
This is how you would draw a masked circle with a gradient fill with d3.
const svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
const defs = svg.append("defs")
const gradient = defs.append("linearGradient")
.attr("id", "exampleGradient")
gradient.append("stop")
.attr("offset", "10%")
.attr("stop-color", "white")
gradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", "red")
const mask = defs.append("mask")
.attr("id", "donutMask")
mask.append("circle")
.attr("cx", 250)
.attr("cy", 250)
.attr("r", 150)
.attr("fill", "white")
mask.append("circle")
.attr("cx", 250)
.attr("cy", 250)
.attr("r", 120)
const circle = svg.append("circle")
.attr("r", 149)
.attr("cx", 250)
.attr("cy", 250)
.attr("stroke", "black")
.attr("mask", "url(#donutMask)")
.attr("fill", "url(#exampleGradient)")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am interested in learning how to create a transparent mask with d3.js.
http://jsfiddle.net/59bunh8u/35/
This is where I am up to - how would I create a subtraction mask on the red rectangle - also how could you style the red rectangle to take on more of a multiply style property?
$(document).ready(function() {
var el = $(".mask"); //selector
// Set the main elements for the series chart
var svg = d3.select(el[0]).append("svg")
.attr("class", "series")
.attr("width", "800px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 500)
.attr("height", 500)
.style("fill", "red")
.style('opacity', 0.75)
var rect = svg
.append("circle").attr("cx", 250).attr("cy", 250).attr("r", 125).style("fill", "white");
});
You need an SVG mask. Feel free to play with it to tweak the parameters:
var mask = svgroot
.append("defs")
.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 500)
.attr("height", 500)
.style("fill", "white")
.style("opacity", 0.7);
mask.append("circle")
.attr("cx", 300)
.attr("cy", 300)
.attr("r", 100);
Modified example: http://jsfiddle.net/59bunh8u/40/
See also SVG clipPath to clip the *outer* content out
http://jsfiddle.net/LsMZp/37/
I'm having issues stabilizing the patterns in this mock.
how do I ensure the image is always centrally aligned - it seems to jump around depending on its location/scale.
here is the code to this issue. Hoping the solution can be applied to this force chart example.
d3.js Force Chart - image/node linkage and animation
function addUserPatterns(patternsSvg, userData){
$.each(userData, function( index, value ) {
var defs = patternsSvg.append('svg:defs');
defs.append('svg:pattern')
.attr('id', "--"+index+"-"+value.userName.toLowerCase())
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 100)
.attr('height',100)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', 100)
.attr('height', 100);
});
var circle = patternsSvg.append("svg:g")
.selectAll("circle")
.data(userData);
//enter
circle
.enter()
.append("svg:circle")
.attr("r", 50)
.style("fill", function(d, i) {
var imgUrl = "--"+i+"-"+d.userName.toLowerCase();
return "url(#"+imgUrl+")";
})
.attr("cy", function(d){
return random(0, 143);
})
.attr("cx", function(d){
return random(0, 143);
})
function random(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
var patternsSvg = d3.select("body")
.append('svg')
.attr('class', 'patterns')
.attr('width', 300)
.attr('height', 300)
.append('g')
.attr("transform","translate(100, 100)")
var userData =[
{
"userName": "Ria",
"userImage" : "https://pbs.twimg.com/profile_images/427892889092231168/4c4Qwynr.png"
},
{
"userName": "Barry",
"userImage" : "https://lh3.googleusercontent.com/-XdASQvEzIzE/AAAAAAAAAAI/AAAAAAAAAls/5vbx7yVLDnc/photo.jpg"
}
]
addUserPatterns(patternsSvg, userData);
I have the images being appended to the circles ok now - but if the images are of different sizes/dimensions - is there a way to ensure the image will be fitted properly?
Is it just an assumption to ensure the images are of the same dimensions as of each other - or is there a more sophisticated way to calculate image width/height and then alter the pattern attributes as required?
http://jsfiddle.net/LsMZp/48/
var defs = patternsSvg.append('svg:defs');
defs.append('svg:pattern')
.attr('id', "--"+index+"-"+value.userName.toLowerCase())
//.attr('patternUnits', 'userSpaceOnUse')
.attr('x', 0)
.attr('y', 0)
.attr('width', 50)
.attr('height', 50)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', 100)
.attr('height', 100);
});
I am using d3.js for graph. at some point i have to show data with some special part of graph for example if the values is cross some boundary then show that part with filling pattern. for more clear is there in and image.
i get the rect part that cross the boundary but how can i fill it with this pattern?
any css or canvas tricks?
Note : this image is just an example not the real one
How about this:
Live Demo
JS
var svg = d3.select("body").append("svg");
svg
.append('defs')
.append('pattern')
.attr('id', 'diagonalHatch')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 4)
.attr('height', 4)
.append('path')
.attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
.attr('stroke', '#000000')
.attr('stroke-width', 1);
svg.append("rect")
.attr("x", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", 'yellow');
svg.append("rect")
.attr("x", 0)
.attr("width", 100)
.attr("height", 100)
.attr('fill', 'url(#diagonalHatch)');
Results
To change the color would be simple, just a conditional if statement. Here's an example i've used before:
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.style("fill", function(d) { // <== Add these
if (d.close >= 50) {return "red"} // <== Add these
else { return "black" } // <== Add these
;}) // <== Add these
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.close); });
To add a pattern would be a little more involved as you first have to add the defs element to your SVG and then add your pattern to it
//first create you SVG or select it
var svg = d3.select("#container").append("svg");
//then append the defs and the pattern
svg.append("defs").append("pattern")
.attr("width", 5)
.attr("height", 5);
I want to write text inside a rectangle I create as follows:
body = d3.select('body')
svg = body.append('svg').attr('height', 600).attr('width', 200)
rect = svg.append('rect').transition().duration(500).attr('width', 150)
.attr('height', 100)
.attr('x', 40)
.attr('y', 100)
.style('fill', 'white')
.attr('stroke', 'black')
text = svg.append('text').text('This is some information about whatever')
.attr('x', 50)
.attr('y', 150)
.attr('fill', 'black')
However, as you can see (http://jsfiddle.net/Tmj7g/3/) the text gets cut off. Any nifty ways to write a paragraph inside of the svg rectangle created? Thanks,
The answer to this question might be relevant. SVG provides no way of wrapping text automatically, but you can embed HTML within SVGs and then use a div for example.
I've updated the jsfiddle here, but it doesn't work that well together with the animation. If you want to make it work properly and behave like any other SVG element, you'll have to pre-compute the line breaks and insert them manually.
To make it work with the animations just enclose in a group element and animate that one instead.
http://jsfiddle.net/MJJEc/
body = d3.select('body')
svg = body.append('svg')
.attr('height', 600)
.attr('width', 200);
var g = svg.append('g').attr("transform" ,"scale(0)");
rect = g.append('rect')
.attr('width', 150)
.attr('height', 100)
.attr('x', 40)
.attr('y', 100)
.style('fill', 'none')
.attr('stroke', 'black')
text = g.append('foreignObject')
.attr('x', 50)
.attr('y', 130)
.attr('width', 150)
.attr('height', 100)
.append("xhtml:body")
.html('<div style="width: 150px;">This is some information about whatever</div>')
g.transition().duration(500).attr("transform" ,"scale(1)");
I had a similar issue and found a reasonable solution by calculating the width of my box.
Secondly, I figured out that on average the character width for my current font is about 8.
Next I simply do a substring on the text to be displayed.
That seems to work perfectly in most cases.
var rectText = rectangles.append("text")
.text(function(d) {
TextBoxLength = timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime));
return d.task.substring(0, Math.floor(TextBoxLength / 8));
})
.attr("x", function(d) {
return (timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime))) / 2 + timeScale(dateFormat.parse(d.startTime)) + theSidePad;
})
.attr("y", function(d, i) {
return d.position * theGap + 14 + theTopPad;
})
.attr("font-size", 12)
.attr("text-anchor", "middle")
.attr("text-height", theBarHeight)
.attr("fill", "#000000");
Another approach, when trying to fit a straight line of text into an svg element, could use the strategy found in http://bl.ocks.org/mbostock/1846692:
node.append("text")
.text(function(d) { return d.name; })
.style("font-size", function(d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
.attr("dy", ".35em");