Search code examples
svggradientsvg-pattern

Apply a gradient to a svg shape filled with a path


I have a svg rectangle filled with a diagonal pattern and I want to apply a gradient on it. So the gradient should span the complete rectangle with the pattern and shouldn't be applied to each pattern item.

This is my code:

import * as React from "react";
import { render } from "react-dom";

const width = 300;
const height = 400;
const patternW = 24;
const patternH = 24;

function createPatternPath(w: number, h: number) {
  const diagonal = `M ${0} ${h / 2}, L ${w / 2} ${0}, 
  M ${0} ${h}, L ${w} ${0}, 
  M ${w / 2} ${h}, L ${w} ${h / 2}`;
  return diagonal;
}


class App extends React.Component<{}> {
  public render() {
    return (
      <svg width={width + patternW} height={height + patternH}>
        <defs>
          <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%">
            <stop offset="0%" stop-color="red"></stop>
            <stop offset="100%" stop-color="blue"></stop>
          </linearGradient>

          <pattern
            id="pat"
            width={patternW}
            height={patternH}
            patternUnits="userSpaceOnUse"
          >
            <path
              d={createPatternPath(patternW, patternH)}
              fill="transparent"
              stroke="url(#grad)"
              stroke-width="1"
              stroke-linecap="square"
              shape-rendering="auto"
            />
          </pattern>

          <mask id="mask">
            <rect width={width} height={height} fill="url(#pat)" />
          </mask>
        </defs>

        <rect
          x={0}
          y={0}
          width={width}
          height={height}
          //fill="url(#pat)"
          //stroke="black"
          fill="url(#grad)"
          mask="url(#mask)"
        />
      </svg>
    );
  }
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

The result is:

enter image description here

As you can see, I'm almost there, I didn't understand why the gradient is so pale and why it seems that each pattern item has the bottom part more transparent than the top one.


Solution

  • You are applying the gradient to the individual pattern template. but if you want the gradient to span the whole patterned area, you only need it there. Instead, set the stroke color of the pattern template to stroke="white". If it is then applied as a luminance mask, the white parts will show the underlying color, while the (transparent black) background will mask out anything else.

          <pattern
            id="pat"
            width={patternW}
            height={patternH}
            patternUnits="userSpaceOnUse"
          >
            <path
              d={createPatternPath(patternW, patternH)}
              fill="transparent"
              stroke="white"
              stroke-width="1"
              stroke-linecap="square"
              shape-rendering="auto"
            />
          </pattern>
    

    Everything else remains the same.