2017-09-13 177 views
1

这是Humongous height value for <filter> not preventing cutoff的延续:我仍然试图在<path>上应用<filter>,但是我遇到了被裁剪的问题。SVG滤波器相对于可能长度为零的边界框,calc()的替代方案?

的问题在其他线程使用的<filter>x/y属性移动过滤器画布的中心解决,还是一切都以百分比,因此相对于你正在尝试应用的大小事影响,但问题是,即使在你看到某些东西的情况下,边长也可以是0,请参阅在以下实施例中的顶线:

.pathWrapper path { 
 
    stroke: grey; 
 
    fill: none; 
 
    stroke-width: 1.5; 
 
    marker-start: url(#circle); 
 
    marker-end: url(#arrow); 
 
} 
 

 
.pathWrapper:hover { 
 
    filter: url(#colorFilter); 
 
}
<svg style="height:400px;width:100%;background-color:LightCyan"> 
 

 
<defs> 
 
    <filter id="colorFilter" x="-300%" y="-300%" width="600%" height="600%"> 
 
    <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur> 
 
    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix> 
 
    <feMerge> 
 
     <feMergeNode in="lightenedBlur"></feMergeNode> 
 
     <feMergeNode in="SourceGraphic"></feMergeNode> 
 
    </feMerge> 
 
    </filter> 
 
    <marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;"> 
 
    <path d="M0,-5L10,0L0,5"></path> 
 
    </marker> 
 
    <marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker> 
 
</defs> 
 

 
<g transform="scale(2)"> 
 

 
    <g class="pathWrapper" transform="translate(70,20)"> 
 
    <path d="M52,10L45,10L-30,10L-37,10"></path> 
 
    </g> 
 
    
 
    <g class="pathWrapper" transform="translate(70,50)"> 
 
    <path d="M42,20L35,20L30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
    <g class="pathWrapper" transform="translate(200,20)"> 
 
    <path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
</g> 
 

 
</svg>

光标悬停在线看施加有所述过滤器的效果。 您会看到悬停时顶部线条变得不可见。这是因为边框高度为0,笔划宽度和标志小玩意不计:

height zero demo

我可以用绝对单位来代替,如就像下面这个例子:

.pathWrapper path { 
 
    stroke: grey; 
 
    fill: none; 
 
    stroke-width: 1.5; 
 
    marker-start: url(#circle); 
 
    marker-end: url(#arrow); 
 
} 
 

 
.pathWrapper:hover { 
 
    filter: url(#colorFilter); 
 
}
<svg style="height:400px;width:100%;background-color:LightCyan"> 
 

 
<defs> 
 
    <filter id="colorFilter" filterUnits="userSpaceOnUse" x="-125" y="-125" width="250" height="250"> 
 
    <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur> 
 
    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix> 
 
    <feMerge> 
 
     <feMergeNode in="lightenedBlur"></feMergeNode> 
 
     <feMergeNode in="SourceGraphic"></feMergeNode> 
 
    </feMerge> 
 
    </filter> 
 
    <marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;"> 
 
    <path d="M0,-5L10,0L0,5"></path> 
 
    </marker> 
 
    <marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker> 
 
</defs> 
 

 
<g transform="scale(2)"> 
 

 
    <g class="pathWrapper" transform="translate(70,20)"> 
 
    <path d="M52,10L45,10L-30,10L-37,10"></path> 
 
    </g> 
 
    
 
    <g class="pathWrapper" transform="translate(70,50)"> 
 
    <path d="M42,20L35,20L30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
    <g class="pathWrapper" transform="translate(200,20)"> 
 
    <path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
</g> 
 

 
</svg>

的问题是,在我的使用情况下,它已应用可以改变相当多的影响元素的大小,所以我将不得不把一个巨大的价值在那里或者总是有它的变化不够大(如跨越最大高度的线的例子所示)。

我发现就在使用CSS calc()可能是一个解决办法:

#colorFilter { 
 
    width: calc(100% + 100); 
 
    height: calc(100% + 100); 
 
    x: calc(-50% - 50); 
 
    y: calc(-50% - 50); 
 
} 
 

 
.pathWrapper path { 
 
    stroke: grey; 
 
    fill: none; 
 
    stroke-width: 1.5; 
 
    marker-start: url(#circle); 
 
    marker-end: url(#arrow); 
 
} 
 

 
.pathWrapper:hover { 
 
    filter: url(#colorFilter); 
 
}
<svg style="height:400px;width:100%;background-color:LightCyan"> 
 

 
<defs> 
 
    <filter id="colorFilter" filterUnits="userSpaceOnUse"> 
 
    <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur> 
 
    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix> 
 
    <feMerge> 
 
     <feMergeNode in="lightenedBlur"></feMergeNode> 
 
     <feMergeNode in="SourceGraphic"></feMergeNode> 
 
    </feMerge> 
 
    </filter> 
 
    <marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;"> 
 
    <path d="M0,-5L10,0L0,5"></path> 
 
    </marker> 
 
    <marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker> 
 
</defs> 
 

 
<g transform="scale(2)"> 
 

 
    <g class="pathWrapper" transform="translate(70,20)"> 
 
    <path d="M52,10L45,10L-30,10L-37,10"></path> 
 
    </g> 
 
    
 
    <g class="pathWrapper" transform="translate(70,50)"> 
 
    <path d="M42,20L35,20L30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
    <g class="pathWrapper" transform="translate(200,20)"> 
 
    <path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
</g> 
 

 
</svg>

这个作品在谷歌Chrome和Mozilla Firefox浏览器的当前版本,但不会出现在Micosoft边缘或工作IE11(从一点搜索看起来好像calc()支持即使对于HTML内容已经相当敏感,参见已知问题部分在https://caniuse.com/#search=calc)。

那么存在一种更好的替代calc()方法吗?

(也许这是值得注意的是,我与动态d3.js生成的内容合作。)

回答

3

当你正在与D3生成的内容的工作,最好的办法是插入一个看不见的矩形进g.pathWrapper涵盖所需的过滤面积。你可能不得不将它适应你的逻辑(也许换ES6等价的ES6结构)。假设你有你从你的生成路径数据点的列表:

var points = [[52,10], [45,10], [-30,10], [-37,10]]; 

// get the min/max values 
var x1 = d3.min(points,(p) => p[0]), 
    x2 = d3.max(points,(p) => p[0]), 
    y1 = d3.min(points,(p) => p[1]), 
    y2 = d3.max(points,(p) => p[1]); 

// construct the path 
var path = d3.path(); 
path.moveto(...points[0]); 
for (var point of points.slice(1)) { 
    path.lineto(...point); 
} 

// wrapper 
var wrapper = d3.select('svg > g').append('g') 
    .classed('pathWrapper'); 

// invisible rect with +5px in each direction as filter region 
wrapper.append('rect') 
    .attr('fill', 'none') 
    .attr('x', x1 - 5) 
    .attr('y', y1 - 5) 
    .attr('width', x2 - x1 + 10) 
    .attr('height', y2 - y1 + 10); 

// and the path itself 
wrapper.append('path') 
    .attr('d', path); 

之后,滤镜效果区域的默认值应该工作原样。

+0

的伟大工程。在你发布这个答案之前,我正在考虑使用不同的过滤器,但是你的方法更好,并且效果很好。 – phk

1

仅仅因为您在筛选器中使用的值为userSpaceOnUse,并不意味着您仍不能使用百分比。只是这些百分比与SVG的宽度和高度有关,而不是元素的宽度和高度。

当然,这确实意味着该过滤器正被应用于多余的不必要像素的批次。过滤器正在应用于SVG大小的区域,而不是元素大小的区域。但是由于您只是在悬停时筛选了一个元素,因此浏览器所做的额外工作导致的任何缓慢都不应该引起注意。

.pathWrapper path { 
 
    stroke: grey; 
 
    fill: none; 
 
    stroke-width: 1.5; 
 
    marker-start: url(#circle); 
 
    marker-end: url(#arrow); 
 
} 
 

 
.pathWrapper:hover { 
 
    filter: url(#colorFilter); 
 
}
<svg style="height:400px;width:100%;background-color:LightCyan"> 
 

 
<defs> 
 
    <filter id="colorFilter" filterUnits="userSpaceOnUse"> 
 
    <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur> 
 
    <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix> 
 
    <feMerge> 
 
     <feMergeNode in="lightenedBlur"></feMergeNode> 
 
     <feMergeNode in="SourceGraphic"></feMergeNode> 
 
    </feMerge> 
 
    </filter> 
 
    <marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;"> 
 
    <path d="M0,-5L10,0L0,5"></path> 
 
    </marker> 
 
    <marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker> 
 
</defs> 
 

 
<g transform="scale(2)"> 
 

 
    <g class="pathWrapper" transform="translate(70,20)"> 
 
    <path d="M52,10L45,10L-30,10L-37,10"></path> 
 
    </g> 
 
    
 
    <g class="pathWrapper" transform="translate(70,50)"> 
 
    <path d="M42,20L35,20L30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
    <g class="pathWrapper" transform="translate(200,20)"> 
 
    <path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path> 
 
    </g> 
 

 
</g> 
 

 
</svg>

+0

悬停只是为了演示目的,但如果绘图空间不是太大,也是一个好主意。在我的情况下,它是相当大的,因为可见空间只显示了我的绘图空间的一部分(用户可以平移/缩放),而且我还没有实现某种类型的截锥体剔除等效(还?)。 – phk