impress / impress.js

It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com.
http://impress.js.org
MIT License
37.62k stars 6.67k forks source link

Add relative move and rotate to rel plugin #794

Closed thawk closed 2 years ago

thawk commented 3 years ago

The relative position in rel plugin is currently based on the world coordinate. So for the same effect, like fly in from the right-hand side, we must use difference data-rel-x/y/z value. Why not let the plugin do the hard part?

So I introduce a data-rel-position, when set to relative, all relative attribute is based on the position and rotation of previous slide. So no matter the rotation of previous slide, data-rel-x="1000" always looks like fly in from the right-hand side. We can change the position and rotation of one slide, and the position of all following slides will be changed too.

When data-rel-position is set to relative, relative rotation has a clear meaning. It describes the relative rotations between slides. We don't need to set rotations for all slide, setting the key slides is enough. If data-rel-position is not relative, the effect of data-rel-rotate-x/y/z is not clear, so they're only used when data-rel-position="relative".

After the introduction of relative rotation, there're 6 attribute that will inherit from previous slide. If we want to set a relative X move, we have to set all other 5 attributes to 0. It's boring. So a data-rel-clear is used to set all 6 attributes to 0, and then the value specified in current slide is applied. I think this attribute should be used in the second slide of a sequence. The first one setup the basic position, the second set the increments, and the following ones inherit from the second slide.

The examples/3D-positions/indaex.html shows some usage. As you can see, the html code of two slide ring is the same, and slides except for the first two in a ring has no position attributes. It work by inheriting the previous one.

BTW, the test/HOWTO.md says the test js should be placed in the same directory of the plugin itself. But it doesn't point aout where to put the corresponding html file. But in karma.conf.js, only test/plugins/*/*.html is served. So the html files should be put into test/plugins/*? Because I want test js put in the same directory of test html, I have to put the test js into test/plugins/rel/ instead of src/plugins/rel/. Maybe we could update the HOWTO.md to describe where to put html files?

This PR invokes a lot math calculations. Basically, the rotation of a slide is translated into the coordinate describing the directions of X/Y/Z axes. And data-rel-x/y/z can be easily calculated by that. The rotations is the hard part, I mainly use the algorithm in the Quaternions and spatial rotation - Wikipedia to compose two and more rotations.

I'm not a math guy, hope I don't make much mistakes.

henrikingo commented 2 years ago

Hi. Sorry for ignoring this great patch, I've been so busy at work this fall.

I took a quick look and am starting to get what you're doing. Also your demo is really nice. Thanks for persisting.

Are you ready from your side? I'll take a deeper look tomorrow/soon.

thawk commented 2 years ago

Thx. I just want to make my life more eaier 😆

I don't have more for this function now. But I am not too good at math, some algorithm is developed by myself, I suspect that there may be some error in corner cases.

For further improvement, if we allow specified the rotate center other than the center of slide, we can avoid calculate the center of next slide manually. It's not too hard to implement, but I don't know if we should put it into the rel plugin, or we should rework on the rel plugin, to add some extension point?

henrikingo commented 2 years ago

I just want to make my life more eaier

I know. But what you have done is pretty brilliant now that I see it coming together. I couldn't figure this out 4 years ago when I developed the original rel plugin.

For further improvement, if we allow specified the rotate center other than the center of slide, we can avoid calculate the center of next slide manually. It's not too hard to implement, but I don't know if we should put it into the rel plugin, or we should rework on the rel plugin, to add some extension point?

Ah right. I actually have an opinion on this. Thanks for reminding...

The attribute data-rel-to="" can be used to make the reference point something else than the previous slide. For example all slides can be relative to the first slide of their section. Now, if you want the reference point to not be a slide, you can still insert an empty slide and use the skip CSS class so the slide is skipped when presenting.

**

But now that you bring this up, there's also #733. If we add sections, I could imagine that the <div> for a new section could allow to set a lot of attributes, and setting the coordinate system to another origin could very well be one such thing.

thawk commented 2 years ago

For the newly added data-rel-inherit, this is due to the need for my presentation. In the presentation, I use 11 slides to make a circle. After some math calculation (using an python script, which uses numpy-quaternion to calculate the relative x/y), we know the relative attributes:

data-rel-rotate-z="32.73" data-rel-x="2059.55" data-rel-y="604.79"

It's quite easy to specify those attributes for the second slide, and the following slides all inherit from it. But in one of those slide, I need to break the sequence by zoom in the slide, then zoom out and resume the sequence to form the circle. How can we resume the sequence? One way is use data-rel-to and double the attributes above again, it works. But if I change my mind to change some relative attributes, I have to change more than one slides. This violates the DRI principle. So I introduce the data-rel-inherit to inherit the relative attributes instead of duplicated them again and again.

The data-rel-inherit can have different target with data-rel-to to increase flexibility. For example, we have two groups of slides: A-B-C-D and E-F-G-H, we can make slide F rel-to E, but rel-inherit B, so the two groups will have the same shape (maybe a square), we can position slide A and E to change the overall position of two groups, and change the attributes of slide B to change the shape of two groups.

This attribute is based on the real need, hope I make it clear.

Maybe I can find some time to make an example.

henrikingo commented 2 years ago

Thanks. I understand the explanation. Let me think about it for a while.

It seems your other PR that was merged, now conflicts with this one for the tests.

henrikingo commented 2 years ago

Ok, so I think what you are trying to do with data-rel-inherit is kind of a form of templating. We could generalize what you are doing and just say that somewhere in the presentation you have a slide:

<div id="slideone" class="step" data-foo="5" data-bar-z="abcd" data-bar-q="99">

Now, later you want to "inherit" some attribute values from this slide to another slide:

<div id="slidefifteen" class="step" data-template-use="slideone" data-foo="6">

This would copy all data-* attributes from slideone, but then set data-foo to 6.

Does this make sense?

thawk commented 2 years ago

Thanks. I understand the explanation. Let me think about it for a while.

It seems your other PR that was merged, now conflicts with this one for the tests.

The conflict is raised because both PR add new tests. I will updtae this PR to resolve it.

thawk commented 2 years ago

Ok, so I think what you are trying to do with data-rel-inherit is kind of a form of templating. We could generalize what you are doing and just say that somewhere in the presentation you have a slide:

<div id="slideone" class="step" data-foo="5" data-bar-z="abcd" data-bar-q="99">

Now, later you want to "inherit" some attribute values from this slide to another slide:

<div id="slidefifteen" class="step" data-template-use="slideone" data-foo="6">

This would copy all data-* attributes from slideone, but then set data-foo to 6.

Does this make sense?

Yes, it's what I want. But I need to clarify that:

  1. Attributes inherited from slide before the template slide, like data-rel-x/data-rel-rotate-z, should be copied too. Those attributes should be specified in the second slide of a stream, and be inherited one by one. We should follow the previous slide in the same stream, instead of the first/second slide in the stream, so we don't need to know where the stream starts.
  2. I don't quite sure whether other attributes should be copied too. We should copy data-rel-x/data-rel-rotate-x even when they are inherited. But I don't think we should copy data-x if it's inherited. With data-rel-inherit, it's a plugin specified attribute, we can control the behaviour in detail, but as data-template-use, it may need more precise design.
henrikingo commented 2 years ago

Sorry for delay. Two weekends of snow blizzard have kept me busy outside.

Attributes inherited from slide before the template slide, like data-rel-x/data-rel-rotate-z, should be copied too. Those attributes should be specified in the second slide of a stream, and be inherited one by one. We should follow the previous slide in the same stream, instead of the first/second slide in the stream, so we don't need to know where the stream starts.

Ok this is a good point. This makes this rather specific to rel plugin. Or even if there was a generic template plugin, it would need access to the internal state of rel plugin. To keep it simple, let's continue within the rel plugin and then if this becomes a popular thing, in the future someone can generalize it.

I don't quite sure whether other attributes should be copied too. We should copy data-rel-x/data-rel-rotate-x even when they are inherited. But I don't think we should copy data-x if it's inherited. With data-rel-inherit, it's a plugin specified attribute, we can control the behaviour in detail, but as data-template-use, it may need more precise design.

So let's probe this a bit more...

In the normal case, both data-x and data-rel-x are inherited from the previous slide, and then of course the effective value of xis essentially the sum of those.

Now if you specify that position should be inherited from some other previous slide, why should that inheritance not work exactly the same?

thawk commented 2 years ago

Thanks. I agree that the current design is a bit more compilicated. I've introduced the following attributes besides data-rel-rotate-*:

I think the problem is we need TWO slide to begin a sequence: the first slide to position (it may has data-x/y/z and data-rotate-x/y/z), the second slide (it may has data-rel-x/y/z and data-rel-rotate-x/y/z) to determine the relative relationship, and the following slides inherits from the second slide. So, I think the data-rel-to may mostly be used for the first slide, and the data-rel-inherit may be used by the second slide, data-rel-inherit may not the same as data-rel-to.

I think it's better to make a sequence in the SAME slide, then we can simplified the data-rel-inherit and data-rel-to. But if we merge the attributes of the first and the second slide into the first one, it will has data-x/y/z and data-rel-x/y/z at the same time, and use data-x/y/z itself, make data-rel-x/y/z to be inherited by the following slide. I think it may be confusing.

We need to think about that we need, and make a reasonable syntax.

henrikingo commented 2 years ago

Thanks for the summary!

Backward compatibility adds some complexity, but it's a necessary evil. IMO the fact that absolute/relative map clearly to the same options in CSS positioning makes this a nice interface to the use to digest.

So I think everything else makes sense, and we should just continue to understand what to do with data-rel-to and data-rel-inherit. I think the way you explain it it sounds logical. However, did you actually create any presentation where you needed to separate the two?

Also, you should ask yourself whether you are over optimizing? Using data-rel-to and especially data-rel-inherit is already the exceptional case. Maybe it's better to have the user repeat a few attributes if needed, rather than introducing one more command that is only slightly different from another command.

henrikingo commented 2 years ago

Just to keep the conversation going: What do you think about the KISS proposal: Just remote data-rel-inherit and merge its functionality with data-rel-to?

thawk commented 2 years ago

Sorry for the late reply. I'm a bit busy and not have much time recentlly due to the COVID-19.

I agree that we can remove data-rel-inherit, and let data-rel-to inherits data-rel-rotate-* also, it can be usefull when we temporary leave the flow and then resume the flow. We can just data-rel-to the previous slide in the flow, works like the out-of-flow slides are not exists. We can use data-rel-reset to get the old behavior.

When we remove data-rel-inherit, we CAN'T inherit data-rel-* from the specified slide without inherit it's position. But I think maybe it isn't a strong need, and can be achieved by copy the attributes again.

thawk commented 2 years ago

The problem confuses me is what we should deal with data-rel-rotate-* when data-rel-position no equals relative, should those attributes be inherited? Not the unittest failed when the data-rel-position is not set, the current implement DON'T inherit data-rel-rotate-* as before data-rel-position was introduced, it make the test result not reasonable. It's behavior is clear when data-rel-position="relative", so it succeed then.

henrikingo commented 2 years ago

Sorry for the late reply. I'm a bit busy and not have much time recentlly due to the COVID-19.

Sorry to hear this. Don't apologize. This is a hobby project and my responses aren't always prompt either.

When we remove data-rel-inherit, we CAN'T inherit data-rel-* from the specified slide without inherit it's position. But I think maybe it isn't a strong need, and can be achieved by copy the attributes again.

Since you've highlighted break from flow as a major use case, it seems you'd want to specify those anyway.

The problem confuses me is what we should deal with data-rel-rotate-* when data-rel-position no equals relative, should those attributes be inherited?

No, I don't see how that could make sense. NOT inheriting seems correct.

thawk commented 2 years ago

Considering the original behaviours, I make the following changes when data-rel-position isn't relative:

The unittest is passed now.

thawk commented 2 years ago

For the slide flow, for example, I have a series of slides, they all inherits the same relative attributes:

Slide1 -> Slide2 -> Slide3 -> Slide4

Some slide may need a detail explain, then return the the original slide. The detail slide is out-of-flow.

Slide1 -> Slide2 -> Slide3    Slide4
                       v        ^
                   Slide3.1 ----+

It may be hard to calculate the position of Slide4 based on Slide3.1, but it's easy to do based on Slide3, so it's better to inherit from Slide3, like the slide flow is not been interrupted.

henrikingo commented 2 years ago

data-rel-to don't inherit data-rel-*. I think it's the orignal behaviour.

Otherwise all following slides would end up in the same position. (Same delta relative to data-rel-to slide.)

henrikingo commented 2 years ago

So is this ready to merge now?

thawk commented 2 years ago

So is this ready to merge now?

Maybe you can check whether the old presentations works as expected, if so, I think it's ready.

henrikingo commented 2 years ago

Good idea. They all look ok.

henrikingo commented 2 years ago

Is your first comment still usable as a commit message? Or do you want to provide a commit message for the squashed commit?

thawk commented 2 years ago

Is your first comment still usable as a commit message? Or do you want to provide a commit message for the squashed commit?

English is not my native language. You decide. I don't mind if you rewrite a new comment.

henrikingo commented 2 years ago

Nor is it mine :-) I was more checking whether it's still accurate after changes. But I'll use it. Thanks.

henrikingo commented 2 years ago

And there it is!

Hey, I just wanted to come back and say THANK YOU. This is probably the coolest, most advanced patch I've seen since I started re-activating impress.js project. If you are interested to work on more stuff, please keep in touch and let me know if there's anything I can do to help?

henrik

thawk commented 2 years ago

Yes, I will keep using impress.js, it's a great presentation tool. I will fire some issues or PR if necessary.