Skip to content

Custom Property

Animate any numeric CSS property with a unit. This is an escape hatch for CSS properties not covered by the first-class property modules.

Module: Anim.Property.Custom

GPU Accelerated: No — the only GPU-accelerated numeric property is opacity, which has its own first-class module.

Example

View Example

View Source Code
module Animation.Transition.BorderRadius.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Transition as Transition
import Anim.Property.Custom as Property
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Motion.Easing as Easing exposing (Easing(..))



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = always Sub.none
        }



-- MODEL


type alias Model =
    { animState : Transition.AnimState }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            Transition.init
                [ Property.init animGroup (Property.BorderRadius Px) 0 ]
      }
    , Cmd.none
    )



-- ANIMATION


animGroup : String
animGroup =
    "radiusAnim"


standardTiming : Property.Builder { prop | withTiming : () } -> Property.Builder { prop | withTiming : () }
standardTiming =
    Property.duration 800
        >> Property.easing CubicInOut


roundCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
roundCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 48
        >> standardTiming
        >> Property.end


squareCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
squareCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 0
        >> standardTiming
        >> Property.end



-- UPDATE


type Msg
    = TriggerRound
    | TriggerSquare


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        TriggerRound ->
            ( { model
                | animState =
                    Transition.animate model.animState <|
                        Transition.for animGroup
                            >> roundCorners
              }
            , Cmd.none
            )

        TriggerSquare ->
            ( { model
                | animState =
                    Transition.animate model.animState <|
                        Transition.for animGroup
                            >> squareCorners
              }
            , Cmd.none
            )



-- VIEW


view : Model -> Html Msg
view model =
    div [ class "example-stage" ]
        [ div [ class "example-controls" ]
            [ button
                [ onClick TriggerRound
                , class "ui-action-button primary"
                ]
                [ text "Round" ]
            , button
                [ onClick TriggerSquare
                , class "ui-action-button primary"
                ]
                [ text "Square" ]
            ]
        , div
            [ style "width" "100%"
            , style "display" "flex"
            , style "align-items" "center"
            , style "justify-content" "center"
            , style "padding-top" "10px"
            ]
            [ div
                (Transition.attributes animGroup model.animState
                    ++ [ class "example-box"
                       , style "background-color" "#6366f1"
                       ]
                )
                []
            ]
        ]
module Animation.Keyframe.BorderRadius.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Keyframe as Keyframe
import Anim.Property.Custom as Property
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Motion.Easing as Easing exposing (Easing(..))



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = always Sub.none
        }



-- MODEL


type alias Model =
    { animState : Keyframe.AnimState }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            Keyframe.init <|
                [ Property.init animGroup (Property.BorderRadius Px) 0 ]
      }
    , Cmd.none
    )



-- ANIMATION


animGroup : String
animGroup =
    "boxAnim"


standardTiming : Property.Builder { prop | withTiming : () } -> Property.Builder { prop | withTiming : () }
standardTiming =
    Property.duration 800
        >> Property.easing CubicInOut


roundCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
roundCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 48
        >> standardTiming
        >> Property.end


squareCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
squareCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 0
        >> standardTiming
        >> Property.end



-- UPDATE


type Msg
    = TriggerRound
    | TriggerSquare


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        TriggerRound ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        Keyframe.for animGroup
                            >> roundCorners
              }
            , Cmd.none
            )

        TriggerSquare ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        Keyframe.for animGroup
                            >> squareCorners
              }
            , Cmd.none
            )



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage" ]
        [ Keyframe.styleNode model.animState
        , div [ class "example-controls" ]
            [ button
                [ onClick TriggerRound
                , class "ui-action-button primary"
                ]
                [ text "Round" ]
            , button
                [ onClick TriggerSquare
                , class "ui-action-button primary"
                ]
                [ text "Square" ]
            ]
        , div
            [ style "width" "100%"
            , style "display" "flex"
            , style "align-items" "center"
            , style "justify-content" "center"
            , style "padding-top" "10px"
            ]
            [ div
                (Keyframe.attributes animGroup model.animState
                    ++ [ class "example-box"
                       , style "background-color" "#6366f1"
                       ]
                )
                []
            ]
        ]
module Animation.Sub.BorderRadius.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Sub as Sub
import Anim.Property.Custom as Property
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Motion.Easing as Easing exposing (Easing(..))



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }



-- MODEL


type alias Model =
    { animState : Sub.AnimState }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            Sub.init <|
                [ Property.init animGroup (Property.BorderRadius Px) 0 ]
      }
    , Cmd.none
    )



-- ANIMATION


animGroup : String
animGroup =
    "radiusAnim"


standardTiming : Property.Builder { prop | withTiming : () } -> Property.Builder { prop | withTiming : () }
standardTiming =
    Property.duration 800
        >> Property.easing CubicInOut


roundCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
roundCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 48
        >> standardTiming
        >> Property.end


squareCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
squareCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 0
        >> standardTiming
        >> Property.end



-- UPDATE


type Msg
    = GotSubMsg Sub.AnimMsg
    | TriggerRound
    | TriggerSquare


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotSubMsg subMsg ->
            let
                ( newAnimState, _ ) =
                    Sub.update subMsg model.animState
            in
            ( { model | animState = newAnimState }
            , Cmd.none
            )

        TriggerRound ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        Sub.for animGroup
                            >> roundCorners
              }
            , Cmd.none
            )

        TriggerSquare ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        Sub.for animGroup
                            >> squareCorners
              }
            , Cmd.none
            )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub.Sub Msg
subscriptions model =
    Sub.subscriptions GotSubMsg model.animState



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage" ]
        [ div [ class "example-controls" ]
            [ button
                [ onClick TriggerRound
                , class "ui-action-button primary"
                ]
                [ text "Round" ]
            , button
                [ onClick TriggerSquare
                , class "ui-action-button primary"
                ]
                [ text "Square" ]
            ]
        , div
            [ style "width" "100%"
            , style "display" "flex"
            , style "align-items" "center"
            , style "justify-content" "center"
            , style "padding-top" "10px"
            ]
            [ div
                (Sub.attributes animGroup model.animState
                    ++ [ class "example-box"
                       , style "background-color" "#6366f1"
                       ]
                )
                []
            ]
        ]
port module Animation.WAAPI.BorderRadius.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.WAAPI as WAAPI
import Anim.Property.Custom as Property
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Json.Encode as Encode
import Motion.Easing as Easing exposing (Easing(..))



-- PORTS


port motionCmd : Encode.Value -> Cmd msg


port motionMsg : (Encode.Value -> msg) -> Sub msg



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }



-- MODEL


type alias Model =
    { animState : WAAPI.AnimState Msg }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            WAAPI.init motionCmd motionMsg <|
                [ Property.init animGroup (Property.BorderRadius Px) 0 ]
      }
    , Cmd.none
    )



-- ANIMATION


animGroup : String
animGroup =
    "radiusAnim"


standardTiming : Property.Builder { prop | withTiming : () } -> Property.Builder { prop | withTiming : () }
standardTiming =
    Property.duration 800
        >> Property.easing CubicInOut


roundCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
roundCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 48
        >> standardTiming
        >> Property.end


squareCorners : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
squareCorners =
    Property.begin (Property.BorderRadius Px)
        >> Property.to 0
        >> standardTiming
        >> Property.end



-- UPDATE


type Msg
    = GotWaapiMsg WAAPI.AnimMsg
    | TriggerRound
    | TriggerSquare


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotWaapiMsg waapiMsg ->
            let
                ( newAnimState, _ ) =
                    WAAPI.update waapiMsg model.animState
            in
            ( { model | animState = newAnimState }
            , Cmd.none
            )

        TriggerRound ->
            let
                ( newAnimState, cmd ) =
                    WAAPI.animate model.animState <|
                        WAAPI.for animGroup
                            >> roundCorners
            in
            ( { model | animState = newAnimState }
            , cmd
            )

        TriggerSquare ->
            let
                ( newAnimState, cmd ) =
                    WAAPI.animate model.animState <|
                        WAAPI.for animGroup
                            >> squareCorners
            in
            ( { model | animState = newAnimState }
            , cmd
            )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    WAAPI.subscriptions GotWaapiMsg model.animState



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage" ]
        [ div [ class "example-controls" ]
            [ button
                [ onClick TriggerRound
                , class "ui-action-button primary"
                ]
                [ text "Round" ]
            , button
                [ onClick TriggerSquare
                , class "ui-action-button primary"
                ]
                [ text "Square" ]
            ]
        , div
            [ style "width" "100%"
            , style "display" "flex"
            , style "align-items" "center"
            , style "justify-content" "center"
            , style "padding-top" "10px"
            ]
            [ div
                (WAAPI.attributes animGroup model.animState
                    ++ [ class "example-box"
                       , style "background-color" "#6366f1"
                       ]
                )
                []
            ]
        ]

Basic Usage

View Source Code
import Anim.Property.Custom as Property exposing (Property(..))
import Anim.Unit exposing (Unit(..))

borderRadiusAnimation : AnimBuilder eng -> AnimBuilder eng
borderRadiusAnimation =
    Property.begin (BorderRadius Px)
        >> Property.to 24
        >> Property.end

See the Properties Overview page for the shared usage patterns.

When to use Anim.Property.Custom

Use this module when Elm Motion doesn't provide a first-class module for the CSS property you need to animate. For color-based properties, use Anim.Property.CustomColor instead.

API

Types

Type Description
Builder Alias for the Internal builder used to configure the animation
AnimGroupName Alias for the animation group name
Property Typed property names

Initialization

Function Signature Description
init AnimGroupName -> Property -> Float -> AnimBuilder eng -> AnimBuilder eng Set the initial value — takes group name, CSS property (with unit embedded), and value

Build

Function Signature Description
begin Property -> AnimBuilder eng -> Builder eng Start building — takes CSS property (with unit embedded), using the currently selected animation group
end Builder eng -> AnimBuilder eng Finish building

Start Value

Function Signature Description
from Float -> Builder eng -> Builder eng Start value

End Value

Function Signature Description
to Float -> Builder eng -> Builder eng Absolute End value
by Float -> Builder eng -> Builder eng Relative End value

Timing

Function Signature Description
delay Int -> Builder { eng | withTiming : () } -> Builder { eng | withTiming : () } The delay in ms before the animation starts
duration Int -> Builder { eng | withTiming : () } -> Builder { eng | withTiming : () } The duration in ms that the animation lasts for
speed Float -> Builder { eng | withTiming : () } -> Builder { eng | withTiming : () } The rate of change per second

Easing

Function Signature Description
easing Easing -> Builder eng -> Builder eng Add natural motion

Spring

Function Signature Description
spring Spring -> Builder { eng | withSpring : () } -> Builder { eng | withSpring : () } Use spring physics instead of easing

Next Steps

The Custom Color Property.

Custom Color Property →