Skip to main content

Studying CSS Animation State and Multiple (Competing) Animations

[2022-08-04 Update] Most of my observations seems confirmed in the CSS animation spec.

I stumped upon this youtube video. Then after some discussion with colleages,  I was really into CSS-only projects.

Of course I'd start with a rotating cube:

Then I built this CSS-only first person (rail) shooter:

It was lots of fun. Especially I learned about the checkbox hack

However there were two problems that took me long to solve.


1. Move from in the middle of an animation

The desired effect is:
  • An element is moving from point A to point B.
  • If something is clicked, the element should move from its current state (somewhere between A and B, in the middle of the animation/transition) to another point C.
This is for the last hit of the boss, the boss is fastly moving. I'd like the boss to slowly move to the center if being hit.

The first try: just set a new animation "move-to-new-direction" when triggered, but it does not work. The new animation starts with the "original location" instead of the "current location".


This is because the old animation was removed in the new rule, so the animation state would be instantly lost.

As a side note, it turned out this is easier to achieve with CSS transition:


However transition does not well for me because I need non-trivial scripted animation.

In order to let the browser "remember the state", I tried to "append a new animation" instead of "setting new animations". Which kind of works.


However it is clear that the first animation is not stopped. Both animations are competing with the transform property, and eventually the box will always stop, due to the second animation. It'd be obvious that the order of the animation matters.

Then I tried to play with the first animation in the triggerd rule. 

The original first animation is "default-move 2s infinite 0s". It turns out that it does not matter too much if I
  • change infinite to 100 (or another large number), and I trigger the change before the animation finished
  • change delay from 0s to 2s (or another multiple of 2s), and I trigger the change after that delay.
However it will be an obvious change if I modify the duration, or change the delay to 1s:


So I figured that the browser will keep the state of "animation name" and "play time". It'd re-compute the state once the new value is set for the animation property. 

To stop the first animation from "competing", I figured that I could just set the state to paused, which works well:


And similary, I should keep all the setting (duration, delay etc) of the first animation, of carefully change it to a "compatible" set.

This basically solves my problem. And finally, actually I realized that the boss should just die at where he's shot, so I'd just pause the current animation. It's also easier than copying all existing animations and adding a new one, in the CSS ruleset.


2. Replaying Animation with Multiple Triggers

This is needed for the weapon firing animation, whenever an enemy is being shot, the "weapon firing" animation should play.

This simple implementation does not work well:


Both input elements work individually, however if the first checkbox is checked before clicking on the second, the animation does not replay.

Now it should be clear why this happens: the browser would remember the state of "animation name" and "play time". This can be verified by tweaking duration and delay:


Especially, it would be a good solution, if I know exactly when the animation should be played (despite of the trigger time). This is not the case for my game, because I want to play the weapon-firing animation immediately after the trigger.

To make both triggers work, one solution is to "append the animation":


But this could be repetitive and hard to maintain for a large number of triggers (on the same element).

A better solution is to define multiple identical @keyframes with different names. Then different triggers can just set individual animation names. The browser would replay the animation because the animation name changes:


I think this one is better because the animation rules can be written with a @for loop in SCSS.

In my game I ended up using multiple (duplicate) elements with individual triggers. All elements are hidden by default. Upon triggered, each element would show up, play the animation, then hide.

One nice thing about this solution, I can still control the "non-firing weapon" with scripted animations, because the triggers will not override the animation CSS property.

Note that here it is assumed the triggers will start in a pre-defined order, otherwise the CSS rules would not work properly.


Conclusion

I find most CSS-only projects fascinating. Pure animatoin is one thing, but some interactive projects, especially games, are quite inspiring.

I'm also wondering if there is any practical value besides the "fun factor". It'd be interesting if we can export some simple 3D models/animations from Blender to CSS.

Comments

Popular posts from this blog

Determine Perspective Lines With Off-page Vanishing Point

In perspective drawing, a vanishing point represents a group of parallel lines, in other words, a direction. For any point on the paper, if we want a line towards the same direction (in the 3d space), we simply draw a line through it and the vanishing point. But sometimes the vanishing point is too far away, such that it is outside the paper/canvas. In this example, we have a point P and two perspective lines L1 and L2. The vanishing point VP is naturally the intersection of L1 and L2. The task is to draw a line through P and VP, without having VP on the paper. I am aware of a few traditional solutions: 1. Use extra pieces of paper such that we can extend L1 and L2 until we see VP. 2. Draw everything in a smaller scale, such that we can see both P and VP on the paper. Draw the line and scale everything back. 3. Draw a perspective grid using the Brewer Method. #1 and #2 might be quite practical. #3 may not guarantee a solution, unless we can measure distances/p...

Chasing an IO Phantom

My home server has been weird since months ago, it just becomes unresponsive occassionally. It is annoying but it happens only rarely, so normally I'd just wait or reboot it. But weeks ago I decided to get to the bottom of it. What's Wrong My system set up is: Root: SSD, LUKS + LVM + Ext4 Data: HDD, LUKS + ZFS 16GB RAM + 1GB swap Rootless dockerd The system may become unresponsive, when the IO on HDD  is persistantly high for a while. Also: Often kswapd0 has high CPU High IO on root fs (SSD) From dockerd and some containers RAM usage is high, swap usage is low It is very strange that IO on HDD can affect SSD. Note that when this happens, even stopping the IO on HDD does not always help. Usually restarting dockerd does not help, but rebooting helps. Investigation: Swap An obvious potential root cause is the swap. High CPU on kswapd0 usually means the free memory is low and the kernel is busy exchanging data between disk and swap. However, I tried the following steps, none of the...

[Updated] Failed Attempt: NixOS in LXC

[Updates: 2024-10-21] I found a working solution: - Disable sandboxing in configuration.nix - Build a tarball image using `nixos-generator -f lxc` - Create an LXC container with `lxc-create -t none` - Modify the config of the LXC container (e.g. specify rootfs path, set unconfined AppArmor) - Create the rootfs directory and remove all POSIX ACL (setfacl --remove-all) - Extract the tarball into rootfs/ I planned to try NixOS in LXC. I have found a few successfull stories: 1 , 2, 3 . However they are all using Proxmox LXC, and/or the image file is for LXD. First, I tried to download the official image via lxc-create. The image can boot, but I have trouble running `nix-channel --update`, which complains about sandboxing. I think it's related to unprivileged LXC containers. Further, as part of the nice feature of NixOS, I cannot easily disable sandbox from there. Second, I tried to build a NixOS image from scratch, using nixos-generators. This is mentioned in the 3rd link above. This ...