【问题标题】:d3 image on top of node / circle doesn't work with clipPath节点/圆圈顶部的 d3 图像不适用于 clipPath
【发布时间】:2019-05-08 10:06:12
【问题描述】:

试图找到一种在 D3 中将图像置于圆圈内的方法。

img 已加载到 DOM 中,但我无法让它在圆圈内呈现。

重点是将图片放在顶部,因此圆圈背景颜色必须保持原样(颜色编码目的)。

我在这里做错了什么?

Codepen

期望的输出:

我的代码:

function render(name) {
    let markerCircles = svg.selectAll("circle")
        .data([1, 2, 3, 4, 5])
        .enter()
        .append("circle")
        .style("fill", "none")
        .attr("stroke", "#ff97c4")
        .style("stroke-width", "1.5px")
        .attr("cy", fullSVGHeight / 2)
        .attr("cx", markerCirclesScale(name) + 330)
        .attr("r", 0)

    markerCircles
        .transition()
        .duration(1000)
        .attr("r", function(d) {
            return d * 65;
        });

    let personCircles = svg.selectAll("a")
        .data(data)
        .enter()
        .append("a")
        .attr("id", function(d) {
            console.log(d["Person Name"]);
            if (d && d.length !== 0) {
                return d["Person Name"].replace(/ |,|\./g, '_');
            }
        })
        .style("opacity", 1)
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

    //Define defs 
    let defs =  personCircles.append("defs");

     defs.append('rect')
            .attr('id', 'pic')
            .attr('x', function(d){
                return markerCirclesScale(name)
            })
             .attr('y', function(d){
                return fullSVGHeight / 2;
            })
            .attr('width', 120)
            .attr('height', 30)
            .attr('rx', 10)
            .style('fill', 'blue')

     defs.append("clipPath")
        .append("use")
        .attr('xlink:href', '_')
        .attr("z-index", 1000)

    d3.timeout(function() {
        personCircles
            .append("use")
            .attr('xlink:href', "_")
            .append('image')
            .attr('xlink:href', function(d){
                return 'https://vignette.wikia.nocookie.net/ideas/images/8/82/Donald_Trump.png/revision/latest/scale-to-width-down/640?cb=20170512015233'})
            .attr("clip-path", "url(#pic)")
            .attr("width", 120)
            .attr("height", 30)
            .attr("y", fullSVGHeight / 2)
            .attr("x", markerCirclesScale(name))
            .attr("opacity", .8)
            .append("circle")
            .attr("cy", fullSVGHeight / 2)
            .attr("cx", markerCirclesScale(name) + 330)
            .attr("r", 20)
            .append("title")
            .text(function(d) {
                return "Name: " + d["Person Name"] + ", " + "Time in the company: " + (+d["Period"] + " years");
            });
        simulation.restart().on("tick", ticked);

    }, 2000)

【问题讨论】:

    标签: javascript image d3.js svg


    【解决方案1】:

    D3 代码几乎没问题,但受到 SVG 元素层次结构的影响。 好像应该是这个顺序:

    <a>ID这里对于特定的图片显示任务是不需要的

    let personCircles = svg.selectAll("a")
                .data(data)
                .enter()
                .append("a")
                .attr("id", function(d) {
                    console.log(d["Person Name"]);
                    if (d && d.length !== 0) {
                        return d["Person Name"].replace(/ |,|\./g, '_');
                    }
                })
                .style("opacity", 1)
                .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended));
    

    然后定义

    <defs>
    //Define defs 
            let defs =  personCircles.append("defs");
    

    然后第一个容器将放置图片

    &lt;rect id = "RECT" / &gt; // ID 为必填项

    defs.append('rect')
                        .attr('id', 'rect-ggg')
                        .attr('x', function(d){
                            return markerCirclesScale(name)
                        })
                         .attr('y', function(d){
                            return fullSVGHeight / 2;
                        })
                        .attr('width', 60)
                        .attr('height', 60)
                        .attr('rx', 40)
                        .style('fill', 'red')
    

    容器附加了一个剪辑来创建一个蒙版

    &lt;clipPath id = "the CLIP"&gt; // 这里也需要ID

    defs.append("clipPath")
                    .attr('id', 'clip-ggg')
    

    将掩码绑定到容器

    &lt;use the href = "# a RECT"&gt; &lt;/use&gt; // 对容器的引用

    .append("use")
                        .attr('href', function(d){
                            return  "#rect-ggg";
    
                        })
    

    &lt;use&gt; 据我了解,这需要向页面添加一个容器,因为浏览器不会呈现 DEFS 内容本身

    &lt;use href = "# RECT" &gt; &lt;/ use&gt; // 对容器的引用

    personCircles
                    .append("use")
                    .attr('href', function(d){
                    return  "#rect-ggg";
    
                })
    

    最后添加照片

    &lt;image href clip-path = " url (#CLIP)" /&gt; // 参考掩码

    personCircles.append('image')
                    .attr('href', function(d){
                        return 'https://vignette.wikia.nocookie.net/ideas/images/8/82/Donald_Trump.png/revision/latest/scale-to-width-down/640?cb=20170512015233'})
    
    
                    .attr("clip-path", function(d){ 
                        return "url(#clip-ggg)";
                    })
                    .attr("width", 60)
                    .attr("height", 60)
                    .attr('x', function(d){
                        return markerCirclesScale(name)
                    })
                    .attr('y', function(d){
                       return fullSVGHeight / 2 + 8;
                    })
    

    circle // 这个圆圈不是必须的,我在布局中使用它来添加描边

    //Define SVG width and height
    let fullSVGHeight = 700;
    let fullSVGWidth = 900;
    
    //Define margins 
    // let margins = {
    //     top: 10,
    //     right: 50,
    //     bottom: 10,
    //     left: 10
    // }
    
    //Define chart width, height, x/y scale
    // let chartHeight = fullSVGHeight - margins.top - margins.bottom;
    // let chartWidth = fullSVGWidth - margins.left - margins.right;
    
    //Define marker circles scale
    let markerCirclesScale = d3.scalePoint()
        .range([0, fullSVGWidth])
    
    
    //Fetch data
    d3.json("https://api.myjson.com/bins/7gjbe").then(data => {
    
        //   data.forEach(function(d){
        //     console.log("This is initial d.Period: ", +d.Period)
        //         return data.filter(function() {
        //             if (+d["Period"] === 0){
        //                 return d["Period"] = 1;
        //             } else {
        //             return d.Period = Math.floor(+d["Period"]);
        //         }
        // })});
    
        console.log(data);
    
        markerCirclesScale
            .domain(data.map(function(d) {
                return d["Person Name"];
            }))
    
    
        let svg = d3.select("body")
            .append("svg")
            .attr("width", fullSVGWidth)
            .attr("height", fullSVGHeight)
            .append("g")
            .attr("transform", "translate(" + 0 + "," + 0 + ")");
    
        data.forEach(function(d) {
            return render(d["Person Name"])
        });
    
    
        function render(name) {
            let markerCircles = svg.selectAll("circle")
                .data([1, 2, 3, 4, 5])
                .enter()
                .append("circle")
                .style("fill", "none")
                .attr("stroke", "#ff97c4")
                .style("stroke-width", "1.5px")
                .attr("cy", fullSVGHeight / 2)
                .attr("cx", markerCirclesScale(name) + 330)
                .attr("r", 0)
    
            markerCircles
                .transition()
                .duration(1000)
                .attr("r", function(d) {
                    return d * 65;
                });
    
            let personCircles = svg.selectAll("a")
                .data(data)
                .enter()
                .append("a")
                .attr("id", function(d) {
                    console.log(d["Person Name"]);
                    if (d && d.length !== 0) {
                        return d["Person Name"].replace(/ |,|\./g, '_');
                    }
                })
                .style("opacity", 1)
                .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended));
    
    
    
    
            //Define defs 
            let defs =  personCircles.append("defs");
           
             defs.append('rect')
                    .attr('id', 'rect-ggg')
                    .attr('x', function(d){
                        return markerCirclesScale(name)
                    })
                     .attr('y', function(d){
                        return fullSVGHeight / 2;
                    })
                    .attr('width', 60)
                    .attr('height', 60)
                    .attr('rx', 40)
                    .style('fill', 'red')
                   
    
             defs.append("clipPath")
                .attr('id', 'clip-ggg')
                .append("use")
                .attr('href', function(d){
                    return  "#rect-ggg";
                    
                })
    
                
            let simulation = d3.forceSimulation(data)
                .force("charge", d3.forceCollide().radius(3))
                .force('center', d3.forceCenter(fullSVGWidth / 2, fullSVGHeight / 2))
                .force("radius", d3.forceRadial(function(d) {
                    return +d["Period"] * 60
                }, fullSVGWidth / 2, fullSVGHeight / 2).strength(0.3))
                .on("tick", ticked)
                .velocityDecay(0.07)
                .stop();
    
    
    
            function ticked() {
                personCircles
                    .attr("cx", function(d) { return d.x - 100; })
                    .attr("cy", function(d) { return d.y; });
            }
    
    
            d3.timeout(function() {
                personCircles
                    .append("use")
                     .attr('href', function(d){
                    return  "#rect-ggg";
                    
                })
                personCircles.append('image')
                    .attr('href', function(d){
                        return 'https://vignette.wikia.nocookie.net/ideas/images/8/82/Donald_Trump.png/revision/latest/scale-to-width-down/640?cb=20170512015233'})
                    .attr("clip-path", function(d){ 
                        return "url(#clip-ggg)";
                    })
                    .attr("width", 60)
                    .attr("height", 60)
                    .attr('x', function(d){
                        return markerCirclesScale(name)
                    })
                     .attr('y', function(d){
                        return fullSVGHeight / 2 + 8;
                    })
                
                simulation.restart().on("tick", ticked);
    
            }, 2000)
    
            function dragstarted(d) {
                d.dragged = true;
                simulation.alphaTarget(0.8).restart();
                d.fx = d.x;
                d.fy = d.y;
            }
    
            function dragged(d) {
                d.fx = d3.event.x;
                d.fy = d3.event.y;
            }
    
            function dragended(d) {
                simulation.alphaTarget(0);
                d.fx = null;
                d.fy = null;
                d3.timeout(function() {
                    d.dragged = false;
                }, 1000)
            }
    
        }
    
    }).catch(error => console.log(error));
    body, body *{
                top:0;
                padding: 0;
                margin: 0;
                background-color: #80d6c7;
                overflow: hidden;
                font-family: Lucida Console, Monaco, monospace;
    
            }
    
            a {
                background-color: transparent;
                color: #679fa5;
                text-decoration: none;
            }
    
            svg {
                margin-top: 2% ;
                margin-left: 29%;
            }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 2014-06-22
      • 2014-08-29
      • 2013-12-03
      • 1970-01-01
      • 2019-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-06
      相关资源
      最近更新 更多