Я пытаюсь реализовать SVG-маску в D3, подобно этому очень простому jsfiddle example, но я, должно быть, что-то потерял в переводе. Моя реализация выполняется в классе, который отображает график. Я пытаюсь применить маску для определения границ для графика, так что, когда данные превышают эти границы, график аккуратно обрезается. Когда я применяю маску, штрихи графика полностью исчезают. Насколько я могу сказать маску в нужном месте. ПОМОГИТЕ!Реализовать маску SVG на RECT с D3 (в Javascript)?
Вот где я определяю маску в моей init()
функции:
// Add an SVG element with the desired dimensions and margin.
this.graph = d3.select(this.config.id).append("svg:svg")
.attr("width", this.width + this.m[1] + this.m[3])
.attr("height", this.height + this.m[0] + this.m[2])
.append("svg:g")
.attr("transform", "translate(" + this.m[3] + "," + this.m[0] + ")");
var maskWidth = 640;
var maskHeight = 321;
this.graph.append('svg:defs') <------ I START DEFINING IT HERE !
.call(function (defs) {
// Appending the mask
defs.append('svg:mask')
.attr('id', 'mask')
.attr('width', maskWidth)
.attr('height', maskHeight)
.attr('x', 0)
.attr('y', 0)
.call(function(mask) {
mask.append('svg:rect')
.attr('width', maskWidth)
.attr('height', maskHeight)
.attr('fill', '#ffffff')
});
});
Вот метод, который рисует полосы на графике, где я пытаюсь применить маску (см последнюю строку):
addBars: function (data){
var numberOfBars = Math.floor(this.xMaximum);
var barWidth = this.width/numberOfBars;
// Generate a histogram using twenty uniformly-spaced bins.
var histogramData = d3.layout.histogram()
.bins(this.xScale.ticks(numberOfBars))
(data);
//console.trace('typeof: '+typeof this.xScale);
var xScale = this.xScale;
var yScale = this.yScale;
var height = this.height;
this.bars = this.graph.selectAll("bar")
.data(histogramData, function(d){ return d;})
.enter()
.append("rect")
.attr("class","bar")
.attr("fill","steelblue")
.attr("transform", function(d, i) {
var yOffset = height;
return "translate(" + (i * barWidth - barWidth/2) + ","+yOffset+")";
})
.attr("y", function(d,i) {
var yPosition = yScale(d.length)- height;
return (yScale(d.length)-height);
})
.attr("height", function(d) {
return height - yScale(d.length);
})
.attr("width", barWidth - 1)
.attr('mask', 'url(#mask)'); <---- OVER HERE !!!!
},
Вот ссылка на результирующей HTML в Chrome Developer Tools (я выделил <defs>
и один из столбчатых диаграмм, которые должны быть замаскированы): Chrome Developer Tools Dynamic HTML
Насколько я могу судить, все выглядит хорошо. Это приводит меня к мысли, что маска неправильно выровнена с полосой, заставляя бар быть невидимым. Тем не менее, в инструментах разработчика, когда я нависаю над элементом <rect>
, он показывает, что он накладывается на панели графиков, поэтому это не похоже на проблему выравнивания. Любая помощь будет оценена по достоинству.
Наконец, я сделал jsfiddle класса, который используется в моем приложении (см. Комментарии к ссылке.). Ниже также весь класс для построения графика, только в том случае, было бы полезно, чтобы увидеть код в контексте:
// HistogramGrapher class - constructor
var HistogramGrapher = function() {
// assign default properties
this.config = {
id: "",
xAxisLabel: "xAxis",
yAxisLabel: "yAxis",
width: 1000,
height: 400,
title: "Title",
mean: 20
};
// define variables
this.m = [40, 80, 40, 80]; // margins
this.width; // width
this.height; // height
this.xAxisLabel;
this.yAxisLabel;
this.graph;
this.bars;
this.lines;
this.xScale;
this.xScaleInvert;
this.xAxis;
this.yScale;
this.yScaleInvert;
this.yAxis;
this.yMaximum = 25;
this.xMaximum = 2 * this.config.mean;
}
// methods for this class
HistogramGrapher.prototype = {
init: function (options) {
// copy properties of `options` to `config`. Will overwrite existing ones.
for(var prop in options) {
if(options.hasOwnProperty(prop)){
this.config[prop] = options[prop];
}
}
// update variables
this.updateWidth(this.config.width);
this.updateHeight(this.config.height);
this.updateXMaximum(this.config.mean);
// X scale will fit all values from datay[] within pixels 0-w
this.xScale = d3.scale.linear()
.domain([0, this.xMaximum])
.range([0, this.width]);
this.xScaleInvert = d3.scale.linear()
.range([0, this.xMaximum])
.domain([0, this.width]);
// Y scale
this.yScale = d3.scale.linear()
.domain([0, this.yMaximum])
.range([this.height,0]);
this.yScaleInvert = d3.scale.linear()
.range([0, this.yMaximum])
.domain([this.height,0]);
// Add an SVG element with the desired dimensions and margin.
this.graph = d3.select(this.config.id).append("svg:svg")
.attr("width", this.width + this.m[1] + this.m[3])
.attr("height", this.height + this.m[0] + this.m[2])
.append("svg:g")
.attr("transform", "translate(" + this.m[3] + "," + this.m[0] + ")");
var maskWidth = 640;
var maskHeight = 321;
this.graph.append('svg:defs')
.call(function (defs) {
// Appending the mask
defs.append('svg:mask')
.attr('id', 'mask')
.attr('width', maskWidth)
.attr('height', maskHeight)
.attr('x', 0)
.attr('y', 0)
.call(function(mask) {
mask.append('svg:rect')
.attr('width', maskWidth)
.attr('height', maskHeight)
.attr('fill', '#ffffff')
});
});
// create xAxis
this.xAxis = d3.svg.axis().scale(this.xScale)
.tickSize(-this.height)
.tickSubdivide(true);
// create yAxis
this.yAxis = d3.svg.axis().scale(this.yScale)
.tickSize(-this.width)
.tickSubdivide(true)
.orient("left");
// Add the x-axis label.
this.graph.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", this.width)
.attr("y", this.height + 25)
.text(this.config.xAxisLabel);
// Add the y-axis label.
this.graph.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", -30)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text(this.config.yAxisLabel);
// add Title
this.graph.append("text")
.attr("x", this.width/2)
.attr("y", -20 )
.attr("text-anchor", "middle")
.style("font-size", "12px")
.text(this.config.title);
// Add the x-axis.
this.graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.height + ")")
.call(this.xAxis);
// Add the y-axis.
this.graph.append("svg:g")
.attr("class", "y axis")
.call(this.yAxis);
},
updateWidth: function(width){
this.width = width - this.m[1] - this.m[3];
},
updateHeight: function(height){
this.height = height - this.m[0] - this.m[2]; // height
},
updateXMaximum: function(mean){
this.xMaximum = 2.5 * mean;
},
addBars: function (data){
var numberOfBars = Math.floor(this.xMaximum);
var barWidth = this.width/numberOfBars;
// Generate a histogram using twenty uniformly-spaced bins.
var histogramData = d3.layout.histogram()
.bins(this.xScale.ticks(numberOfBars))
(data);
//console.trace('typeof: '+typeof this.xScale);
var xScale = this.xScale;
var yScale = this.yScale;
var height = this.height;
this.bars = this.graph.selectAll("bar")
.data(histogramData, function(d){ return d;})
.enter()
.append("rect")
.attr("class","bar")
.attr("fill","steelblue")
.attr("transform", function(d, i) {
var yOffset = height;
return "translate(" + (i * barWidth - barWidth/2) + ","+yOffset+")";
})
.attr("y", function(d,i) {
var yPosition = yScale(d.length)- height;
return (yScale(d.length)-height);
})
.attr("height", function(d) {
return height - yScale(d.length);
})
.attr("width", barWidth - 1)
.attr('mask', 'url(#mask)');
},
addLine: function (data){ // the data must be in the form " [ {'x':x1, 'y':y1} , {'x':x2, 'y':y2} , {'x':x3, 'y':y3} ... ]
var xScale = this.xScale;
var yScale = this.yScale;
var height = this.height;
// create a line function that can convert data[] into x and y points
var lineFunction = d3.svg.line()
// assign the X function to plot our line as we wish
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); })
.interpolate("linear");
this.lines = this.graph.append("path")
.attr("d", lineFunction(data))
.attr("class", "line")
.attr("stroke", "green")
.attr("stroke-width", 2)
.attr("fill","none");
},
clear: function() {
var bars = d3.selectAll(".bar").remove();
var lines = d3.selectAll(".line").remove();
},
getxScale: function() {
return this.xScale;
},
getxScaleInvert: function() {
return this.xScaleInvert;
}
}
jsfiddle был бы полезен в этом случае, так как код не такой тонкий :) – tomtomtom
@ tomtomtom Вы хотели бы видеть, что класс графического отображения используется в jsfiddle? видеть, что граф исчезает с помощью маски? – bologna
да быть в состоянии увидеть, что на самом деле происходит, несомненно, будет полезно – tomtomtom