使用D3(在Javascript中)在RECT上实现SVG掩码?

时间:2015-02-12 21:41:57

标签: javascript html css svg d3.js

我正在尝试在D3中实现一个SVG掩码,类似于这个非常简单的jsfiddle example,但我必须在翻译中丢失一些东西。我的实现都发生在一个呈现图形的类中。我正在尝试应用蒙版来定义图形的边界,这样当数据超出这些边界时,图形会被整齐地剪切掉。当我应用蒙版时,图形的条形图完全消失。据我所知,面具在正确的位置。帮助!

这是我在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  !!!!
    },

以下是Chrome开发者工具中生成的HTML的链接(我突出显示了<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;
    }
}

1 个答案:

答案 0 :(得分:1)

好的,我看到了发生了什么。您应该通过将剪贴蒙版附加到图形区域来将剪贴蒙版应用于条形和线条:

//clipping mask
yourSvg.append("clipPath")
    .attr("id", "chart-area")
    .append("rect")
    .attr("x", yourXcoordinates)
    .attr("y", yourYcoordinates)
    .attr("width", 333) //this was the width provided by the webinspector 
    .attr("height", 649) //this was the height provided by the webinspector;

然后在绘制线条和条形图时,将其添加到两个生成器

.attr("clip-path", "url(#chart-area)")

这应该会给你你正在寻找的剪辑。基本上它的作用是剪切那个矩形区域之外的所有东西,所以如果你正确地绘制它,它应该剪掉不需要的东西