Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Svg masking doesn't render when generated

<svg x="0" y="0" height="2048" width="4096" style="position: absolute; top: 0px; left: 0px; pointer-events: none;">
   <defs>
      <mask x="0" y="0" id="shadowLayerMask">
         <polygon fill="#FFF" points="1042,1578 630,2048 3902,2048 3370,1464"></polygon>
      </mask>
   </defs>
   <rect x="0" y="0" fill="red" mask="url(#shadowLayerMask)" maskContentUnits="userSpaceOnUse" height="2048" width="4096"></rect>
</svg>

Simple right? Here's the thing, if I drop this svg into an html file, the masking works perfectly. But when I generate the same svg with virtual dom, the mask has no effect and we just have a huge red rect.

Annoyingly I can get it to display when generated, if I open the developer tools and add a pointless <defs></defs> to the svg. That seems to kick the svg somehow and remind it that it needs to mask.

Anyone know what is going on here? Is there a workaround that doesn't involve setting a timer to inject an empty defs?

Update:

here is the source

render : Layer -> Html
render { key, shader, mask, size } =
  let
    key' =
      key ++ "LayerMask"

    style' =
      [ "position" => "absolute"
      , "top" => "0px"
      , "left" => "0px"
      , "pointer-events" => "none"
      ]

    hw =
      [ A.height << toString <| getY size
      , A.width << toString <| getX size
      ]

    polygon =
      Svg.polygon
        [ A.fill "#FFF"
        , toPoints mask
        ]
        []

    mask' =
      node
        "mask"
        [ A.x "0", A.y "0", id key' ]
        [ polygon ]

    image =
      Svg.rect
        (A.x "0"
          ::
            A.y "0"
          --   :: A.xlinkHref shader
          ::
            A.fill "red"
          ::
            A.mask (url key')
          ::
            A.maskContentUnits "userSpaceOnUse"
          ::
            hw
        )
        []
  in
    Svg.svg
      (A.x "0" :: A.y "0" :: style style' :: hw)
      [ Svg.defs [] [ mask' ]
      , image
      ]

here are some relevant imports

import Html exposing (..)
import Svg
import Svg.Attributes as A
import Html.Attributes as H exposing (style, id)

Update

Figured it out with help from comments. It was node vs Svg.node. When I changed it to Svg.node the problem went away. Question is:

  1. Why did this fix it?
  2. What is going on under the covers here that makes this important?
  3. Can this be made typesafe so that the problem I experienced could be a compile time error?
like image 397
Fresheyeball Avatar asked Sep 26 '22 09:09

Fresheyeball


2 Answers

Its cause of this 2 lines:

import Html exposing (..)
import Svg

The first one imports all attributes of Html including node the second just import the Svg namespace. So when you are using node in this the environment node is the Html.node. You would get an compile error with this imports:

import Html exposing (..)
import Svg exposing (..)

or this:

import Html exposing (node)
import Svg exposing (node)

cause then, Elm did not know which node you want to use. So its safer to import the functions that you needed and not using (..)

So the main question is why does Html.node accepts List Svg.Attribute without throwing an error. Its cause Svg.Attribute and Html.Attribute are not real types but type aliases for VirtualDom.Property. So for the compiler both are the same type. Same for Htm.Html and Svg.Svg which are both aliases for VirtualDom.Node.

In the end both node functions have the Signature

String -> List VirtualDom.Property -> List VirtualDom.Node -> VirtualDom.Node

so the compiler cant distinguish between them.

like image 62
Andreas Köberle Avatar answered Oct 11 '22 17:10

Andreas Köberle


Just for reference, here is the code for both node functions:

--Html.node
node : String -> List Attribute -> List Html -> Html
node =
    VirtualDom.node


--Svg.node
node : String -> List Attribute -> List Svg -> Svg
node name =
  \attributes children ->
    VirtualDom.node name (svgNamespace :: attributes) children

Maybe the compiler should warn you when this happens.

like image 45
Adrian Avatar answered Oct 11 '22 17:10

Adrian