From c9e80b9f590a845bbe6f2ac0d3e5d9801d1551e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Granstr=C3=B6m?= <5092565+HugoGranstrom@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:03:38 +0100 Subject: [PATCH] Auto-animation API (#40) * 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 --- docsrc/tutorials/auto_animate.nim | 129 ++++++++++++++++++++---------- src/nimiSlides.nim | 23 +----- src/nimiSlides/autoAnimation.nim | 38 +++++++++ src/nimiSlides/conversions.nim | 15 ++++ 4 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 src/nimiSlides/autoAnimation.nim create mode 100644 src/nimiSlides/conversions.nim diff --git a/docsrc/tutorials/auto_animate.nim b/docsrc/tutorials/auto_animate.nim index f91609a..c97b7ce 100644 --- a/docsrc/tutorials/auto_animate.nim +++ b/docsrc/tutorials/auto_animate.nim @@ -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: """ @@ -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 diff --git a/src/nimiSlides.nim b/src/nimiSlides.nim index 73346cd..2cf80a5 100644 --- a/src/nimiSlides.nim +++ b/src/nimiSlides.nim @@ -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 @@ -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 = """ @@ -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): diff --git a/src/nimiSlides/autoAnimation.nim b/src/nimiSlides/autoAnimation.nim new file mode 100644 index 0000000..50a248c --- /dev/null +++ b/src/nimiSlides/autoAnimation.nim @@ -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 &= "" % [$i] & txt & "" + nbText: result + + + \ No newline at end of file diff --git a/src/nimiSlides/conversions.nim b/src/nimiSlides/conversions.nim new file mode 100644 index 0000000..bee2850 --- /dev/null +++ b/src/nimiSlides/conversions.nim @@ -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 \ No newline at end of file