7

I am trying to get the width and height of an image, so give a URL, what is the width and height of that image

Anis Jonischkeit
  • 914
  • 7
  • 15
  • 1
    Trying how? Do you have a [mcve] you can add to the question? – Robert Longson Jan 31 '18 at 21:53
  • I'm not really sure how to start, I guess maybe my question is more how can I get the size of an image in elm. Javascript I can create an Image() elment and set its url, then onload read its width and height. How can I do something like this in elm without porting out to js – Anis Jonischkeit Feb 01 '18 at 05:57
  • Are you using Elm to target in-browser? If so, may be able to use/adapt this technique somehow (I'm an Elm novice): https://stackoverflow.com/questions/623172/how-to-get-image-size-height-width-using-javascript – Eljay Feb 03 '18 at 17:31

2 Answers2

8

I don't believe that there is a way to do this in elm. Ports are one possible solution. You can read about them here. I've written a small example of your use case which you can run your self on ellie. In this example I use the JS example you gave in your comment, but there are other possible solutions such as event listeners or querying the DOM.

Main.elm

port module Main exposing (main)

import Html exposing (..)
import Html.Attributes exposing (..)


main : Program Never Model Msg
main =
    Html.program
        { init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }


type alias Model =
    { imageUrl : String
    , dim : Maybe ( Int, Int )
    }


testImg : String
testImg =
    "https://images-na.ssl-images-amazon.com/images/I/71TcaVWvBsL._SY355_.jpg"


init : ( Model, Cmd msg )
init =
    Model testImg Nothing
        ! [ getDim testImg ]


type Msg
    = UpdateDim ( Int, Int )


update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
    case msg of
        UpdateDim xy ->
            { model | dim = Just xy } ! []


view : Model -> Html msg
view model =
    case model.dim of
        Nothing ->
            div [] []

        Just dims ->
            div []
                [ img [ src model.imageUrl ] []
                , text <|
                    "size: "
                        ++ toString dims
                ]


subscriptions : Model -> Sub Msg
subscriptions model =
    newDim UpdateDim

-- this port handles our incomming height and width
-- and passes it to a Msg constructor
port newDim : (( Int, Int ) -> msg) -> Sub msg

-- this port passes our string out of Elm and into
-- js land
port getDim : String -> Cmd msg

index.html

<html>
<head>
  <style>
    /* you can style your program here */
  </style>
</head>
<body>
  <script>
    var app = Elm.Main.fullscreen()
    // you can use ports and stuff here
    app.ports.getDim.subscribe(function(url){
      // recieve the url for the image through
      // the `getDim` port in Main.elm
      let img = new Image()
      img.src = url
      img.onload = function() {
        // send the height and width back to elm through
        // the `newDim` port in Main.elm
        app.ports.newDim.send([img.height, img.width])
      }
    })
  </script>
</body>
</html>
Tyler Nickerson
  • 215
  • 1
  • 6
  • 2
    As someone in the slack pointed out, if you are intending to display the image in the dom, you can attach an onload handler in elm and get the size from that so you don’t need the ports. The answer you provided is however the best answer I have found so far if you don’t want to display the image in the dom. – Anis Jonischkeit Feb 11 '18 at 13:45
  • @AnisJonischkeit it would have been helpful to have some details on that approach as another answer. Or at least some kind of link to the slack thread archive. – CoderDennis Oct 19 '18 at 17:13
  • @CoderDennis, I have posted another answer with this solution. – Anis Jonischkeit Oct 19 '18 at 23:51
6

I mentioned in a comment that if you display the image, you can do this without ports. This is the solution I was talking about:

module Main exposing (main)

import Browser
import Debug exposing (toString)
import Html exposing (Html, div, img, text)
import Html.Attributes exposing (src)
import Html.Events exposing (on)
import Json.Decode as Decode


type alias Model =
    { imageDimensions : Maybe ImgDimensions }


initialModel : Model
initialModel =
    { imageDimensions = Nothing }


type Msg
    = ImgLoaded ImgDimensions


update : Msg -> Model -> Model
update msg model =
    case msg of
        ImgLoaded dimensions ->
            { model | imageDimensions = Just dimensions }


type alias ImgDimensions =
    { width : Int
    , height : Int
    }


decodeImgLoad msg =
    Decode.map msg <|
        Decode.field "target" <|
            Decode.map2 ImgDimensions
                (Decode.field "width" Decode.int)
                (Decode.field "height" Decode.int)


view : Model -> Html Msg
view model =
    div []
        [ text <| "Image Loaded = " ++ toString model.imageDimensions
        , img
            [ on "load" (decodeImgLoad ImgLoaded)
            , src "https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/bd07f82e-a30d-4e93-a2cf-0c16ea2b7f40/08-owl-opt.jpg"
            ]
            []
        ]


main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }

https://ellie-app.com/3FCdcDqy4gqa1

Anis Jonischkeit
  • 914
  • 7
  • 15