2

I have a very simple path within a SVG.

<svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 61 57" 
    version="1.1" x="0px" y="0px">
    <defs>
        <style type="text/css"><![CDATA[
            ]]>
        </style>
    </defs>
    <path id="pipe" d="
        M8.0178,52.0035
        L27.0178,52.0035
        C42.4568,52.0035 55.0178,39.4425 55.0178,24.0035
        L55.0178,8.0035
        L43.0178,8.0035
        L43.0178,24.0035
        C43.0178,32.8255 35.8398,40.0035 27.0178,40.0035
        L8.0178,40.0035
        L8.0178,52.0035
        Z">
    </path>
</svg>

(Preview: https://i.stack.imgur.com/FfBRg.png)

What I'd like to achive is that I have 3 separate gradients or filling spaces:

  • The first one is from the inner curve to the center of the bended tube (curve).
  • The second one is the center area.
  • The third one from the center area to the outer curve of the tube.

Alternatively I could also use a single gradient with multiple stop colors.

The following image illustrates the wanted result: https://i.stack.imgur.com/I4CNa.png In this case the rectangles I added illustrate the gradient that I want to use along the whole curve.

I did some research regarding advanced gradients in SVG but I was not able to understand how to apply them or if that is even necessary. I understand how to apply radial and linear gradients to rectangles or even to curves but that did not deliver the expected result.

I also found Can I apply a gradient along an SVG path? which creates a gradient in the tube from left to right (so to say) and I'd like it from top to bottom.

Do you guys have any ideas how to solve this?

2 Answers2

7

You can may get the result you want by using filters with blur or lighting. Here is a good article on advanced filters: https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 150 150" >
    <defs>
        <filter id="filter1">
            <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blurOut" />
            <!-- We cut off the parts that overlap the source graphic… -->
            <feComposite operator="in" in="blurOut" in2="SourceAlpha" result="COMPOSITE"/>
            <!-- … and then merge source graphic and lighting effect: -->
            <feMerge>
                <feMergeNode in="SourceGraphic" />
                <feMergeNode in="COMPOSITE"/>
            </feMerge>
        </filter>

        <!-- https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/ -->
        <filter id="filter2">
            <!--We create a heightmap by blurring the source: -->
            <feGaussianBlur stdDeviation="5" in="SourceAlpha" result="BLUR"/>
            <!-- We then define a lighting effect with a point light that is positioned at virtual 3D coordinates x: 40px, y: -30px, z: 200px: -->
            <feSpecularLighting surfaceScale="6" specularConstant="1" specularExponent="30" lighting-color="#white" in="BLUR" result="SPECULAR">
                <fePointLight x="40" y="40" z="2000" />
            </feSpecularLighting>
            <!-- We cut off the parts that overlap the source graphic… -->
            <feComposite operator="in" in="SPECULAR" in2="SourceAlpha" result="COMPOSITE"/>
            <!-- … and then merge source graphic and lighting effect: -->
            <feMerge>
                <feMergeNode in="SourceGraphic" />
                <feMergeNode in="COMPOSITE"/>
            </feMerge>
        </filter>
    </defs>

    <path stroke="white" stroke-width="20" fill="none" filter="url(#filter1)" 
          d="M-90,50 h150 a20,20 0 0,0 20,-20 v-150" />

    <path stroke="black" stroke-width="20" fill="none" filter="url(#filter2)" 
          d="M-40,100 h150 a20,20 0 0,0 20,-20 v-150" />
</svg>
Sphinxxx
  • 12,484
  • 4
  • 54
  • 84
  • 1
    For future work I will keep this in mind. Right now I have already several "straight" pipes that are already using gradients which will look the curved pipe look strange when it's connected to other pipes. I'd have to change all the created pipes to use filters aswell. Nice trick though. – Daniel Mierswa Sep 13 '17 at 05:11
  • Can filters solve this kind of problem too: https://stackoverflow.com/q/14633363/3310334 ? – theonlygusti Jan 08 '23 at 14:55
  • @theonlygusti Not as far as I know.. – Sphinxxx Jan 08 '23 at 17:07
2

In general it is not possible to create gradients that flow along a path.

However, in cases like yours which only involve straight pieces and circular arcs, you can achieve the effect by breaking the shape up into those sections. Then you apply a different gradient to each part. You use a <linearGradient> for the straight sections, and a <radialGredient> for the curved sections.

In the example below, I have used a very simplified gradient for the "pipe" effect. You will probably wish to add more stops to yours to give a better 3D effect.

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 61 57" 
    version="1.1" x="0px" y="0px">
    <defs>
      <linearGradient id="horizontalPipe" x2="0" y2="1">
        <stop offset="0" stop-color="white"/>
        <stop offset="0.25" stop-color="black"/>
        <stop offset="0.75" stop-color="black"/>
        <stop offset="1" stop-color="white"/>
      </linearGradient>
      <linearGradient id="verticalPipe">
        <stop offset="0" stop-color="white"/>
        <stop offset="0.25" stop-color="black"/>
        <stop offset="0.75" stop-color="black"/>
        <stop offset="1" stop-color="white"/>
      </linearGradient>
      <radialGradient id="curvedPipe" cx="0" cy="0" r="1">
        <stop offset="0.57" stop-color="white"/>
        <stop offset="0.677" stop-color="black"/>
        <stop offset="0.893" stop-color="black"/>
        <stop offset="1" stop-color="white"/>
      </radialGradient>
    </defs>
    
    <rect x="8" y="40" width="19" height="12" fill="url(#horizontalPipe)"/>
    <path d="M 27,40 A 16,16, 0,0,0 43,24 H 55 A 28,28, 0,0,1, 27,52 Z" fill="url(#curvedPipe)"/>
    <rect x="43" y="8" width="12" height="16" fill="url(#verticalPipe)"/>
</svg>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • This looks great. I was wondering if there's a generalized solution with advanced gradients as specified in the SVG proposal (I think it was related to meshes). I was trying to figure out how the SVG commands in the path are related to the rectangle and also how the offsets in the radialGradient are related to the path. Do you have any pointers? – Daniel Mierswa Sep 13 '17 at 05:10
  • The radial gradient is defined so that the centre of the gradient is at the top left (cx=0, cy=0). The offset values in gradients are basically percentages. The 0.57 value for the first offset corresponds to the ratio 16 (inner radius of the pipe) to 28 (outer radius). 16/28=0.57. So the gradient starts at 0.57 of the way to the outer radius. The 0.677 and 0.893 correspond to 0.25 and 0.75 (respectively) of the distance from 0.57 to 1.0 (end of the gradient, and outer radius). – Paul LeBeau Sep 13 '17 at 10:02