在鼠标悬停时更改 <svg:path/> 不透明度

时间:2021-04-26 16:19:42

标签: reactjs svg d3.js data-visualization

我正在尝试创建一种效果,如果用户将鼠标悬停在折线图上,则鼠标右侧的 svg:path 元素部分将淡出,而 svg:path 元素的部分到左侧保持完全不透明。

我尝试了几个选项都无济于事 - 见下文。

我的第一次尝试是使用带有遮罩的路径,它确实改变了不透明度,但其余的线被隐藏了,因为它们不在遮罩下。

<defs>
    <mask
        id='mask-for-line'
        maskUnits="userSpaceOnUse"
        maskContentUnits="userSpaceOnUse"
    >
        <rect style={{opacity: .5, stroke: 'none', fill: 'white'}}
              x={x}
              y={y}
              width={width}
              height={height}
        />
    </mask>
</defs>
<path mask='url(#mask-for-line)' ... />

line with svg mask

我的第二次尝试是将 svg:rect 放在淡出部分,但这也不起作用。

<rect x={x} y={0} width={width} height={height} 
    style={{opacity: .1, stroke: 'none', fill: 'lightgray'}}/>

svg:rect overlaid on line chart

2 个答案:

答案 0 :(得分:1)

多亏了 michael-rovinsky 的启发,我才能够解决这个问题。在 mask 中,我有一个完全不透明度的 <rect/> 覆盖图表左侧,第二个 <rect/> 覆盖图表右侧的不透明度为 25%。

<defs>
    <mask
        id='mask-for-line'
        maskUnits="userSpaceOnUse"
        maskContentUnits="userSpaceOnUse"
    >
        <rect style={{fillOpacity: .25, fill: 'white'}}
              x={x}
              y={y}
              width={width - x}
              height={height}
        />
        <rect style={{fillOpacity: 1, fill: 'white'}}
              width={x}
              height={height}
        />
    </mask>
</defs>

two svg:mask solution

答案 1 :(得分:0)

您可以尝试使用可变停止偏移的线性渐变:

const svg = d3.select('svg');
const width = parseInt(svg.attr('width'));
const height = parseInt(svg.attr('height'));
console.log(width, height);

const colors = ['red', 'green', 'blue', 'orange', 'purple', 'brown'];

const defs = svg.append('defs');
colors.forEach(color => {
    const grad = defs.append('linearGradient').attr('id', `${color}-opacity-mask`);
  grad.append('stop').attr('offset', '0%').attr('stop-color', color).attr('stop-opacity', 1);
  grad.append('stop').attr('stop-color', color).attr('stop-opacity', 1).classed('mid-stop', true);
  grad.append('stop').attr('stop-color', color).attr('stop-opacity', 0.25).classed('mid-stop', true);
  grad.append('stop').attr('offset', '100%').attr('stop-color', color).attr('stop-opacity', 0.25);
})

const step = 100;
const paths = colors.map(color => {
    let path = '';
  for (let index = 0; index <= width / step; index++)
    if (!index)
        path = `M 0,${Math.random() * height}`;
    else
        path += `L ${index * step},${Math.random() * height}`;
  return {color, path};
});

paths.forEach(({path, color}) => svg.append('path').attr('d', path).style('stroke', `url(#${color}-opacity-mask)`).style('fill', 'none'));

const line = svg.append('line')
  .attr('y1', 0)
  .attr('y2', height)
  .style('stroke', 'black')
  .style('stroke-dasharray', '3 3')
  .style('visibility', 'hidden');

svg.on('mousemove', e => {
    const pct = Math.round(100 * e.layerX / width); 
    svg.selectAll('.mid-stop').attr('offset', `${pct}%`);
    line.attr('x1', e.layerX).attr('x2', e.layerX).style('visibility', 'visible');
});

svg.on('mouseleave', e => line.style('visibility', 'hidden'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg width="500" height="200">
</svg>