Javascript 为 svg 生成动态剪辑路径

Javascript to generate dynamic clip-path for svg

我正在使用如下所示的 svg 元素

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>


<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">


    <rect class="vBoxRect" width="1280" height="720" fill="none" stroke="red"></rect>
    <rect class="boundRect" x="70" y="70" width="1160" height="600" fill="none" stroke="green"></rect>

    <g class="bound" style="transform: translate(70px, 70px);">
        <g class="yAxis">
            <g class="yAxisLeft" fill="none" font-size="10" font-family="sans-serif">
                <g class="tick" opacity="1" transform="translate(0,411.7647058823529)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">20%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,223.52941176470583)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">40%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,35.29411764705883)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">60%</text>
                </g>
            </g>
        </g>
    </g>
</svg>
</body>
<script type="text/javascript"></script>
</html>

使用 javascript,我打算生成一个 svg,它将在文本后显示每个 yAxis 网格线。例如

为了以编程方式生成它,我的方法是生成一个 clipPath,其中 rectx=text.x+text.width 开始,与 y,width 和 [=20] 相同=] 并将其应用于每个 line.

这是我目前尝试的方法

const svgns = 'http://www.w3.org/2000/svg';

document.querySelectorAll('.tick>line').forEach(
    (a, i) => {
        const defs = document.createElementNS(svgns, 'defs');
        document.querySelector('svg').appendChild(defs);
        const clipPath = document.createElementNS(svgns, 'clipPath');
        clipPath.setAttribute('id', `clip${i}`);
        defs.appendChild(clipPath);
        const data = document.querySelectorAll('.tick>text');
        const rect = document.createElementNS(svgns, 'rect')
        rect.setAttribute('x', `${data[i].getBoundingClientRect().x+data[i].getBoundingClientRect().width}`);
        rect.setAttribute('y', `${a.getBoundingClientRect().y}`);
        rect.setAttribute('height', `${data[i].getBoundingClientRect().height}`)
        rect.setAttribute('width', `${a.getBoundingClientRect().width}`)
        clipPath.appendChild(rect);
        a.style.setProperty('clip-path', `url(#clip${i})`)


    }
)
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>


<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">


    <rect class="vBoxRect" width="1280" height="720" fill="none" stroke="red"></rect>
    <rect class="boundRect" x="70" y="70" width="1160" height="600" fill="none" stroke="green"></rect>

    <g class="bound" style="transform: translate(70px, 70px);">
        <g class="yAxis">
            <g class="yAxisLeft" fill="none" font-size="10" font-family="sans-serif">
                <g class="tick" opacity="1" transform="translate(0,411.7647058823529)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">20%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,223.52941176470583)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">40%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,35.29411764705883)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">60%</text>
                </g>
            </g>
        </g>
    </g>
</svg>
</script>
</html>

我不知道我做错了什么。 return 这不是我想要的。 此外,javascript 中是否存在内置方法 currentyl,它对 svg 个元素执行 union/combine/intersect/subtract 操作?

这对我有用

const svgns = 'http://www.w3.org/2000/svg';

document.querySelectorAll('.tick>line').forEach(
    (a,i)=>{
        const defs = document.createElementNS(svgns, 'defs');
        document.querySelector('svg').appendChild(defs);
        const clipPath = document.createElementNS(svgns, 'clipPath');
        clipPath.setAttribute('id',`clip${i}`);
        defs.appendChild(clipPath);
        const data = document.querySelectorAll('.tick>text');
        const rect = document.createElementNS(svgns, 'rect')
        rect.setAttribute('x', `${data[i].getComputedTextLength()}`);
        //rect.setAttribute('y', `${a.getBoundingClientRect().y}`);
        rect.setAttribute('height', `${data[i].getBBox().height}`)
        rect.setAttribute('width', `${a.x2.baseVal.value-a.x1.baseVal.value}`)
        clipPath.appendChild(rect);
        a.style.setProperty('clip-path',`url(#clip${i})`)

        
            }
        )

const svgns = 'http://www.w3.org/2000/svg';

    document.querySelectorAll('.tick>line').forEach(
        (a,i)=>{
            const defs = document.createElementNS(svgns, 'defs');
            document.querySelector('svg').appendChild(defs);
            const clipPath = document.createElementNS(svgns, 'clipPath');
            clipPath.setAttribute('id',`clip${i}`);
            defs.appendChild(clipPath);
            const data = document.querySelectorAll('.tick>text');
            const rect = document.createElementNS(svgns, 'rect')
            rect.setAttribute('x', `${data[i].getComputedTextLength()}`);
            //rect.setAttribute('y', `${a.getBoundingClientRect().y}`);
            rect.setAttribute('height', `${data[i].getBBox().height}`)
            rect.setAttribute('width', `${a.x2.baseVal.value-a.x1.baseVal.value}`)
            clipPath.appendChild(rect);
            a.style.setProperty('clip-path',`url(#clip${i})`)

            
                }
            )
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>


<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">


    <rect class="vBoxRect" width="1280" height="720" fill="none" stroke="red"></rect>
    <rect class="boundRect" x="70" y="70" width="1160" height="600" fill="none" stroke="green"></rect>

    <g class="bound" style="transform: translate(70px, 70px);">
        <g class="yAxis">
            <g class="yAxisLeft" fill="none" font-size="10" font-family="sans-serif">
                <g class="tick" opacity="1" transform="translate(0,411.7647058823529)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">20%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,223.52941176470583)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">40%</text>
                </g>
                <g class="tick" opacity="1" transform="translate(0,35.29411764705883)">
                    <line stroke="currentColor" x2="1160"></line>
                    <text fill="currentColor" x="0" dy="0.32em">60%</text>
                </g>
            </g>
        </g>
    </g>
</svg>
</body>

</html>

d3 equivalent

const clipRectData =d3.selectAll('.tick>text').nodes();
d3.select('svg')
        .selectAll('defs')
        .data(d3.selectAll('.tick>line'))
        .join('defs')
        .append('clipPath')
        .attr('id', (d, i) => { return `clip${i}` })
        .append('rect')
        .attr('x', (d, i) => {
            return `${clipRectData[i].getComputedTextLength()}`
        })
        .attr('height', (d, i) => {
            return `${clipRectData[i].getBBox().height}`
        })
        .attr('width', (d, i) => {
            return `${d.x2.baseVal.value-d.x1.baseVal.value}`
        })

    d3.selectAll('.tick>line').style('clip-path', (d, i) => {
            return `url(#clip${i})`
        })