Заливка фигуры шаблоном изображения без повтора: нежелательная линия контура

#svg #shapes #fill

#svg #фигуры #заливка

Вопрос:

В моем приложении Angular пользователь может вставить изображение в фигуру SVG и применить преобразование к изображению в интерактивном режиме. По умолчанию изображение заполняет фигуру.

Это работает хорошо, за исключением одной очень раздражающей проблемы, которая возникает, когда:

  • Фигура не имеет обводки
  • Одна сторона изображения намного темнее, чем противоположная сторона

При выполнении этих условий на той стороне фигуры, где изображение светлее, появляется очень тонкая линия. Например, если нижняя часть изображения намного темнее верхней, тонкая линия появится в верхней части фигуры. Эта линия идет с противоположной стороны изображения, как будто шаблон пытается повториться. Я знаю, что для шаблонов изображений нет ничего похожего на no-repeat атрибут, поэтому я установил следующие атрибуты шаблона, чтобы избежать повторения:

 <pattern patternUnits="objectBoundingBox" x="0" y="0" width="1" height="1" ...>
  

Это работает, но не идеально. Тонкая линия не всегда видна на экране, даже при масштабировании, но в большинстве случаев это так. Это вызывает у нас проблемы, поскольку эти SVG-формы являются частью книжных страниц, и тонкая линия часто оказывается видимой в конечном отпечатке.

Есть ли хороший способ полностью избежать повторения шаблона? Я попытался расширить шаблон так, чтобы он перекрывал фигуру, но дополнительный размер должен быть несколько значительным, чтобы быть эффективным:

 <pattern x="-0.01" y="-0.01" width="1.02" height="1.02" ...>
  

Когда я запускаю приведенный ниже фрагмент кода в своем браузере Chrome, нежелательная тонкая синяя линия видна в верхней части фигуры. Однако проблема не специфична для Chrome; Я вижу это и в Firefox.

 <div style="padding: 20px; width: 180px; height: 150px; background-color: yellow;">
  <svg height="100%" width="100%" x="0%" y="0%" viewBox="0 0 1600 1200">
    <defs>
      <pattern id="pattern1" patternUnits="objectBoundingBox" 
        preserveAspectRatio="xMidYMid slice" 
        width="1" height="1" x="0" y="0" viewBox="0 0 1600 1200">
        <image width="1600" height="1200" x="0" y="0" 
          xlink:href="https://i.ibb.co/vZ9spGH/1600x1200-1.png"></image>
      </pattern>
    </defs>
    <rect fill="url('#pattern1')" height="100%" width="100%" x="0%" y="0%"></rect>
    </svg>
</div>  

Комментарии:

1. Но шаблон создается только для повторения его содержимого, так почему бы вам просто не использовать обычное изображение, поскольку оно не должно повторяться?

2. @philipp — Могу ли я включить обычное изображение внутри фигуры? Я показываю простой случай в вопросе, но фигура может быть любым контуром, и у нее может быть обводка.

3. Если все дело в том, что изображение обрезано до формы, используйте <clipPath> , возможно, с clipPathUnits="objectBoundingBox" , а размер фигуры нормализован до 1.

4. Что касается вашего вопроса, влияет ли настройка style="shape-rendering:geometricPrecision" на прямоугольник?

5. @ccprog — Я попытался установить shape-rendering стиль / атрибут, но тонкая повторяющаяся линия все еще там. Для пояснения функции: каждый элемент SVG представляет собой рамку, в которую пользователь может вставить изображение; одна конкретная форма устанавливается в качестве контейнера изображения. Пользователь может в интерактивном режиме: вставить изображение, изменить толщину обводки фигуры, перемещать, изменять размер и поворачивать рамку, перемещать, изменять размер и поворачивать картинку внутри рамки. Вот почему изображение внутри фигуры показалось мне естественным. preserveAspectRatio Атрибут pattern также удобен для масштабирования.

Ответ №1:

Заливка фигуры выполняется довольно просто с помощью фильтра:

 <div style="padding: 20px; width: 180px; height: 150px; background-color: yellow;">
  <svg height="100%" width="100%" x="0%" y="0%" viewBox="0 0 1600 1200">
    <defs>
      <filter id="simple-image-fill" primitiveUnits="userSpaceOnUse"> 
      
        <feImage width="1600" height="1200" x="0" y="0" preserveAspectRatio="xMidYMid slice"
          xlink:href="https://i.ibb.co/vZ9spGH/1600x1200-1.png" result="image-res"/>
          <feComposite operator="in" in2="SourceGraphic" in="image-res"/>
      </filter>
    </defs>
    <rect filter="url(#simple-image-fill)" height="100%" width="100%" x="0%" y="0%"/>
    </svg>
</div>  

Комментарии:

1. Это выглядит очень многообещающе. Спасибо! Я не могу полностью протестировать это прямо сейчас, но сделаю это, как только смогу.

2. Фильтр перезаписывает обводку, когда она есть, не так ли?

3. Да, это так. Существует метод, который вы можете использовать для сохранения обводки, но он становится немного сложнее и требует фильтра, специфичного для каждого цвета обводки.

4. Это прискорбно. Чего я не понимаю, так это почему в шаблонах SVG нет no-repeat опции. Тем более, что использование шаблона — единственный способ включить изображение в фигуру (AFAIK). Вариант использования, когда повторение нежелательно, не должен быть таким исключительным.

5. Вы также можете попробовать немного сместить начальную позицию вашего шаблона — это, кажется, устраняет ошибку: width = «1» height =»1.01″ x = «0» y =»-0.01″ viewBox =»0 0 1600 1200″