svg + Sprite sheet + d3 + clipPath + position + size

时间:2014-01-17 18:42:32

标签: javascript svg d3.js sprite-sheet

需要事先道歉:因为长度和我的无知。我正在尝试自学新概念:d3.js和精灵表。精灵表概念很容易理解,但我很困惑如何将它整合到d3中。基本上我想要做的是从精灵表中选择我想用作图像的精灵,然后使用d3在页面上的其他地方显示这个选定的精灵,并且很可能是同一精灵的多个副本。

实际精灵表供参考(见下文免责声明): Sample icons see disclaimer below

以下是问题: 1)我将精灵表添加到我的html,现在硬编码<clipPath>,这显示了我想要的特定精灵,然而,精灵的尺寸/位置就像显示整个精灵表一样。我怎样才能“捕捉”精灵本身,而不仅仅是隐藏未使用的精灵?在下图中,我想在d3鼠标悬停事件中使用单个“图标”(第2部分)。

修改此示例:SO: Display CSS image sprite in SVG without using foreignObject

HTML

<svg id="mySvg1" width="100%" height="100%">
    <defs>
        <clipPath id="c">
            <rect x="135" y="0" width="150" height="150"/>
        </clipPath>
    </defs>
        <image transform="scale(1.0)" x="0" y="0" width="550" height="420" xlink:href="static/img/iconSheet.png" clip-path="url(#c)"/>
<svg>

结果

Using clipPath

2)我可以使用<pattern>来确定要在d3对象/事件中显示的图像。就像在矩形中显示图形一样。但这似乎不适用于大型图像(精灵表)?如果我尝试使用精灵表本身及其在模式中的原生尺寸,它会变得奇怪和模糊。如果我们解决第1部分,我们可能会忽略第2部分,但这对于了解一般知识/将来的使用会很好。

修改此示例:SO: Adding an image within a circle object in d3 javascript?

HTML

<svg id="mySvg" width="550" height="420">
  <defs id="mdef">
    <pattern id="image" x="0" y="0" height="550" width="420">
      <image transform="scale(1.0)" x="0" y="0" width="550" height="420" xlink:href="static/img/iconSheet.png"></image>
    </pattern>
  </defs>
</svg>

使用Javascript:

var svgContainer = d3.select("div#content-main").append("svg")
                                    .attr("width", 740)
                                    .attr("height", 760)
                                    .attr("class", "mySvg")
                                    .style("border", "none");

svgContainer.append("rect")
         .attr("class", "logo")
         .attr("x", 0)
         .attr("y", 0)
         .attr("width", 550)
         .attr("height", 420)
         .style("fill", "transparent")  
         .style("stroke", "black")     
         .style("stroke-width", 0.25)     
         .on("mouseover", function(){ 
               d3.select(this)
                   .style("fill", "url(#image)");
         })
          .on("mouseout", function(){ 
               d3.select(this)
                   .style("fill", "transparent");
         });

结果

Weird scaling when using <pattern>

3)如果有更有效的方法来实现这一点,我愿意接受建议。我只是坚持使用d3模型,因为我已经渲染了一个svg对象,我只需要添加它。

免责声明:图标不是我的作品!我使用这些图标仅用于教育目的。作者的链接在这里:Fitness Icons

2 个答案:

答案 0 :(得分:8)

我不确定<pattern>示例会发生什么,但您<image>元素的问题在于您没有翻译图像,因此您想要的图标位于(0 ,0)SVG的一点。

这就是你需要的:

<svg id="mySvg1" width="100%" height="100%" viewBox="0 0 150 150">
    <defs>
        <clipPath id="c">
            <rect x="135" y="0" width="150" height="150"/>
        </clipPath>
    </defs>
        <image transform="translate(-135,0)" width="550" height="420" 
            xlink:href="static/img/iconSheet.png" clip-path="url(#c)"/>
<svg>

当然,如果您要制作大量图标并使用多个位置,我建议:

  1. <defs>元素中定义图标,然后在需要时使用<use>元素引用它们;
  2. 使用<use>元素在每个图标中定位图像,因此您只需定义图像网址,高度和宽度一次;
  3. 将每个图像/使用元素嵌套在<g>元素中,并将剪切路径应用于它,这样您只需定义剪切路径一次(假设所有图标大小相同)。
  4. 此处示例:http://codepen.io/AmeliaBR/pen/mwzBD

    定义图标的关键代码:

    <svg class="icon-defs">
      <defs>
        <!-- The icons are defined in an SVG <defs> element;
            it could be in a different file,
            since the icons will be referenced by url. -->
    
        <!-- One clipping path defines icon size -->
        <clipPath id="icon-cp" >
            <rect x="0" y="0" width="150" height="100" />
        </clipPath>
    
        <!-- One image element imports the file -->
        <image id="icon-sprite" width="969" height="293" 
               xlink:href="http://i.stack.imgur.com/TPx5h.png" />
    
        <!-- Each icon fragment uses the same image 
             with a different translation -->
        <g id="icon1" clip-path="url(#icon-cp)">
            <use xlink:href="#icon-sprite" 
                transform="translate(0,0)" />
        </g>
        <g id="icon2" clip-path="url(#icon-cp)">
            <use xlink:href="#icon-sprite" 
                transform="translate(-240,0)" />
        </g>
        <g id="icon3" clip-path="url(#icon-cp)">
            <use xlink:href="#icon-sprite" 
                transform="translate(-240,-193)" />
        </g>   
     </defs>
    

    然后你引用这样的图标:

    <svg class="icon" viewBox="0 0 150 100" height="4em" width="6em">
            <use xlink:href="#icon3"/>
    </svg>
    

    viewBox属性设置布局图像的内部尺寸,每次使用图标时都相同;高度和宽度可以是你想要的任何东西(虽然缩小当然看起来比放大更好)。如果高度/宽度比率与图标不匹配,则会被压缩或拉伸,但您可以使用preserveAspectRatio属性来阻止它。

    现在,到d3。虽然您可以动态地构造该DOM,但可能最容易定义提前表示图标的SVG片段,可能在单独的文件中。当你真的想要插入一个图标时,你

    例如,要在每个元素末尾添加带有“警告”类的内联图标图像,您可以执行以下操作:

    d3.selectAll(".warning")
        .append("svg")
          .attr("viewBox", "0 0 "+ iconWidth + " " + iconHeight)
          .style("display", "inline")
          .style("height", "1em")
          .style("width", (iconWidth/iconHeight) + "em")
        .append("use")
          .attr("xlink:href", "#warning");
    

    当然,如果你正在使用d3,你可能会得到一些数据变量来告诉你使用哪个图标而不是类,但你明白了。

答案 1 :(得分:0)

我认为剪辑和定位图标的一种更简单的方法是使用带有适当<svg>的嵌套viewBox

&#13;
&#13;
<svg width="100%" height="100%">
                              
    <!-- Repeat this for ever icon instance you want.
         Just change x and y attributes to set position of the icon in your SVG,
         and minX,minY (first two coords) of viewBox to select icon from sprite sheet. -->
    <svg x="0" y="0" width="150px" height="150px" viewBox="135 0 150 150">
        <image width="550px" height="420px" xlink:href="http://i.stack.imgur.com/qAO2h.png" />
    </svg>
  
</svg>
&#13;
&#13;
&#13;