Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for stepNext and stepPrev for in-slide "animations": enables for custom animations, SVG animations, video play/pause, etc... #80

Closed
wants to merge 5 commits into from

Conversation

twitwi
Copy link
Contributor

@twitwi twitwi commented Apr 25, 2012

Hi,

As quickly mentioned on the mailing list, I extended deck.js to allow for interesting animation-like extensions.

An demo of such extension (SVG animations) can be found there:
http://home.heeere.com/data/deck-js-demo/examples/svg-animations.html

This pull-request offers the cleaned version of my changes.
Feel free to pull it (or not) or to discuss it.

Thanks,
Rémi

…ons [backport]

* stepNext is stepping within a slide (if meaningfull) while the existing "next" is skipping the current slide and going to the next one
* select some key binding for those (borrowed from the "next")
* there is also a stepPrev to undo animations
* the in-slide animations can be defined in a dedicated "pre" element in the slide itself
* animations are optionally init-able and undo-able
* animations are initialized and undone in reverse order
…ly (or refreshing) a slide even if it has svg animations [backport]
…a jasmine test failed by previous modifications)
@imakewebthings
Copy link
Owner

Hi Rémi,

Sorry it's taken so long to address this. Busy times, and all that. Here's what I'd like to get out of this thread: What do I need to provide in core that could make this work as an extension.

At least one change will be necessary, and I want to make it regardless of this ticket anyway. You need a way to hijack the next and previous actions and run your step code instead of change slides. There IS a way to prevent slide change right now, but it's just awful. Currently if you prevent default inside of a deck.change handler, it will then just fire another deck.change back to the from slide. Trying to manage an alternate action within this workflow is a mess.

I'd like to add an event that fires before deck.change, called (big surprise) deck.beforeChange. If the event is defaultPrevented from within this handler deck.change is never fired. Within your aSVG extension, you could keep track of the animations for each slide, check if you still have animations to run, and prevent if you do. This should also remove the need for separate key bindings, since you'll just be hijacking the core ones for prev/next. And it should let other next-calling actions trigger the animations, like clicking the navigation arrows.

Can you think of anything else I'm missing that would need to be changed in core to facilitate this?

@twitwi
Copy link
Contributor Author

twitwi commented May 9, 2012

Hi Caleb,
I know what busy times look like :)

I understand your motivation to keep the core as simple as possible. I originally started my implementation touching the core minimally but I ended up doing dirty stuffs like overriding the next() method (maybe by ignorance on how to do it differently). To overcome this overriding, the deck.beforeChange event might be sufficient, I guess.

Nevertheless, if I try to decompose the pull request, there are the following main aspects:
(1) having a per-slide animation container (list)
(2) executing custom code within the slide to fill the animation list
(3) having stepNext/stepPrev to play animations forward and backward
(4) having a mechanism to wait for asynchronous loading (e.g., svg loading) before executing initialization of animations (they might access the asynchronously loaded resources)

For (1) and (2) I guess that as you said there is no problem to move it as a “animation-controller” extension (provided there is deck.beforeChange).

For (3) and the associated bindings, I actually see it more as a end-user requirement than a hack to play animations.
As a presenter/speaker I don't see a nested slide (or an animation) as a new slide but rather as a "step" within the parent slide. Hence, I used to have (and ported to deck.js) the distinction next/stepNext. I also like to keep the concept in the UI, allowing to skip the current slide or to step within it. This allows also to not increase the slide number when stepping (maybe increase a subslide number): huge, artificial slide numbers may be confusing for the audience.

Overall for (3), I really like the concept and think it would fit in the core... but then it might try to pull (1) with it. Making (3) as an extension would only require a way to add the stepNext (and stepPrev) and to override the key bindings.

For (4), I would need to think more about it to see how it could fit out of the core... I am open to your insights.

Rémi

@nemec
Copy link

nemec commented May 18, 2012

Rémi,

I fully understand your issues with large slide counts (no one wants to see "1/100" when someone starts up their presentation), but unless I'm misunderstanding you couldn't you just use extra subslides for your animations instead of bolting on "steps"?

You can use the extension deck.events.js to bind javascript to a slide, which I believe would work for each of your animations.

  1. I'm not really sure what you mean by "animation container". If it's just a <canvas> or other element, couldn't you place it in the parent slide and use subslides to manipulate it?
  2. As mentioned before, deck.events.js executes custom code.
  3. If you take a look at [Enhancement] Reversible Events mikeharris100/deck.events.js#1, I pasted a reversibleEvent function that analyzes when a slide becomes next, previous, and current and calls one of two functions. The rev function should ideally completely reverse whatever the fwd function does (the demo is still up if you want to try it). The deck.event.js code that fixes the issue isn't committed at the moment, but I'll be doing that soon.

This does not let you "skip" animations, since they have the same weight as a regular slide, so we'd need to find a solution to that.

@imakewebthings
Copy link
Owner

Replies for @twitwi first:

As a presenter/speaker I don't see a nested slide (or an animation) as a new slide but rather as a "step" within the parent slide. Hence, I used to have (and ported to deck.js) the distinction next/stepNext. I also like to keep the concept in the UI, allowing to skip the current slide or to step within it.

That's completely reasonable. But if you want wholly separate methods and UI elements for going through steps, you don't even need beforeChange. You should be able to do that now, by using extend to add functions to deck.js, extending the default options and creating new key bindings on deck.init, and all the normal stuff the existing extensions do right now to add their functionality. I don't see what part of this code needs to live in core to achieve this goal.

This allows also to not increase the slide number when stepping (maybe increase a subslide number): huge, artificial slide numbers may be confusing for the audience.

Yup, but just so you know this is already an option: countNested. It's true by default, but you can set it to false in the initial options:

$.deck('.slide', {
  countNested: false
})

For (4), does the loading of external SVG resources behave any different than the loading of, say, a large img? Seems like you would just want to run the initializer line $.deck('.slide') within a window.load handler if you want all the resources to be loaded beforehand.

@nemec:

I think what should and shouldn't be considered a "slide" is open to interpretation. But this isn't the first time I've had a request to basically override the next action in order to do something else, and there are cases where making them subslides make no sense. How about a video. Let's say a presenter has bound a presentation remote to the next/prev key bindings, and they have a slide with a video in it. What if they just want to press the next button on the remote to play the video. Next again to make it stop. And then one last time to advance the slides. This seems more natural than having to create another key binding to play/pause in-slide videos, or make the presenter walk over to the computer.

That said, I could see Rémi implementing this using any of the methods we've gone over so far:

  • An extension that uses normal subslides, but adds some function to skip to the next "real" slide.
  • An extension that uses the proposed beforeChange to short-circuit the slide change and execute his step code instead.
  • An extension that just provides completely separate functions and key bindings for going through steps.

And I agree that using deck.events would make any of these resulting extensions more readable and easier to organize.

@nemec
Copy link

nemec commented May 18, 2012

Subslides is a poor term for it, I agree, but as I mentioned before your video example (if javascript can start/pause the video) can be handled by deck.events without making the presenter use different keybindings. Just add something like this:

<div class="slide" id="video-slide">
  <embed src="video-url"></embed>
  <div class="slide" id="play-video"></div>
  <div class="slide" id="pause-video"></div>
</div>

<script>
$("#play-video").bind('deck.becameCurrent', function(ev, direction) {
  playVideo();
});
$("#ause-video").bind('deck.becameCurrent', function(ev, direction) {
  pauseVideo();
});
</script>

When you hit the "next" key to move to the play-video and pause-video slides, the corresponding javascript is run automatically. It requires no other actions on the presenter's part.

I did not know about countNested -- that's nifty.

@imakewebthings
Copy link
Owner

@nemec Yep, that's how I would do it personally as well. And in this theoretical video plugin, I might inject the necessary pseudo-slides in the beforeInit handler so the author doesn't have to write them. But from @twitwi's last comment it appears he specifically wants separate methods, keybinding, and entities from the usual slides. In that case I think all the suggestions we've both made are mute, and it really comes down to refactoring the changes into a proper extension.

I do still plan on adding this beforeChange event though, for other reasons. Currently if you want to "prevent" a change you can call event.preventDefault() inside the change handler, but the results are disgusting. Since you don't know if the change is prevented until after all the handlers run, you have to fire another change event reversing the to and from to undo everything. It works on the surface, but I don't like it. And in the case of things like deck.events you end up getting fired events when there wasn't "really" a change. If you prevent you end up getting a reverse becameCurrent that you probably don't want.

Plus it would be nice to have this event fire on any go call, even if the to isn't valid. Like when you press next on the last slide. Currently this will exit the go function before the change event fires, since the to index is out of bounds. This change would make looping trivial:

$(document).bind('deck.beforeChange', function(event, from, to) {
  var onLast = from === $.deck('getSlides').length - 1;
  var isNextAction = to === from + 1;

  if (onLast && isNextAction) {
    $.deck('go', 0);
  }
});

@twitwi
Copy link
Contributor Author

twitwi commented May 21, 2012

Hi @imakewebthings and @nemec,
Sorry for not being able to read this before, I'll try to digest that and provide my conclusions.

@twitwi
Copy link
Contributor Author

twitwi commented May 21, 2012

I didn't know about neither deck.events.js and the countNested option: these are very nice. I think, I can refactor things to use these two in a proper way.

@nemec, what I called animation container was basically a list of animations for a slide.
@imakewebthings, about SVG loading: I need to check but the SVG are loaded using jquery-svg, which I think is asynchronous. Would an asynchronous loading delay the window.load event? Is there a better way than jquery-svg to load an SVG and put it in the DOM?

Related to my "animations" (i.e., the evented sub-slides), they can be "executed", "reversed" but also "inited".
The idea of "initialisation" is that if you have animations that make things appear, it is a good default to hide these stuffs when initially showing the top-level slide (e.g. http://home.heeere.com/data/deck-js-demo/examples/svg-animations.html#slide-1). This initialization could actually be implemented either in deck.events.js or separately (just going through the subslides and sending an initialized event).

I actually use the play/pause actions where a first "next" is starting a video the next "next" stops it and the following one e.g. changes slide. This is indeed very convenient and was a motivation (outside SVG animations) of my animation code.

Overall, I would like to integrate at best, using subslides (with countNested=false) and deck.events.js.
Here are my plans, please let me know your inputs:
1- keep the core deck.js intact
2- have a "step" extension
3- in "step": extend deckjs with a skipToNextTopLevelSlide function
4- in "step": override the key bindings (and the countNested), making a distinction between next/prev and skipNext/skipPrev (can an extension do that? how to do it properly?)
5- in "step" (or other): have a special class "animation" (that someone can specify in addition to "slide" for a subslide) that will execute the javascript code within it, expecting to get back an object that with do()/undo() methods that the extension will bind properly to the subslide events
6- in "step" (or other): when the top-level slide is shown, call all the possible "init()" methods from subslides (the init method being optionally defined by the objects from previous point)
7- use this new animation backend in my "smartsyntax" (it improved since then but here is an example http://home.heeere.com/data/deck-js-demo/examples/smart-syntax.html )
8- think/experiment again with SVG loading

Cheers

@imakewebthings
Copy link
Owner

Hey @twitwi, a couple quick responses:

Would an asynchronous loading delay the window.load event? Is there a better way than jquery-svg to load an SVG and put it in the DOM?

No, the async load would not prevent the window.load. If you want to prevent the user from navigating a deck until all the SVGs load, it's quite possible. You would bind an event to prevent all deck movement (via beforeChange in the future, change right now). Then create a counter equal to the number of SVG animation slides. jquery-svg provides an onLoad callback for each SVG load, so in that callback you would decrement that counter and then check if it is equal to 0. When it hits 0 that means all the SVGs have loaded, and you can then unbind the prevent function, allowing movement again. If that sounds as confusing in text as I think it does, let me know and I can post an example gist.

4- in "step": override the key bindings (and the countNested), making a distinction between next/prev and skipNext/skipPrev (can an extension do that? how to do it properly?)

You just redefine the defaults for both:

$.extend(true, $.deck.defaults, {
  keys: {
    next: [...],
    previous: [...],
    skipNext: [...],
    skipPrev: [...]
  }
});

I'm going to close this ticket out right now, and open one specifically about implementing beforeChange. But if anyone wants to keep discussing this stuff in the thread that's cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants