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

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


person JonasVoorzanger    schedule 27.03.2014    source источник


Ответы (2)


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

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

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

person Lars Kotthoff    schedule 27.03.2014

Ваши тени обрезаются областью фильтра нижележащего фильтра 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");

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

person Michael Mullany    schedule 12.04.2014