Как предотвратить резкие тени для маленьких фигур с D3

Я разрабатываю небольшой образовательный инструмент, для которого я хотел бы добавить тени к путям SVG. Для построения моей графики я использую D3.js. Обычно это прекрасно работает, но с конкретными фигурами у меня возникают проблемы, особенно с маленькими фигурами, которые имеют горизонтальные и / или вертикальные линии.

Чтобы проиллюстрировать это, я сделал пример JSFiddle с тремя треугольниками. Тень на зеленом треугольнике выглядит симпатично, тень на оранжевом треугольнике все еще в порядке, но тень на красном треугольнике выглядит очень некрасиво.

Тень создается с использованием этого кода в качестве примера:

var defs = svg.append("defs").attr("height","160%");
var filter = defs.append("filter").attr("id", "schaduw");
filter.append("feGaussianBlur").attr("in", "SourceAlpha")
      .attr("stdDeviation", 5).attr("result", "blur");
filter.append("feOffset").attr("in", "blur")
      .attr("dx", 3)
      .attr("dy", 3)
      .attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode").attr("in", "offsetBlur");
feMerge.append("feMergeNode").attr("in", "SourceGraphic");

Как видно из кода JSFiddle, единственное значимое различие между формами - это их размер. Как я могу предотвратить появление у маленьких фигур некрасивых теней?

Любая помощь очень ценится! знак равно

2 ответа

Решение

Качество тени, на которую вы ссылаетесь, контролируется стандартным отклонением гауссова размытия, которое вы используете. Использование абсолютного значения означает, что тень "растягивается" на одинаковую величину независимо от размера элемента, на котором вы используете фильтр. Для меньших фигур это имеет эффект, который вы видите.

Есть два способа исправить это. Быстрый и простой способ - уменьшить стандартное отклонение, чтобы оно выглядело хорошо даже для самых маленьких фигур. Я сделал это в вашем примере здесь, установив .attr("stdDeviation", 1),

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

Ваши тени обрезаются областью фильтра нижележащего фильтра SVG. По умолчанию эффекты фильтра отображаются в области переполнения 10% вокруг отфильтрованной фигуры. Ваши тени на меньших фигурах слишком велики и выходят за пределы этой области. Это вызывает края. Вам нужно добавить явную область фильтра и сделать ее больше. Это расширит область фильтра до 20% области, окружающей форму:

var filter = defs.append("filter").attr("id", "schaduw") .attr("width", 1.4) 
    .attr("height",1.4) .attr("x",-0.2) .attr("y",-0.2);

Если вам нужна тень, пропорциональная фильтруемому элементу, вам не нужно вычислять размер чего-либо. SVG обладает встроенной способностью делать относительные размеры, определяя ваши единицы измерения в единицах objectBoundingBox. Здесь вы должны использовать объекты objectBoundingBox в качестве примитивных модулей фильтра, а не (по умолчанию) значение userSpaceUnits ("обычные" модули окна просмотра). Вам необходимо пересчитать stdDeviation размытия, а также смещение смещения в% от поля исходного элемента, выраженное в виде десятичной дроби:

var filter = defs.append("filter").attr("id", "schaduw") .attr("width", 1.4) 
     .attr("height",1.4) .attr("x",-0.2) .attr("y",-0.2)     
     .attr("primitiveUnits","objectBoundingBox");
filter.append("feGaussianBlur").attr("in", "SourceAlpha")
              .attr("stdDeviation", .02).attr("result", "blur");
filter.append("feOffset").attr("in", "blur")
              .attr("dx", .01)
              .attr("dy", .01)
              .attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode").attr("in", "offsetBlur");
feMerge.append("feMergeNode").attr("in", "SourceGraphic");

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

Другие вопросы по тегам