OrgPad logo

Spring Animations in OrgPad

Created by Pavel Klavík

Preparation of replacing react-motion with animation generated by solving second-order differential equations of the spring motion.

#OrgPad, #development, #mathematics

Spring Animations in OrgPad

Bug where unit is kept playing even after animation is done.

Change in position u → v

We have to animate u towards w such that w rotated is mapped to u. So we apply inverse rotations in the opposite order.

Rendering of rotation centers

Derivative of position

image

Unit position and rotations

We have rotation ordering R1,R2,…,Rk and position u as :unit/pos. The displayed position is RkRk-1...R2R1u. When Rt is finished, we want to remove it from the sequence of all rotations and apply it directly towards u.

Wait till all rotations are finished

Should not take that long and we won't have to deal with permuting rotations.

How is this system solved (INCORRECT SOLUTION)

IMG 20210308 225337

Righthand side should be v-s₁*s, not v

8639C5BB-AC90-4542-A3A4-2248C23CA7F3

Also wait till all unit animations are done

Otherwise we would have to continue unit animation after rotation, which itself would have to be rotated.

Measure sizes in DOM to detect problems with autoresize

Since s_1 is different from s_2, the matrix is regular

unit-wrapper

Zero-sided element placed at :unit/pos and :unit/z.

This leads to solving this system of linear equations

DA41D641-8045-4D16-8B33-9A602A804383

animating box shadow and size

unit

when :unit/changing-pages is set

unit

We can also support collapsing but not stricly necessary

Equation for target position 0

But we can just shift it for another target position, so it should not be a problem.

Overdamped motion

image

Stopping criterium

AC188D36-7C78-4601-A578-A6B02F9214A3

When a unit has currently only a title displayed or only has a content, unit is collapsed with title/content element to simplify DOM

title

content

Just a transparent box of its size. Overflow is set to hidden to deal with larger contents.

we assume that mass m=1

But is it within React motion???

content

Takes the rest of size available in the unit.

title

Just title background which is animated. Overflow is set to hidden to deal with larger titles.

One class for title height

It changes as the unit is opened.

We only use this class for title-bg, the title text is always displayed with the correct title height.

Three cases

image

Derivative of position

image

title-height is animated

content-wrapper

Of zero size positioned in the middle of  currently visible content.

Where d = -c/2

Double eigenvalue.

title-wrapper

Of zero size positioned in the middle of title background.

Content opacity

Fade-out for this. Used for both title and content.

At 80% done

Starts when fade-out is 80% done. It should already be bearly visible, so shrinking the unit little bit should not look strange.

One class for full unit background

It's width and height changes.

At 80% done

The new unit size should be pretty much ok, so we can start fade-in new content.

Content opacity

Fade-in for new content.

damping c

Physics of spring motion

http://www.ambrsoft.com/CalcPhysics/Spring/SpringData.htm

image

Critically damped motion

image

displayed-content-1

...

dispayed-content-n

drag is underdamped, so it creates movement back and forth

c^2-4k = -700

We should probably just increase the value of damping to make it feel better. Also it should be faster than it is currently.

stiffness k

For t=0, we get two equations

A = s

-(c/2)*A+B = v,

so B = v + cs/2

displayed-title-1

...

dispayed-title-n

Old and new content can be visible at the same time, so we need to render both of them

2021-06-10 15-12-02.mp4

Current setup used in OrgPad

image

Underdamped motion

image

Derivative of position

image

switch-page is also underdamped

c^2-4k = -300

touch-velocity is overdamped

c^2-4k = 400

It moves quite slowly towards the target.

CSS animations

https://medium.com/@dtinth/spring-animation-in-css-2039de6e1a03

When t=0

e^(ct) = 1

sin(dt) = 0

cos(dt) = 1

Computation of coefficients A and B

C24C2506-3BA6-4C75-85C1-5B1CED775EAA

aA+Bd

Where a=-c/2 and

image

Dynamically generating CSS in runtime

Tracking should not longer move camera when opened unit is fully contained within visible part of canvas

Opening side panel should not change canvas size anymore, looks strange

Happens automatically when start of an animation is detected

How animations are handled by the browser

Canvas init

We are sending all initial information about canvas, including pos, zoom, size, etc.

Stopping criterium

8F1F21D5-0C15-46AC-A853-E56D877CBA86

Canvas camera changes

For unit tracking, presentations, etc.

We are sending new :canvas/pos and new :canvas/zoom.

Canvas size changes

We are sending new :canvas/width and :canvas/height.

:set-frame true

Whether to set the frame once after the change. Maybe we can just set canvas directly, not calling set-frame function.

We automatically check whether it should be set repeatedly.

:timeout

:link-bbox

Sets bbox of the SVG element and the wrapping div element.

:anim/on-time-percentage

Map from percentage to keyword/vector of Re-frame event.

Animating paths

Seems quite broken in Chrome, so a different solution will be likely needed:

http://hoffmann.bplaced.net/svgtest/index.php?in=attributes#pathd

TODO: Test on mobile browsers as well, but should be fine

Effect for storing changes in canvas

We need to communicate changes in canvas.

New values are stored and style for #root-map is immediately changes.

Which side-effects are there?

:anim/duration

Length of the entire animation.

:anim/start-time

By calling (system-time) at the animation start.

:anim/on-done

A keyword or a vector of Re-frame event which runs when animation is finished.

Re-frame db

:anim/on-target-percentage

Map from percentage to keyword/vector of Re-frame event. It is called approximately, based on computed values.

Animating SVG path via keyframes

https://codepen.io/jorgeatgu/pen/bEEemM

Using animate SVG tag works even in Firefox and Safari

Fast canvas animations

set-frame!

This should be a normal function, not an anim-effect. It will also cause changes changes in values of animated objects.

  1. It replaces existing anim-db with a new version of anim-db, where positions of canvas and all units and links are updated. (This is maybe useful if we would want to stop the animation in the middle, etc. Or maybe we could also skip this. Not sure.)
  2. When canvas is animated, it updates its style.
  3. For all animated units, it updates their position and also elevation shadow.
  4. For all animated links, it updates their path, arrowhead, width and color.
  5. When some animation is still running, it will request running of this function again on the next animation frame.

Updating new canvas pos and zoom

As fast animation is done? Or almost done? It should not take too long, otherwise OrgPad will seem slow.

anim-map

Contains all informations about a single animation.

:param/min-value and :param/max-value

Optional arguments capping the values. Used when outputting current-value and also in frame computation. Not used for source and target values.

This should actually be just a normal anim-event and changes to styles, etc. should just be side-effects

:anim/id

Based on a element for which animation occurs, so it either will be a keyword for special elements like side-panel, or a string like "unit-<id>" for unit animation.

Spring profiles

A map keyed by profile id.

:anim-param/stiffness and :anim-param/damping

Computed from profile or given directly.

Effects of perspective

D50AA9EC-8634-4CA4-AA21-F69B6746C511

anim-effects??

Do we want to keep Re-frame style architecture, so the entire db is stored under the key :db, so other keys are free to run side-effects?

Each function maps anim-effects -> anim-effects

Another input should be system-time and all inputs of the function.

It is wrapped inside another general function which

The function frame-anim/dispatch is passed event described either by a keyword or a vector starting with the keyword. This keyword is used to dispatch an event multi-fn.

  1. Gets the current state from @anim-db/db atom.
  2. Runs the function on it.
  3. Updates anim-db/db according to the output and deals with side-effects.

:frame-anim/set-frame

:anim/params

A map from param ids to params map, containing information for animation of each param which are independent.

:param/spring-profile

Id of the used spring profile.

Animation infrastructure

3AE7D300-7519-4425-8BA8-38A566C69B9E

:param/from-value

This value is only used when there is no ongoing animation for this :anim/id.

:anim-param/spring-type

A keyword representing type of the spring:

Frame-anims package

It is no longer animating just canvas and also fast animations is a confusing name. Frame-animations are much more descriptive since we animate everything frame by frame.

:param/eigenvalues

In case of underdamped spring, we store -c/2 and delta which are real and imaginary parts of an eigenvalue.

Computing mouse position on our own

Since Chrome is stupid, so changing a CSS class causes the events such as on-mouse-over or on-mouse-out to be randomly called.

Initiating unit and link fast animations

:anim-db/canvas

:canvas/anim

When animations are done

We should also update positions of canvas/unit/link to its final position, since it won't happen within set-frame! anymore.

All springs go towards zero, so their current value is their to-value + their spring value

:param/target-value

The value to which the param should be animated.

:param/coefficients

multipliers A and B in the equation

:param/precision

Fixed for a spring profile, so we don't have to store it at each animation

Writing an effect moving a given unit

Major refactoring of full code

anim-db

We have all paths defined. For each path, we have assoc, get and update. They will be used as anim-db/get-unit, etc.

Writing an effect for moving a given link

:anim-db/units

unit-id

:unit/anim

Creating CSS class for the initial/target state.

Adding timeouts for percentages done

Depend on initial conditions: value and velocity at t=0

:spring/multi-prod

Combines multiple different spring types together. The resulting value is created by multiplying the values together.

Adding db like infrastructure for anim-state

:link/anim

How the current from and target values should be stored

Rewriting requestAnimationFrame to run also when a unit or a link is moving

:anim-db/links

link-id

Setting-up initial animation state

:spring/multi-sum

Combines multiple different spring types together. The resulting value is created by summing the values together.

Multispring params

Source and target values are computed for totals, and also :anim-param/from and :anim-param/to, by summing or multiplying the spring targets together.

There is an extra parameter :anim-param/springs which is a map from :spring/id to anim-param map containing from, from-velocities, to and other anim-param values.

CSS style sheet should have as id the animation-id itself, instead of css class id.

And updating it should just update innerHTML instead of adding and removing it. It will be faster.

When used for multispring params

For multispring params only, animates the total value of the anim-param towards new-to-value while changing the value of the spring given in the last component and preserving the other spring targets.

anim-map/play

Animating units in set-frame fn

Animating links in set-frame fn

a few other scalar values??

Likely at least whether requestAnimationFrame function is running right now, maybe a few others??

Moving link arrowhead so it is in the middle of a visible interval

:anim-db/dragged-unit-ids

:anim-db/redrawn-link-ids

Which link-ids are updated by dragging the given set of all units.

:anim-param/diff

Animates the anim-param to its target value increased by new-to-value.

:anim-param/current-diff

Same as :anim-param/diff, but uses the current value instead of the target value.

1) Store current time

Either the passed :anim/current-time, or (system-time) at the beginning.

anim-map/done

5) Compute coefficients for all anim-params and duration

:anim-param/target (default)

Animates the anim-param towards new-to-value.

:anim-state/playing-units and :anim-state/playing-links to speed-up checking whether it should run next, and also to find what should be animated.

:anim-db/dragged-link-ids

Animating immediately as unit is dragged

Animating immediately as link is dragged

When not set, we are dragging canvas instead

:anim-param/id

to-value-type

spring

For multispring params only, it describes which spring is altered.

new-to-value

2) Check whether animation is done

If so, run anim-map/done.

3) When playing, compute current values and velocities

Store them in anim-param maps.

4) Set new targets from given :anim/targets

With respect to target-type and the given spring.

All opacities have to sum to 1 to ensure correct crossfade composition in each frame

How to handle page opacities when switching pages

:anim-param/skip

Sets the value immediately without any animation.

:anim/targets

Each target is a vector consisting of up-to four components.

Blog post about crossfade

https://jakearchibald.com/2021/dom-cross-fade/

Fast Euclidean projection onto the probability simplex - Duchi et al. (2008)

https://doi.org/10.1145/1390156.1390191

duchi2008.pdf

Freeze should stop all computations/updated in frame-anim.

Probability simplex

For n pages, we have opacities w_1,\dots,w_n such that w_i \ge 0, \forall i\in[n] and \sum_{i=1}^n w_i = 1. All solutions form a probability simplex, for example for n=3, it is a triangle in \mathbb R^3.

Screenshot-2023-08-20-115108

Improving debugging

Deal with incorrect clip-path when quickly opening/closing cells

Allow recording and rewinding animations

We can just store all anim-effects state. After recording is done, animation engine will be stopped and rewinding will work with respect to the rendering.

Crossfading animations for switching images??

Switching to page j

It switches the current state towards w_j = 1 and w_i = 0: i\ne j.

The previous state and the current velocity is preserved.

Also sometimes the content disappears altogether. Why is that?

Page opacities

Switching pages should do cross-fade

How to deal with multiple pages switching at the same time

For example, while switching from 1 to 2, a switch to 3 begins. Or the unit will start closing.

Probably happens more frequently than one would expect.

just-image → content-page

Animates :unit/content-opacity towards 1.

Opening page should have opacity 1 for the entire animation

Closing page should have opacity 1 for most of the animation.

Switching between just-image and content page

:unit/content-opacity

Applies opacity on unit DOM node, but not on just-image images/hyperlinks.

content-page → just-image

Animates :unit/content-opacity towards 0

At the end, add crossfade into unit background, so the last ugly part of the animation will be hidden.

The current state of unit animations

Currently looks little bit ugly, but is it important?

It might fade-out little bit before fading in, but this is likely visible only for very similar page content.

We would have to ensure that all page-opacities always sum up to 1. This might be tricky. We could always animate at most k-1 opacities and compute the last opacity so that they add to 1.

Cell with some text

Something written right here and more.

And some other page content is here as well.

And another point is here as well.

Multipage unit with similar content

hockey stick identity image 0

hockey stick identity image 1

hockey stick identity image 2

hockey stick identity image 3

This is a cell with a lot of content and a long heading

doggy smile (1)

crazy dog

crazy dog crossed

Some text written inside the cell so it has a page content.

And with some list:

crazy dog

volume-of-cylinder-01

volume-of-cylinder-02

volume-of-cylinder-03

volume-of-cylinder-04