javascript - 使用 D3(在 Javascript 中)在 RECT 上实现 SVG mask ?

标签 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

据我所知,一切看起来都不错。这让我相信 mask 与条形图未对齐,导致条形图不可见。但是,在开发人员工具中,当我将鼠标悬停在 <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;
    }
}

最佳答案

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

//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)")

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

关于javascript - 使用 D3(在 Javascript 中)在 RECT 上实现 SVG mask ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28488344/

相关文章:

javascript - Bootstrap 表 "item per page"下拉列表不工作

javascript - 将表格<td>作为复选框,如何在<td>上实现点击和拖动多选

javascript - 我的 javascript 阻止我的代码工作

html - 想要在视差区域使文本 div 透明

html 和 css 边距自动错误

javascript - Ajax 从不正确的 URL 错误地成功获取

javascript - 防止div重叠

html - 设置没有底部滚动的溢出滚动

javascript - 工具提示的 CSS 定位

javascript - 使用 ember.js 异步批量查找或创建