svg + лист спрайтов + d3 + clipPath + позиция + размер

Заранее нужно извиниться: за длину и за мою неосведомленность. Я пытаюсь освоить новые концепции: d3.js и листы спрайтов. Концепция листа спрайтов проста для понимания, но я не понимаю, как интегрировать это в d3. По сути, я хочу выбрать спрайт, который я хочу использовать в качестве изображения, из листа спрайтов, а затем использовать d3 для отображения этого выбранного спрайта где-то еще на странице и, скорее всего, несколько копий одного и того же спрайта.

Фактический лист спрайтов для справки (см. Заявление об отказе от ответственности ниже): Примеры значков см. Заявление об отказе от ответственности ниже

Вот проблемы: 1) Я добавляю лист спрайтов в свой html, жестко кодирую <clipPath> на данный момент, это отображает конкретный спрайт, который я хочу, однако размеры/позиционирование спрайта такие же, как если бы отображался весь лист спрайтов. . Как я могу «захватить» только сам спрайт, а не просто скрыть неиспользуемые? На изображении ниже я хочу использовать один «значок» в событии наведения мыши d3 (часть 2).

Изменение этого примера: SO: отображать спрайт изображения CSS в SVG без использования иностранный объект

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>

Результат

Использование clipPath

2) Я могу использовать <pattern> для определения изображения, которое будет отображаться в объекте/событии d3. Как показать графику в прямоугольнике. Но это не работает для больших изображений (листов спрайтов)? Это становится странным и размытым, если я пытаюсь использовать сам лист спрайтов и его собственные размеры в шаблоне. Если мы решим часть 1, мы, вероятно, сможем проигнорировать часть 2, но это было бы полезно понять для общих знаний/использования в будущем.

Изменение этого примера: SO: добавление изображения в круг объект в 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");
         });

Результат

Странное масштабирование при использовании ‹pattern›

3) ЕСЛИ есть более эффективный способ сделать это, я открыт для предложений. Я просто придерживаюсь модели d3, потому что я уже визуализировал объект svg, и мне просто нужно добавить к нему что-то.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Иконки не моя работа! Я использую эти значки только в образовательных целях. Ссылка автора находится здесь: Значки фитнеса


person James    schedule 17.01.2014    source источник


Ответы (2)


Я не уверен, что происходит с примером <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> для позиционирования изображения в каждом значке, поэтому вам нужно только один раз определить URL-адрес, высоту и ширину изображения;
  3. вложение каждого элемента изображения/использования в элемент <g> и применение к нему пути отсечения, так что вам нужно определить путь отсечения только один раз (при условии, что все значки имеют одинаковый размер).

Пример здесь: 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. Вероятно, будет проще определить фрагменты SVG, представляющие значки, заранее, возможно, в отдельном файле, хотя вы можете создать этот DOM динамически. Когда вы действительно хотите вставить значок, вы

Например, чтобы добавить встроенное изображение значка в конце каждого элемента с классом «предупреждение», вы должны сделать что-то вроде этого:

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, у вас, вероятно, есть некоторая переменная данных, которая говорит вам, какой значок использовать вместо класса, но вы поняли идею.

person AmeliaBR    schedule 18.01.2014
comment
Кстати, если единственная причина, по которой вы использовали SVG для своих спрайтов, заключается в том, что вы хотели использовать d3, помните, что вы также можете использовать d3 для добавления элементов HTML и установки их класса для запуска фона CSS. изображение; или даже установить свойства CSS напрямую с помощью .style(). - person AmeliaBR; 18.01.2014
comment
Спасибо за очень подробный ответ. Было очень полезно с вашей стороны пройтись по примерам. У меня все еще есть проблемы с масштабированием, но в целом у меня есть возможность добавлять значки на холст svg нужного мне размера. Способ масштабирования значков не интуитивно понятен. Можно было бы ожидать, что значки будут масштабироваться относительно собственного разрешения изображения, но это не обязательно так работает (очевидно). Несмотря на это, вы дали мне достаточно информации, чтобы продолжить изучение и применение концепций. Спасибо! - person James; 21.01.2014
comment
К масштабированию и SVG нужно время, чтобы привыкнуть. Просто помните, что все с SVG рисуется так, что один пиксель равен одной единице измерения SVG viewBox. Таким образом, коэффициент масштабирования — это соотношение между высотой или шириной viewBox и высотой или шириной SVG. (Какой из них используется, зависит от preserveAspectRatio.) Если вы сделаете свой viewBox больше, чем размеры значка, то значок не будет заполнять весь размер SVG. Если вы сделаете свой viewBox меньше, чем размеры значка, значок будет обрезан. Пример с размерами шрифта: codepen.io/AmeliaBR/pen/iFJng - person AmeliaBR; 21.01.2014

Я думаю, что гораздо более простой способ обрезать и расположить значок — использовать вложенный <svg> с соответствующим viewBox.

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

person Paul LeBeau    schedule 18.12.2014