Search code examples
svgsafarisvg-filters

Safari trouble positioning SVG feSpotlight filter


I want to use a spotlight effect but it only seems to work in Chrome, looks "just ok" in Firefox, but will not position (x,y,z) in Safari. (Other browsers not tested)

I've tried different filter and primitive units, and while this makes a difference, Safari still cannot seem to position the lighting effect in any case.

In pursuit of understanding what is going on, I've tried lots of workarounds including different userSpaceOnUse/objectBoundingBox combos, and different svg structure but have never been able to find one that works on Safari.

Examples

Default filter/primitive units: https://jsfiddle.net/localnerve/y470d52v/

objectBoundingBox units: https://jsfiddle.net/localnerve/uyc7o52k/

A picture is also worth a 1000 words (Safari, Chrome, FF). The spotlight on Safari is rendered off-canvas to the right and bleeds in from the right.

LTR: Safari, Chrome, and Firefox showing the spotlight

To show the spotlight positions on Safari are "out of whack", here's me nudging them in web inspector so I can see the spotlight render at all:

enter image description here

Here is the code using objectBoundingBox filter and primitive units:

<style>
  body {
    margin: 0;
    padding: 0;
    font-family: sans-serif;
    font-size: 16px;
    color: #fff;
  }
  * {
    transform-origin: 50% 50% 0;
  }
  .scene-container {
    display: flex;
    flex-flow: column nowrap;
    justify-content: center;
    align-items: center;
  }
  .scene-container.content {
    position: relative;
    width: 100%;
    height: 150vh;
    background-image: linear-gradient(hsla(240, 90%, 8%, 1) 0%, 99%, hsla(217,24%,71%,0) 100%);
    box-shadow: 0px -10px 20px hsl(240, 90%, 8%);
  }
  .spot {
    height: 100%;
    width: 100%;
  }
</style>

<body>
<div class="scene-container content">
  <h2>Here's a spotlight.</h2>
  <svg class="spot" viewBox="0 0 2000 3000" preserveAspectRatio="xMidYMid">
    <defs>
      <filter x="-0.2" y="-0.2" width="1.4" height="1.4" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" id="spotlight">
        <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"></feGaussianBlur>
        <feSpecularLighting result="specOut" in="blur1" specularConstant="1.8" specularExponent="5" lighting-color="#ffffff">
          <feSpotLight x="0.5" y="-0.4" z="0.03" pointsAtX="0.5" pointsAtY="0.8" pointsAtZ="0" limitingConeAngle="13.7"></feSpotLight>
        </feSpecularLighting>
        <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
      </filter>
      <clipPath id="spot-clip">
        <rect x="-50" y="2840" width="2200" height="200"></rect>
      </clipPath>
      <filter id="spot-blur">
        <feGaussianBlur in="SourceGraphic" stdDeviation="15">
        </feGaussianBlur>
      </filter>
    </defs>
    <g transform="translate(0, -175)" filter="url(#spotlight)">
      <ellipse cx="50%" cy="95.333%" rx="27%" ry="130" fill="#fff" clip-path="url(#spot-clip)" filter="url(#spot-blur)"></ellipse>
      <rect x="25%" y="43.667%" width="50%" height="50%" stroke="peru" stroke-width="3%" stroke-linejoin="round" fill="#000"></rect>
    </g>
  </svg>
</div>
</body>

Any insight you can give is greatly appreciated.


Solution

  • There are known bugs in webkit/Safari for light source positioning wrt transforms and oBB units. After the webkit/blink engine schism, no-one at Apple picked these bugs up from the Chrome team.

    https://bugs.webkit.org/show_bug.cgi?id=88877 https://bugs.webkit.org/show_bug.cgi?id=113059

    The workaround is not to use transforms and oBB units (do any dynamic positioning or sizing via JavaScript)