Skip to content

Commit

Permalink
Auto-animation API (#40)
Browse files Browse the repository at this point in the history
* refactor and implement autoAnimateSlides template

* change name to showAt and introduce showUntil and showFrom

* save progress

* write auto-animate docs

* update to reveal.js version 5.0.4
  • Loading branch information
HugoGranstrom authored Jan 26, 2024
1 parent cbecb1e commit c9e80b9
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 63 deletions.
129 changes: 85 additions & 44 deletions docsrc/tutorials/auto_animate.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,91 @@ Auto-animation is a feature of Reveal.js where two similar slides can be transit
For example when adding a new piece of text to a slide, step-by-step:
"""

codeAndSlides:
autoAnimateSlides(5):
showUntil(3):
nbText: "Shows at slides 1, 2, 3"
showAt(2):
nbText "Shows only at slide 2"
nbText: "Always shown"
showFrom(3):
nbText: "Shows at slides 3, 4, 5"

nbText: hlMd"""
Neat huh? `autoAnimateSlides(5)` will create the `slide:`s for us and it will create 5 slides. These 5 slides will be indexed
1-5 (i.e. 1-indexed). Inside you have three constructs to use:
- `showAt`: only shows its content on the specified slides. You can either give a list of indices or a set. e.g. `showAt(1, 3, 5)` or `showAt({1..3, 6..9})`.
- `showFrom(i)`: only shows its content starting from slide number `i`.
- `showUntil(i)`: only shows its content **up and until** slide number `i`.
"""

nbText: hlMd"""
# Auto-animate paragraphs
The examples above work fine if you want to animate **separate paragraphs** but it doesn't work for animations within paragraphs.
For that use the experimental `showText` construct:
"""

codeAndSlides:
autoAnimateSlides(3):
showText(@[
({}, "Hello"),
({2, 3}, " world"),
({3}, "!")
])

nbText: """
The API is ugly but it is what it is currently. The paragraph is given as a list of tuples.
Each tuple has a string and at which indices that string should be shown. An empty set (`{}`)
means that the string is always visible. `showText` can be used alongside the other auto-animation constructs showcased above.
"""

nbText: hlMd"""
## Tips from the coach
It can be tempting to use cool animations like this all over the place, but I would
advice you to sprinkle it sparingly. Because of the code duplication, the build times may increase if they are used too much.
So I try to use it at most once per slide. But if there is a specific slide that I think could really
shine by having more I'll sprinkle on some more.
Typically I first have a slide with only the header, and then I auto-animate in all the content at once.
And to get the gradual reveal of the content of the slide I use [fragments](./fragments/index.html).
This gives a good balance of movement and simplicity.
"""

codeAndSlides:
autoAnimateSlides(2):
nbText: "# Cool header"

showFrom(2):
fragment:
nbText: "The header moved up and this faded in."
fragment:
nbText: "Some more text."


nbText: hlMd"""
## Caveats and Limitations
The largest caveat is that Reveal.js isn't able to always see that two elements are the same between two slides because of
how nimiSlides generates its HTML. The gist of it is that there must be two elements that are identical between two slides for it to work.
What typically works are:
- Text
- Separate `nbText` blocks
- Separate paragraphs
- Images
What typically doesn't work:
- Code blocks
- Columns
- Advanced blocks (e.g. `typewriter`)
See [Reveal.js docs on auto-animate](https://revealjs.com/auto-animate/#how-elements-are-matched) for detailed explanations.
"""

nbText: hlMd"""
## Old API
This section is here for completeness and it is suggested that you don't use this unless you have to as it is very boilerplatey.
The example slides are pretty good, though so scroll through them.
"""

codeAndSlides:
slide(slideOptions(autoAnimate=true)):
nbText: """
Expand Down Expand Up @@ -75,48 +160,4 @@ codeAndSlides:
fitImage("https://github.com/nim-lang/assets/raw/master/Art/logo-crown.png")
nbText: "Text Below"


nbText: hlMd"""
## Tips from the coach
It can be tempting to use cool animations like this all over the place, but I would
advice you to sprinkle it sparingly. Because of the code duplication, if you have to change something,
you will have to change it in all the slides of the auto-animation.
So I try to use it at most once per slide. But if there is a specific slide that I think could really
shine by having more I'll sprinkle on some more.
Typically I first have a slide with only the header, and then I auto-animate in all the content at once.
And to get the gradual reveal of the content of the slide I use [fragments](./fragments/index.html).
This gives a good balance of movement and simplicity.
"""

codeAndSlides:
slideAutoAnimate:
nbText: "# Cool header"

slideAutoAnimate:
nbText: "# Cool header"
fragment:
nbText: "The header moved up and this faded in."
fragment:
nbText: "Some more text."


nbText: hlMd"""
## Caveats and Limitations
The largest caveat is that Reveal.js isn't able to always see that two elements are the same between two slides because of
how nimiSlides generates its HTML. The gist of it is that there must be two elements that are identical between two slides for it to work.
What typically works are:
- Text
- Separate `nbText` blocks
- Separate paragraphs delimited by newlines in single `nbText`
- Images
What typically doesn't work:
- Code blocks
- Columns
- Advanced blocks (e.g. `typewriter`)
See [Reveal.js docs on auto-animate](https://revealjs.com/auto-animate/#how-elements-are-matched) for detailed explanations.
"""

nbSave
23 changes: 4 additions & 19 deletions src/nimiSlides.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import nimib
import nimib/[capture, config]
import markdown

import nimiSlides/[autoAnimation, conversions]
export autoAnimation, conversions

type
FragmentAnimation* = enum
fadeIn = "fade-in" # the default
Expand Down Expand Up @@ -51,7 +54,7 @@ proc slideOptions*(autoAnimate = false, iframeInteractive = true, colorBackgroun
gradientBackground: gradientBackground,
)

const reveal_version* = "5.0.2"
const reveal_version* = "5.0.4"

const document = """
<!DOCTYPE html>
Expand Down Expand Up @@ -396,24 +399,6 @@ template listItem*(animation: FragmentAnimation, body: untyped) =

template listItem*(body: untyped) =
listItem(fadeInThenSemiOut, body)


proc toHSlice*(h: HSlice[int, int]): HSlice[int, int] = h
proc toHSlice*(h: int): HSlice[int, int] = h .. h


proc toSet*(x: set[range[0..65535]]): set[range[0..65535]] = x
proc toSet*(x: int): set[range[0..65535]] = {x}
proc toSet*(x: Slice[int]): set[range[0..65535]] =
for y in x:
result.incl y
proc toSet*(x: seq[Slice[int]]): set[range[0..65535]] =
for s in x:
result.incl s.toSet()
proc toSet*(x: set[range[0..255]]): set[range[0..65535]] =
for y in x:
result.incl y


template animateCode*(lines: string, body: untyped) =
newNbCodeBlock("animateCode", body):
Expand Down
38 changes: 38 additions & 0 deletions src/nimiSlides/autoAnimation.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import std/[strutils]
import nimib
import ./[conversions]

template autoAnimateSlides*(nSlides: int, body: untyped) =
for autoAnimateCounter {.inject.} in 1 .. nSlides:
slide(slideOptions(autoAnimate=true)):
body

template showAt*(slideNrs: varargs[set[range[0..65535]], toSet], body: untyped) = # how to auto convert to set like in varargs?
# use vararg and union all results!
# This way you don't need to use the set syntax but can pass in `showOn(1, 2, 3)` instead.
var totalSet: set[range[0..65535]]
for x in slideNrs:
totalSet.incl x

if autoAnimateCounter in totalSet:
body
# The if-statement will cause some troubles for some code if it assumes it will be run in the same scope :/
# To fix that we would need to require static inputs

template showFrom*(slideNr: int, body: untyped) =
if autoAnimateCounter >= slideNr:
body

template showUntil*(slideNr: int, body: untyped) =
if autoAnimateCounter <= slideNr:
body

template showText*(text: seq[(set[range[0..255]], string)]) =
var result = ""
for i, (indices, txt) in text:
if indices == {} or autoAnimateCounter in indices:
result &= "<span data-id=\"autoAnimateId-$1\">" % [$i] & txt & "</span>"
nbText: result



15 changes: 15 additions & 0 deletions src/nimiSlides/conversions.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
proc toHSlice*(h: HSlice[int, int]): HSlice[int, int] = h
proc toHSlice*(h: int): HSlice[int, int] = h .. h


proc toSet*(x: set[range[0..65535]]): set[range[0..65535]] = x
proc toSet*(x: int): set[range[0..65535]] = {x}
proc toSet*(x: Slice[int]): set[range[0..65535]] =
for y in x:
result.incl y
proc toSet*(x: seq[Slice[int]]): set[range[0..65535]] =
for s in x:
result.incl s.toSet()
proc toSet*(x: set[range[0..255]]): set[range[0..65535]] =
for y in x:
result.incl y

0 comments on commit c9e80b9

Please sign in to comment.