5
<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?
Fresheyeball
  • 29,567
  • 20
  • 102
  • 164
  • Show us the code that generates the SVG. I imagine that's where the problem lies. – Robert Longson Feb 16 '16 at 04:48
  • More than likely that the poster is trying to use elm-html for svg elements and getting confused when it doesn't work (there's an [elm-svg](https://github.com/evancz/elm-svg) library) – kakigoori Feb 16 '16 at 05:15
  • Updated with code @RobertLongson – Fresheyeball Feb 16 '16 at 06:12
  • @kakigoori updated with code, also I am using elm-svg and I don't think there is a difference. – Fresheyeball Feb 16 '16 at 06:12
  • 1
    It seems that `Svg.node` and `Html.node` have similar signatures: `> Html.node : String -> List Html.Attribute -> List Html.Html -> Html.Html` `> Svg.node : String -> List Svg.Attribute -> List Svg.Svg -> Svg.Svg ` I checked `Svg.Svg` and `Html.Html` types and both are aliases of `Node`. Since you get different behaviours, for sure the implementations differ. – Adrian Feb 18 '16 at 06:34
  • The aliasing to `Node` for both `Svg.Svg` and `Html.Html` and to `Property` for `Svg.Attribute` and `Html.Attribute` explain why you don't get compilation errors. – Adrian Feb 18 '16 at 06:42

2 Answers2

3

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.

Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • Thank you! However, this doesn't answer any of my 3 questions. – Fresheyeball Feb 17 '16 at 14:13
  • Sorry but it answers all of them in my opinion. The problem is that you import all function when using `(..)`. So they are now available under the name with out the need of the namespace prefix. So `Html.node` and `node` are the same thing. – Andreas Köberle Feb 17 '16 at 14:17
  • Do in the **update** section, I stated as much. I know that the manner of imports and mixing up `Svg.node` and `Html.node`. I get that, but WHY does this matter? What is different under the covers of the `Html` and `Svg` modules that makes this difference important? Why was this not a type error? Could it be a type error? Here are the questions as stated above: 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? – Fresheyeball Feb 17 '16 at 14:27
  • Your answer correctly asserts that `Svg.node` vs `Html.node` was the problem, but does nothing to explain why its the case. – Fresheyeball Feb 17 '16 at 14:27
  • add a sentence on why this not lead to an compile error – Andreas Köberle Feb 17 '16 at 14:48
  • I did. Its in question 3. – Fresheyeball Feb 17 '16 at 16:10
  • Sorry, I mean I did. I add a sentence to my answer. – Andreas Köberle Feb 17 '16 at 19:35
  • Again, you are telling me "what" but not "why". I'm sorry, but that's all I'm interested in, I already know the "what". – Fresheyeball Feb 18 '16 at 22:12
  • up1 for all the work you've done on this so far. Thank you. – Fresheyeball Feb 18 '16 at 22:30
1

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.

Adrian
  • 1,006
  • 2
  • 9
  • 20
  • 1
    The problem is that both functions have the same signature. `Html.Attribute` and `Svg.Attribute` are both aliases for `VirtualDom.Property`. Same for Svg.Svg and `Html.Html` which are just aliases for `VirtualDom.Node`. – Andreas Köberle Feb 18 '16 at 10:46
  • Yes, I know. That's why I said that it might be good to get an warning from compiler. – Adrian Feb 18 '16 at 13:10
  • Interesting, so the reason it fails has to do with the missing `svgNamespace`, but what is that? When I look at the rendered page the source looks no different. – Fresheyeball Feb 18 '16 at 22:29
  • it should have the namespace also: `svgNamespace : Attribute svgNamespace = VirtualDom.property "namespace" (Json.string "http://www.w3.org/2000/svg")` – Adrian Feb 19 '16 at 08:55