1.14.0.5
Hummingbird
A modern user interface library for games
ShapeMorphing

Hummingbird has a custom support for morphing shapes. It is done via SVG that has a special format. Example morph:

<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="450px">
<coherent:morph xmlns:coherent="http://coherent-labs.com"
first-frame-group-id="somename"
animation-duration="3s">
<coherent:morphing-keyframe percent="60">
<path fill='blue' stroke='red'
d=" M 308.05 210.7
L 354.7 210.7
L 354.7 348.05
L 308.05 348.05
L 308.05 210.7"/>
</coherent:morphing-keyframe>
<coherent:morphing-keyframe percent="60.01">
<path fill='blue' stroke='red'
d=" M 308.05 210.7
Q 326.3 177.0 354.7 210.7
L 354.7 348.05
L 308.05 348.05
L 308.05 210.7"/>
</coherent:morphing-keyframe>
<coherent:morphing-keyframe percent="100">
<path fill='orange' stroke='yellow' stroke-width="8" fill-opacity="0.8"
d=" M 418.05 322.05
Q 474.4 322.05 530.7 322.05
L 530.7 354.7
L 418.05 354.7
L 418.05 322.05"/>
</coherent:morphing-keyframe>
</coherent:morph>
<g id="somename">
<path fill='orange' stroke-width="5" stroke='yellow'
d=" M 40.05 48.75
L 200.7 48.75
L 200.7 114.75
L 40.05 114.75
L 40.05 48.75"/>
</g>
</svg>

The morph is defined by the tag coherent:morph which has coherent:morphing-keyframe elements inside. Only the first keyframe is defined in a special manner outside the coherent:morph tag as a G element. The reason for this is that all SVG displayers will be able to show the first state of the animation. In order to match the morph with it's first keyframe the value of the coherent:morph attribute first-frame-group-id must match the id of the G containing it.

The animation happens between pair of keyframes, so the first and second keyframes form an animation, the third and fourth form another and so on, we call each of them an animated pair of keyframes, where the first keyframe is the begin keyframe and the second is the end keyframe. Each animated pair must have one or more SVG path elements inside which are animated from the begin to the end keyframe values.

In order for a animation morph to be semantically valid, there are a few rules that must be met

  1. The total number of keyframes must be an even number (this includes the implicit first keyframe), because we animate between pairs of keyframes
  2. Each begin and end keyframe for an animation pair must have the same number of paths
  3. Each animated path must have the same commands in the begin and end keyframe
  4. The allowed commands inside an animated path are Move, LineTo and QubicBezier
  5. Only path elements are allowed inside a keyframe
  6. Each keyframe must have a specified percent value similar to CSS animations (the first keyframe which is the G element has implicit percent="0")

The coherent:morph element has attributes that can specify how the animation is performed which are equivalent to the CSS ones

  • animation-duration
  • animation-ease
  • animation-iteration-count
  • animation-delay
  • animation-fill-mode
  • animation-timing-function
  • animation-play-state

The morph element also has a mandatory first-frame-group-id which must match a G element with the same id

The coherent:morphing-keyframe element has only one attribute percent corresponding to the CSS percent value of the keyframes declaration. Note that since we interpolate between pairs of keyframes we cannot interpolate between second and third keyframe because they might have different topology. For this reason it is advisable to put close percent values as in the example (60 and 60.01). If you leave a larger gap the animation will stay still until the gap time passes, which can be used as an internal animation delay inside the morph if needed.

The SVG can have not animated parts mixed with a morph, so you can have a few elements that are not animated and a morphing that animates. The position of the morphing inside the hierarchy is determine by the place where the G element is positioned inside the SVG not the coherent:morph element.

The morph can be placed as background or mask SVG of an element or as src attribute of an image tag. Currently border SVGs are not supported in general.

The morphing animation can be manipulated through JS in a way similar to the Web Animations API. This can be done through an MorphAnimation JS object which can be acquired by calling

  • element.getBackgroundSVGAnimation
  • element.getBorderSVGAnimation - currently we don't have border SVGs
  • element.getMaskSVGAnimation
  • element.getSrcSVGAnimation - if the element is an image

For Web Animations API reference check the JS documentation.