我尝试创建一个 HTML/CSS/JS 漏斗图,具有简单的前端结构,为此,我将漏斗段放置为弹性行:
document.addEventListener( 'DOMContentLoaded', function() {
let funnels = document.getElementsByClassName( 'funnel' );
for( let i = 0; i < funnels.length; i++ ) {
let segments = funnels[i].children[1].children;
for( let j = 0; j < segments.length; j++ ) {
const from = parseFloat( segments[j].getAttribute( 'data-from' ) );
const to = parseFloat( segments[j].getAttribute( 'data-to' ) );
const difference = ( from - to ) / 2;
const d = `M 0 ${50 - from * 50} Q 25 ${50 - from * 50} 50 ${50 - ( ( to + difference ) * 50 )} T 100 ${50 - to * 50} V 100 ${50 + to * 50} Q 75 ${50 + to * 50} 50 ${50 + ( ( to + difference ) * 50 )} T 0 ${50 + from * 50} V ${50 - from * 50} Z`;
const svg = '<svg id="fcm-' + i + '-' + j + '" viewBox="0 0 100 100" preserveAspectRatio="none" style="width: 100%; height: 100%;"><path d="' + d + '" fill="black"></path></svg>';
segments[j].style.setProperty( '--mask', ( 'url( #fcm-' + i + '-' + j + ' )' ) );
segments[j].innerHTML = svg;
}
}
} );
.funnel > div:last-child {
display: flex;
flex-direction: row;
height: 300px;
}
.funnel > div:last-child div {
background: green;
height: 100%;
position: relative;
width: 100%;
}
.funnel:not( .no-mask ) > div:last-child div {
mask-image: var( --mask );
mask-size: cover;
mask-repeat: no-repeat;
}
<div class="funnel">
<div>
Funnel (Mask)
</div>
<div>
<div data-from="1" data-to="0.66667"></div>
<div data-from="0.66667" data-to="0.25"></div>
<div data-from="0.25" data-to="0.1"></div>
</div>
</div>
<div class="funnel no-mask">
<div>
Funnel (No Mask)
</div>
<div>
<div data-from="1" data-to="0.66667"></div>
<div data-from="0.66667" data-to="0.25"></div>
<div data-from="0.25" data-to="0.1"></div>
</div>
</div>
对于漏斗中的每个分段,脚本将绘制该分段的相关 SVG 路径,并将其(带有生成的 ID)添加到 DIV 中。新 ID 将作为 CSS 变量(类似
style="--mask: url( #fcm-0-0 );"
的属性)传递到 DIV。
我面临的问题是,当我尝试使用 SVG 的黑色区域来遮盖父 DIV 时,它似乎不起作用并且所有颜色都消失了。
我的目标是暂时用 SVG 形状遮盖 DIV 的绿色背景,以便稍后我可以用渐变替换背景。
我在我的代码片段中添加了 2 个示例:
你能帮忙吗?
我会在 mask 变量中定义 SVG,而不是创建 SVG 元素然后引用它。
document.addEventListener('DOMContentLoaded', function() {
let funnels = document.getElementsByClassName('funnel');
for (let i = 0; i < funnels.length; i++) {
let segments = funnels[i].children[1].children;
for (let j = 0; j < segments.length; j++) {
const from = parseFloat(segments[j].getAttribute('data-from'));
const to = parseFloat(segments[j].getAttribute('data-to'));
const difference = (from - to) / 2;
const d = `M 0 ${50 - from * 50} Q 25 ${50 - from * 50} 50 ${50 - ( ( to + difference ) * 50 )} T 100 ${50 - to * 50} V 100 ${50 + to * 50} Q 75 ${50 + to * 50} 50 ${50 + ( ( to + difference ) * 50 )} T 0 ${50 + from * 50} V ${50 - from * 50} Z`;
segments[j].style.setProperty('--mask', 'url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="none" style="width: 100%; height: 100%;"><path d="' + d + '" fill="black"></path></svg>\')');
}
}
});
.funnel>div:last-child {
display: flex;
height: 300px;
}
.funnel>div:last-child div {
background: green;
flex: 1;
}
.funnel:not( .no-mask)>div:last-child div {
mask-image: var( --mask);
}
<div class="funnel">
<div>
Funnel (Mask)
</div>
<div>
<div data-from="1" data-to="0.66667"></div>
<div data-from="0.66667" data-to="0.25"></div>
<div data-from="0.25" data-to="0.1"></div>
</div>
</div>