diff --git a/design/Port-Architecture.xcf b/design/Port-Architecture.xcf index ffa77f6..ef46f66 100644 --- a/design/Port-Architecture.xcf +++ b/design/Port-Architecture.xcf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95524121472103aed35932fd21660441bf646a8f270e1f26d01061de1213d291 -size 427903 +oid sha256:9b0faf7bb55d609b2de1770999c8c2c9c6cd123b25628041905eec47e7c257e5 +size 517922 diff --git a/design/Slide-Background.xcf b/design/Slide-Background.xcf new file mode 100644 index 0000000..b738226 --- /dev/null +++ b/design/Slide-Background.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d0edb7f4389d888853ea622cd5e56b6993b2c6a6d9c97fd163681f16dedcace +size 3030690 diff --git a/design/Web-Component-Architecture.xcf b/design/Web-Component-Architecture.xcf index 365dfcb..aed6cff 100644 --- a/design/Web-Component-Architecture.xcf +++ b/design/Web-Component-Architecture.xcf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:516611e37aaf6929991373645d1d45b85a0aeeec317530d5cb88a55e8c4c4998 -size 436297 +oid sha256:ecf802e38176f6d8284640a76cf480b9bcdc4ffc530523b8abad6bf029bc32f1 +size 1332441 diff --git a/src/Caterpillar/SlowMain.elm b/src/Caterpillar/SlowMain.elm index b5616aa..abf5f03 100644 --- a/src/Caterpillar/SlowMain.elm +++ b/src/Caterpillar/SlowMain.elm @@ -21,7 +21,6 @@ import Random import RotationSpeed exposing (degPerS) import Speed exposing (pxPerMs, pxPerS) import Task -import Time @@ -577,7 +576,7 @@ view model = isHappy = data.fps - |> Maybe.map (\currentFps -> currentFps >= 20) + |> Maybe.map (\currentFps -> currentFps >= 30) |> Maybe.withDefault True caterpillar = diff --git a/src/Slide/index.html b/src/Slide/index.html index 2f7fca1..5aea626 100644 --- a/src/Slide/index.html +++ b/src/Slide/index.html @@ -16,6 +16,10 @@ href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,700,700i%7CMaitree:200,300,400,600,700&subset=latin-ext" rel="stylesheet" /> + @@ -106,55 +110,69 @@ -
= container 90% /
= 45%; --> -
+

Building Highly Performant Animation in Elm

-

by Abadi Kurniawan

-

Elm Conf 2019

+ +

by Abadi Kurniawan

+

Elm Conf 2019

+
-
+
+
+
-

Journey Through the Animation Lands

+ +
+

Journey Through the
Animation Lands

+
+
-

Goal of Our Journey

+

The Goal

    -
  • Explore ways of doing animation
  • +
  • Explore ideas for ways of doing animation
  • High performant animation
  • Make our caterpillar happy
+
-

NOT the Goal of Our Journey

+

NOT the Goal

    -
  • Build a complete solution
  • +
  • Build a finished solution
  • Teach you best practice
+

The Land of Pure Elm

+
-
- +
+
+ +
-

Elm Animation

+

Single Animation in Elm

+
 type alias Model = { x : Float }
@@ -185,15 +203,19 @@ 

Elm Animation

+
-
- +
+
+ +
-

Elm Animation

+

Multiple Animations in Elm

+
 type alias Model =
@@ -237,37 +259,43 @@ 

Elm Animation

+
-

What's Great

+

Pros

  • Pure functional
+
-

What's not so great

+

Cons

    -
  • Cumbersome to synchronize multiple animations
  • +
  • Cumbersome
  • No browser/hardware optimization
  • Break elm debugger
+

Animation as Side Effect

+

CSS Animation Land

+

Domain Specific Language (DSL) for CSS Keyframe Animations

+

Simple Animation

@@ -276,8 +304,7 @@

DSL

 div
   |> Animation.with
-    [ Animation.translate
-      { x = px 0, y = px 100 }
+    [ Animation.translateY (px 100)
       (second 1)
     , Animation.opacity
       { from = percentage 100, to = percentage 0 }
@@ -310,6 +337,7 @@ 

CSS

+

Sequence of Animations

@@ -319,9 +347,9 @@

DSL

div |> Animation.with [ Animation.sequence - [ Animation.translate { x = px 0, y = px 100 } + [ Animation.translateY (px 100) } (second 1) - , Animation.translate { x = px 100, y = px 0 } + , Animation.translateX (px 100) } (second 2) ] ] @@ -334,12 +362,12 @@

CSS

 @keyframes sequence2902574612 {
-    33% { transform: translate(0px, 100px); }
-    100% { transform: translate(100px, 100px); }
+  33% { transform: translate(0px, 100px); }
+  100% { transform: translate(100px, 100px); }
 }
 
 div {
-    animation: 3s sequence2902574612;
+  animation: 3s sequence2902574612;
 }
                         
@@ -348,6 +376,7 @@

CSS

+

Multiple Animations

@@ -356,12 +385,12 @@

DSL

 [ div
   |> Animation.with
-    [ Animation.translate { x = px 100, y = px 0 }
+    [ Animation.translateX (px 100) }
         (second 2)
     ]
 , div
   |> Animation.with
-    [ Animation.translate { x = px 100, y = px 0 }
+    [ Animation.translateX (px 100) }
         (second 2)
       |> Animation.delay (second 2)
     ]
@@ -373,12 +402,12 @@ 

CSS

 @keyframes sequence2902574612 {
-    33% { transform: translate(0px, 100px); }
-    100% { transform: translate(100px, 100px); }
+  33% { transform: translate(0px, 100px); }
+  100% { transform: translate(100px, 100px); }
 }
 
 div {
-    animation: 3s sequence2902574612;
+  animation: 3s sequence2902574612;
 }
                   
@@ -388,85 +417,92 @@

CSS

+
-
- +
+
+ +
-

CSS Animation

+

CSS Animation

-[ apple 
-  |> Animation.css
-    [ Animation.sequence
 
-      [ Animation.translate 
-        { x = px 500, y = px 0 } 
-        (second 2)
-
-      , Animation.translate 
-        { x = px 0, y = px 300 }
-        (second 2)
-      ]
-    ]
+view model =
+  div []
+    [ apple 
+      |> Animation.css
+        [ Animation.sequence
+
+          [ Animation.translate 
+            { x = px 500, y = px 0 } 
+            (second 2)
+
+          , Animation.translate 
+            { x = px 0, y = px 300 }
+            (second 2)
+          ]
+        ]
 
-, apple 
-  |> Animation.css
+    , apple 
+      |> Animation.css
 
-    [ Animation.translate 
-      { x = px 500, y = px 0 } 
-      (second 2)
-      |> Animation.delay (second 2)
+        [ Animation.translate 
+          { x = px 500, y = px 0 } 
+          (second 2)
+          |> Animation.delay (second 2)
 
-    , Animation.opacity 
-      { from = percentage 100, to = percentage 0 } 
-      (second 2) 
-      |> Animation.delay (second 2)
+        , Animation.opacity 
+          { from = percentage 100, to = percentage 0 } 
+          (second 2) 
+          |> Animation.delay (second 2)
+        ]
     ]
-]
                     
-
+
+
-

The Good

-
-
-
    -
  • Highly performant
  • -
  • Declarative
  • -
+

Pros

+
    +
  • Highly performant
  • +
  • Declarative
  • +
-
+
+
-

The Bad

+

Cons

    -
  • Fire and forget type of animation
  • -
  • Difficult to coordinate multiple animations
  • +
  • Difficult to synchronize multiple animations
-
+
+

Web Animations Land

a.k.a JavaScript Land

-
+
+

Web Animations API

JavaScript API for accessing browser's animation engine

- +
 document.getElementById("apple").animate(
   [
@@ -487,7 +523,8 @@ 

-
+
+

JS Interop

    @@ -496,8 +533,9 @@

    JS Interop

-
+
+
@@ -506,8 +544,9 @@

Port

-
+
+
@@ -517,35 +556,77 @@

( Custom Element )

-
-
-

Our API

-
    -
  • -

    Running an Animation

    - Elm > Keyframes data > JSON Encoder > Web Component's Attribute - > Run Animation -
  • -
  • -

    Listening to Animation

    - Current Animation .onFinish > Web Component dispatchEvent > Elm - onFinish > Msg -
  • -
+
+
+ +
+

Running an Animation

+ +
    +
  • + Elm +
  • +
  • Keyframes
  • +
  • + JSON Encoder +
  • +
  • JSON
  • +
  • + JavaScript +
  • +
  • +
  • + Web Animations +
  • +
  • run()
  • +
+
+
+

The Workflow

+
-
+
+ +
+

Listening to Animation Events

+ +
    +
  • + JavaScript +
  • +
  • animation.onFinish()
  • +
  • + Web Component +
  • +
  • dispatchEvent()
  • +
  • + Elm +
  • +
  • FinishMsg
  • +
+
+
+

The Workflow

+
+
+
+
+
+
-
- +
+
+ +
-

One Apple

-

Elm Code

+

Single Animation

+
 
@@ -565,39 +646,24 @@ 

Elm Code

-

JS Code

- -
-class ElmAnimation extends HTMLElement {
-  connectedCallback() {
-    const animateJson = this.getAttribute("animate");
-
-    if (animateJson) {
-      this.children[0].animate(
-        animationData.keyframes,
-        animationData.options
-      );
-    }
-  }
-}
-                    
-
-
+
+
-
- +
+
+ +
-

Two Apples

-

Elm Code

+

Multiple Animations

+
 
@@ -634,31 +700,38 @@ 

Elm Code

-
+
+
-

The Good

+

Pros

  • Highly performant
  • Declarative
  • -
  • Simple to synchronize
  • +
  • Simpler to synchronize multiple animations
-
+
+
-

The Bad

+

Cons

  • Browser support (only FF and Chrome)
-
+
+
+
-
+
+
+
-
+
+

What's Next?

    @@ -666,21 +739,22 @@

    What's Next?

-
+
+

Links

-

+

Elm Animation Exploration -

+ -
https://github.com/abadi199/elm-animation-exploration
-
+

https://github.com/abadi199/elm-animation-exploration

-
+

Thank you!

+
diff --git a/src/Slide/index.ts b/src/Slide/index.ts index 6f4281b..8baa081 100644 --- a/src/Slide/index.ts +++ b/src/Slide/index.ts @@ -102,24 +102,20 @@ Object.keys(flags).forEach((key: string) => { }); const ws: any = new WebSlides(); -function isVisible(node: HTMLElement | null): boolean { - if (!node) { - return false; - } - +function isVisible(name: string): boolean { + let node = document.getElementById(`${name}Container`); + if (!node) return false; const section = node.closest("section"); if (section) { return section.style.display !== "none"; } - return false; } -let slowCaterpillar: any = null; -function startSlowCaterpillar(node: HTMLElement | null, useStage: boolean) { - if (slowCaterpillar) { - slowCaterpillar.ports.pause.send(false); - } else if (isVisible(node)) { +let slowCaterpillar: any; +function startSlowCaterpillar(name: string, useStage: boolean) { + let node = document.getElementById(name); + if (node) { slowCaterpillar = Slow.Elm.Caterpillar.SlowMain.init({ node, flags: { ...flags, useStage } @@ -127,12 +123,11 @@ function startSlowCaterpillar(node: HTMLElement | null, useStage: boolean) { } } -let fastCaterpillar: any = null; -function startFastCaterpillar(node: HTMLElement | null) { - if (fastCaterpillar) { - fastCaterpillar.ports.pause.send(false); - } else if (isVisible(node)) { - fastCaterpillar = Fast.Elm.Caterpillar.FastMain.init({ +function startFastCaterpillar(name: string) { + console.log("startFastCaterpillar"); + let node = document.getElementById(name); + if (node) { + Fast.Elm.Caterpillar.FastMain.init({ node, flags }); @@ -140,13 +135,15 @@ function startFastCaterpillar(node: HTMLElement | null) { } function startElmApp( - node: HTMLElement | null, + name: string, elmApp: any = null, elmModule: any, startButton: HTMLElement | null = null ) { const actuallyStartTheApp = () => { - if (!elmApp && elmModule && isVisible(node)) { + console.log("actuallyStartTheApp"); + const node = document.getElementById(name); + if (!elmApp && node && elmModule && isVisible(name)) { return elmModule.init({ node, flags: { apple } }); } }; @@ -187,68 +184,68 @@ function slideChangeHandler() { enableSyntaxHighlight(); hideFpsCounter(); - let node = document.getElementById("slowCaterpillar"); - if (isVisible(node)) { - startSlowCaterpillar(node, true); + let name = "slowCaterpillar"; + if (isVisible(name)) { + startSlowCaterpillar(name, true); showFpsCounter(); } - node = document.getElementById("slowCaterpillarAgain"); - if (isVisible(node)) { - startSlowCaterpillar(node, false); + name = "slowCaterpillarAgain"; + if (isVisible(name)) { + startSlowCaterpillar(name, false); showFpsCounter(); } - node = document.getElementById("fastCaterpillar"); - if (isVisible(node)) { - startFastCaterpillar(node); + name = "fastCaterpillar"; + if (isVisible(name)) { + startFastCaterpillar(name); showFpsCounter(); } - node = document.getElementById("elmOneApple"); - if (isVisible(node)) { + name = "elmOneApple"; + if (isVisible(name)) { elmOneApple = startElmApp( - node, + name, elmOneApple, ElmOneApple.Elm.OneApple, document.getElementById("startElmOneAppleButton") ); } - node = document.getElementById("elmTwoApples"); - if (isVisible(node)) { - elmTwoApples = startElmApp( - node, + name = "elmTwoApples"; + if (isVisible(name)) { + elmOneApple = startElmApp( + name, elmTwoApples, ElmTwoApples.Elm.TwoApples, document.getElementById("startElmTwoApplesButton") ); } - node = document.getElementById("jsOneApple"); - if (isVisible(node)) { + name = "jsOneApple"; + if (isVisible(name)) { jsOneApple = startElmApp( - node, + name, jsOneApple, JsOneApple.Js.OneApple, document.getElementById("startJsOneAppleButton") ); } - node = document.getElementById("jsTwoApples"); - if (isVisible(node)) { + name = "jsTwoApples"; + if (isVisible(name)) { jsTwoApples = startElmApp( - node, + name, jsTwoApples, JsTwoApples.Js.TwoApples, document.getElementById("startJsTwoApplesButton") ); } - node = document.getElementById("cssApple"); - if (isVisible(node)) { + name = "cssApple"; + if (isVisible(name)) { cssApple = startElmApp( - node, + name, cssApple, Css.Css.Main, document.getElementById("startCssAppleButton") diff --git a/src/Slide/main.scss b/src/Slide/main.scss index 857d5c8..616d395 100644 --- a/src/Slide/main.scss +++ b/src/Slide/main.scss @@ -1,6 +1,39 @@ @import "webslides/static/css/webslides.css"; @import "webslides/static/css/svg-icons.css"; +@mixin box-shadow() { + box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.25); +} + +h1 { + font-size: 5em; + line-height: 1.3em; +} + +html, +body { + font-family: "Lexend Deca", sans-serif !important; +} + +.background { + z-index: -1; + &.tree { + background-image: url("../images/tree-background.png"); + } + &.css { + background-image: url("../images/css-background.png"); + } + &.js { + background-image: url("../images/js-background.png"); + } + &.elm { + background-image: url("../images/elm-background.png"); + } + &.blue-sky { + background-image: url("../images/slide-background.png"); + } +} + #webslides { cursor: none; &.mousemoving { @@ -17,6 +50,12 @@ } .card-50 { + .aligncenter { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } &.flip { flex-direction: row-reverse; } @@ -27,6 +66,7 @@ padding: 2em; } } + #fpsCounter { &.visible { display: block; @@ -40,10 +80,12 @@ font-size: 3em; font-weight: bold; } + .fullscreen figure { padding: 2em; &.diagram { + @include box-shadow; background-color: #fff; display: flex; justify-content: center; @@ -56,17 +98,10 @@ } } } -.card-50 { - .aligncenter { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - } -} code { width: 100%; + @include box-shadow; pre { font-size: 1.5em; @@ -83,6 +118,7 @@ code { } .preview { + @include box-shadow; background-color: #fff; display: flex; justify-content: center; @@ -90,3 +126,49 @@ code { position: relative; } } + +.flexblock { + &.features { + font-size: 2.5em; + line-height: 100px; + + li { + line-height: 1.2; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + @include box-shadow; + } + } +} + +ul.workflow { + font-size: 2.5em; + li { + list-style: none; + padding: 20px; + border: 8px solid #cecece; + } + + li.arrow { + border: none; + font-size: 0.8em; + position: relative; + margin-left: 50%; + height: 100px; + display: flex; + align-items: center; + padding-left: 30px; + } + li.arrow:before { + content: ""; + background-image: url("../images/arrow-down.png"); + width: 100px; + height: 100px; + background-size: contain; + position: absolute; + background-repeat: no-repeat; + left: 0; + } +} diff --git a/src/images/arrow-down.png b/src/images/arrow-down.png new file mode 100644 index 0000000..1dbbfa0 --- /dev/null +++ b/src/images/arrow-down.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d9c765359b25d0ad857090b957d38f2e831e0535e35dc31ebd8358933d69c03 +size 3910 diff --git a/src/images/css-background.png b/src/images/css-background.png new file mode 100644 index 0000000..689c74b --- /dev/null +++ b/src/images/css-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33b6fa02873819e292b113d36a93fcd69a54fc1f0aaadde79a8c224e79cbd43e +size 1049554 diff --git a/src/images/elm-background.png b/src/images/elm-background.png new file mode 100644 index 0000000..8c4d64a --- /dev/null +++ b/src/images/elm-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f08ec7b432b6138b9c99a7786cb97233a3cca3ef4aedff422f742080e8cd13ee +size 1063002 diff --git a/src/images/elm-conf-logo.png b/src/images/elm-conf-logo.png new file mode 100644 index 0000000..9da564c --- /dev/null +++ b/src/images/elm-conf-logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ee8e6545f363bfa9221c12e7ebd907f08199243b902add64e638d46d5c9c16e +size 73205 diff --git a/src/images/js-background.png b/src/images/js-background.png new file mode 100644 index 0000000..3f9482b --- /dev/null +++ b/src/images/js-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbcb9ac6b73e1c44054f00d125cd1489bf2916b4198d15a3081ddc74f87f8242 +size 1068975 diff --git a/src/images/port-architecture.png b/src/images/port-architecture.png index 159d172..5933b4e 100644 --- a/src/images/port-architecture.png +++ b/src/images/port-architecture.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b016e7c1df2014106752abd57e2a32bef874fae48e2b4d20002b865fc8591e4 -size 239722 +oid sha256:1a4bf4c621748895ccebe2b5c9fce153d85a813a6d678f0ac19f60e3731c9d52 +size 270856 diff --git a/src/images/single-caterpillar.png b/src/images/single-caterpillar.png new file mode 100644 index 0000000..cdc1511 --- /dev/null +++ b/src/images/single-caterpillar.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4620a5450278801e30ffd51e4b9fc603faa6047b42f93526ad0328f3d9d891a3 +size 766023 diff --git a/src/images/slide-background.png b/src/images/slide-background.png new file mode 100644 index 0000000..fe6f259 --- /dev/null +++ b/src/images/slide-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6c423481defd994bd8fbcdfcea176bb4e44734e9aea479566aa45705e5a0abc +size 1036030 diff --git a/src/images/tree-background.png b/src/images/tree-background.png new file mode 100644 index 0000000..5c19aaa --- /dev/null +++ b/src/images/tree-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64c2841f8d4a2503ce2377a39e857d97e78b8366081652e2b06828f2459dfeec +size 1083646 diff --git a/src/images/web-component-architecture.png b/src/images/web-component-architecture.png index 407fe01..1c8c67a 100644 --- a/src/images/web-component-architecture.png +++ b/src/images/web-component-architecture.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e7c964e06e9c2f03378a7eff099ff84dbad367fa1df263c1c971685b2f4b224 -size 242139 +oid sha256:137e107f34585515dca62987d5fc1dd237ef0af1c733e6ca8904cd6a38d7c92c +size 302176