From 99f5615bde162957b7b6b9b9e7440bf6c556956d Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 5 Jun 2020 11:50:22 -0700 Subject: [PATCH] Adds V2 of Confetti without highlighted words --- src/Nri/Ui/Confetti/V2.elm | 197 +++++++++++++++++++++++++++ styleguide-app/Examples/Confetti.elm | 55 ++------ 2 files changed, 208 insertions(+), 44 deletions(-) create mode 100644 src/Nri/Ui/Confetti/V2.elm diff --git a/src/Nri/Ui/Confetti/V2.elm b/src/Nri/Ui/Confetti/V2.elm new file mode 100644 index 00000000..a1ed5ba7 --- /dev/null +++ b/src/Nri/Ui/Confetti/V2.elm @@ -0,0 +1,197 @@ +module Nri.Ui.Confetti.V2 exposing + ( Model, init + , view + , Msg, burst, update, updatePageWidth, subscriptions + ) + +{-| + +@docs Model, init +@docs view +@docs Msg, burst, update, updatePageWidth, subscriptions + +Changes from V1: + + - removes custom words from confetti + +-} + +import Css exposing (Color) +import Html.Styled as Html +import Html.Styled.Attributes as Attributes exposing (css) +import Nri.Ui.Colors.V1 as Colors +import Particle exposing (Particle) +import Particle.System as ParticleSystem +import Random exposing (Generator) +import Random.Float exposing (normal) + + + +-- MODEL + + +{-| -} +type Model + = System (ParticleSystem.System Confetti) Float + + +type Confetti + = Square + { color : Color + , rotations : Float + , rotationOffset : Float + } + + +{-| -} +init : Float -> Model +init center = + System + (ParticleSystem.init (Random.initialSeed 0)) + center + + + +-- VIEW + + +{-| -} +view : Model -> Html.Html msg +view (System system _) = + system + |> ParticleSystem.viewCustom viewConfetti + (Html.div + [ Attributes.style "position" "absolute" + , Attributes.style "top" "0" + , Attributes.style "left" "0" + , Attributes.style "width" "100%" + , Attributes.style "height" "100vh" + , Attributes.style "pointer-events" "none" + ] + ) + + +viewConfetti : Particle Confetti -> Html.Html msg +viewConfetti particle = + let + lifetime = + Particle.lifetimePercent particle + in + case Particle.data particle of + Square { color, rotationOffset, rotations } -> + Html.div + [ css + [ Css.backgroundColor color + , Css.width (Css.px 14) + , Css.height (Css.px 14) + ] + , Attributes.style "position" "absolute" + , Attributes.style "left" "0px" + , Attributes.style "top" "0px" + , Attributes.style "transform-origin" "center" + , Attributes.style "will-change" "transform" + , Attributes.style "transform" <| + "translateX(" + ++ String.fromFloat (Particle.leftPixels particle) + ++ "px) translateY(" + ++ String.fromFloat (Particle.topPixels particle) + ++ "px) rotate(" + ++ String.fromFloat ((rotations * lifetime + rotationOffset) * 360) + ++ "deg)" + ] + [] + + + +-- UPDATE + + +{-| `burst` BURSTS CONFETTI!!! +-} +burst : Model -> Model +burst (System system center) = + System + (ParticleSystem.burst (particlesGenerator center) system) + center + + +particlesGenerator : Float -> Generator (List (Particle Confetti)) +particlesGenerator center = + Random.list 200 <| particleGenerator center squareGenerator + + +squareGenerator : Generator Confetti +squareGenerator = + Random.map3 + (\color rotations rotationOffset -> + Square + { color = color + , rotations = rotations + , rotationOffset = rotationOffset + } + ) + randomHighlightColor + (normal 1 1) + (Random.float 0 1) + + +randomHighlightColor : Generator Color +randomHighlightColor = + Random.weighted + ( 1 / 20, Colors.highlightYellowDark ) + [ ( 1 / 20, Colors.highlightCyanDark ) + , ( 1 / 20, Colors.highlightMagentaDark ) + , ( 1 / 20, Colors.highlightGreenDark ) + , ( 1 / 20, Colors.highlightBlueDark ) + , ( 1.5 / 10, Colors.highlightYellow ) + , ( 1.5 / 10, Colors.highlightCyan ) + , ( 1.5 / 10, Colors.highlightMagenta ) + , ( 1.5 / 10, Colors.highlightGreen ) + , ( 1.5 / 10, Colors.highlightBlue ) + ] + + +particleGenerator : Float -> Generator Confetti -> Generator (Particle Confetti) +particleGenerator center generator = + Particle.init generator + |> Particle.withLifetime (normal 2.5 1) + |> Particle.withLocation (normal center (center / 2) |> Random.map (\x -> { x = x, y = -50 })) + |> Particle.withDirection (Random.constant (degrees 0)) + |> Particle.withSpeed (Random.float 0 1000) + |> Particle.withGravity 600 + |> Particle.withDrag + (\confetti -> + { density = 0.001226 + , coefficient = 1 + , area = 1 + } + ) + + +{-| -} +type alias Msg = + ParticleSystem.Msg Confetti + + +{-| -} +update : ParticleSystem.Msg Confetti -> Model -> Model +update msg (System system center) = + System (ParticleSystem.update msg system) center + + +{-| You will need to watch for page resize events and update the confetti model +with the new width. If you don't, your confetti will be off-center. + +Why is this not part of subscriptions? Your application may already be listening +for browser resize events -- we don't want to double-up listeners unnecessarily. + +-} +updatePageWidth : Int -> Model -> Model +updatePageWidth width (System system _) = + System system (toFloat (width // 2)) + + +{-| -} +subscriptions : (Msg -> msg) -> Model -> Sub msg +subscriptions confettiMsg (System system _) = + ParticleSystem.sub [] confettiMsg system diff --git a/styleguide-app/Examples/Confetti.elm b/styleguide-app/Examples/Confetti.elm index 59d1936d..7babbeaa 100644 --- a/styleguide-app/Examples/Confetti.elm +++ b/styleguide-app/Examples/Confetti.elm @@ -14,8 +14,7 @@ import Example exposing (Example) import Html.Styled as Html exposing (Html) import Html.Styled.Attributes as Attributes exposing (css) import Nri.Ui.Button.V10 as Button -import Nri.Ui.Colors.V1 as Colors -import Nri.Ui.Confetti.V1 as Confetti +import Nri.Ui.Confetti.V2 as Confetti {-| -} @@ -23,13 +22,13 @@ example : Example State Msg example = { name = "Nri.Ui.Confetti.V1" , categories = [ Animations ] - , state = init + , state = Confetti.init 700 , update = update , subscriptions = \state -> Sub.batch [ Browser.Events.onResize WindowResized - , Confetti.subscriptions ConfettiMsg state.confetti + , Confetti.subscriptions ConfettiMsg state ] , view = \state -> @@ -38,21 +37,14 @@ example = , Button.small , Button.secondary ] - , Confetti.view state.confetti + , Confetti.view state ] } {-| -} type alias State = - { confetti : Confetti.Model - } - - -init : State -init = - { confetti = Confetti.init 700 - } + Confetti.Model {-| -} @@ -65,39 +57,14 @@ type Msg {-| -} update : Msg -> State -> ( State, Cmd Msg ) update msg state = - case msg of + ( case msg of LaunchConfetti -> - ( { state | confetti = Confetti.burst [] state.confetti } - , Cmd.none - ) + Confetti.burst state ConfettiMsg confettiMsg -> - ( { state | confetti = Confetti.update confettiMsg state.confetti } - , Cmd.none - ) + Confetti.update confettiMsg state WindowResized width _ -> - ( { state | confetti = Confetti.updatePageWidth width state.confetti } - , Cmd.none - ) - - -getColor : Int -> Color -getColor key = - let - dict = - List.indexedMap (\i c -> ( i, c )) - [ Colors.highlightBlue - , Colors.highlightBlueDark - , Colors.highlightCyan - , Colors.highlightCyanDark - , Colors.highlightGreen - , Colors.highlightGreenDark - , Colors.highlightMagenta - , Colors.highlightMagentaDark - , Colors.highlightYellow - , Colors.highlightYellowDark - ] - |> Dict.fromList - in - Maybe.withDefault Colors.highlightYellow (Dict.get key dict) + Confetti.updatePageWidth width state + , Cmd.none + )