Reproduire l’animation d’un personnage en CSS et en GIF
Parce que j’ai quelques one-liners et une méthodologie issus de mon travail sur mon fan-site sur Wiz’n’Liz à partager et que ça pourrait aider à peupler le web de merveilleux sprites injustement oubliés.
Extraire l’animation d’un jeu Mega Drive émulé
Gens KMod
Parce qu’il a la capacité de filtrer certains calques et parce qu’il est bien plus simple d’extraire une animation quand l’émulateur peut faire un screenshot de chaque frame, j’utilise Gens KMod. Le problème c’est qu’il ne tourne que sous fenêtres, sous un UNIX-like il faudra donc tenter de l’émuler, dawg.
ImageMagick
Le but est maintenant d’identifier l’emplacement, la taille de l’animation (deux informations que vous obtiendriez aisément en sélectionnant la zone sous Gimp) et éventuellement la couleur de fond. Une fois fait il suffit de lancer une commande équivalente à :
find . -name ‘*.bmp’ -exec convert {} -crop 12x34+56+78 -transparent #901234 ;
Où 12x34
sont respectivement la largeur et la hauteur de la sélection, +56+78
respectivement sa position horizontale et verticale et #901234
la couleur à remplacer par de la transparence.
Cette commande est destructrice : elle remplace les originaux par les découpes.
Reproduire l’animation en GIF
Reproduire l’animation en GIF se résume à une commande ImageMagick :
convert -delay 4 -loop 1 -layers OptimizePlus *.bmp animation.gif
Le délai est exprimé en centième de seconde. J'ai ici choisi 4 pour correspondre au système PAL en faisant du 25 images par seconde.
D'autre part, il est à noter que cette commande crée une animation optimisée. Si vous veniez, par exemple, à la décomposer, vous n'obtiendriez à partir de la seconde image que la différence d'avec l'image précédente.
Si vous n’avez qu’une seule animation et qu’elle est fixe, alors c’est tout ce dont vous avez besoin.
Cependant si vous avez plusieurs animations fixes, comme dans le cas de la lune ci-dessus, alors pour les enchaîner de façon fluide vous devrez en connaître la durée exacte car il n’y a aucun moyen de détecter la fin d’une animation GIF en Javascript.
Donnez un caractère aléatoire à l’enchaînement et vous aurez probablement une réplique relativement fidèle.
Si vos animations ne sont pas fixes, alors en plus de la durée des animations vous devrez stocker leurs tailles. Suivre les mouvements sur un plan horizontal se résumera alors à soustraire ou ajouter à la position courante la largeur de la prochaine animation.
Seulement, si vous avez plusieurs animations non-fixes et que vous comptez les faire coexister sur plusieurs scènes simultanément alors je vous épargne la surprise de découvrir la gestion qui en est faite par au moins Firefox et Chrome : l’animation « saute ».
Ma théorie est que le navigateur redémarre l’animation à chaque fois qu’elle est appelée autre part. C’est moche et a priori irrésolvable, il faut donc se passer du GIF.
Reproduire l’animation en CSS
Reproduire l’animation en CSS commence par une commande ImageMagick :
montage *.bmp -tile x1 -geometry +0+0 -background Transparent animation.gif
On obtient un sprite horizontal sur fond transparent très facilement utilisable en CSS grâce aux animations. Ainsi pour une animation de 600 millisecondes comprenant 16 frames de 32x16 pixels et à partir d’un sprite horizontal comme celui créé plus haut, le code CSS (sans les préfixes propriétaires) donne quelque chose comme ça :
@keyframes animation {
from { background-position: 0px; }
to { background-position: -480px; }
}
.animation {
width: 32px;
height: 16px;
background: url('animation.gif') 0px bottom no-repeat;
animation: animation 600ms steps(16) 1;
}
En plus de gagner le contrôle sur la durée de l’animation, on peut désormais associer une fonction callback à la fin de l’animation grâce à l’évènement animationend
.
Et, bien entendu, ça résoud le problème des redémarrages intempestifs des animations GIF.
Annexes
Création de l'image d'illustration de l'extraction d'une animation par ImageMagick
L’image d’illustration de l’extraction d’une animation par ImageMagick a elle-même été réalisée en partie par ImageMagick. La partie de gauche a nécessitée les commandes suivantes :
convert -size 1000x808 xc:transparent png32:composition.png
offset=0
for image in *.bmp; do
composite -dissolve 50 -alpha Set -geometry +$offset+$offset $image composition.png composition.png;
convert composition.png -fill none -stroke white -strokewidth 5 -draw "rectangle $((44 + offset)),$((76 + offset)) $((115 + offset)),$((147 + offset))" composition.png;
let 'offset += 15';
done
La première ligne crée une image transparente au format PNG 32 bits de 1000 pixels sur 808. La seconde itère sur tous les .bmp
du répertoire courant en incrémentant la variable $offset
de 15 à chaque tour. À l’image composition.png
est ajoutée $image
placée au coordonnées pointées par $offset
. Enfin, on y ajoute un rectangle transparent à bordure blanche de 5 pixels aux coordonnées de la sélection auxquelles on ajoute $offset
.
La seconde est cependant beaucoup plus simple :
montage *.bmp -tile 5x5 -geometry 64x64+1+1 -background none composition-tiled.png
Cela crée un montage sur fond transparent de tous les fichiers .bmp
du répertoire courant en les alignant sur 5 lignes et 5 colonnes et en les séparant d'1 pixel.
Extraire les frames d'une animation GIF
Pour reconstituer les sprites de base des animations CSS j’ai cherché une façon de désassembler les animations GIF déjà créées. Le résultat est un one-liner tellement beau que je n’ai pas pu m’empêcher de l’ajouter aux annexes :
convert animation.gif animation-frame-%d.gif
En fonction du mode d'enchaînement des frames de l'animation, les images extraites peuvent n'être que la différence d'avec l'image précédente (comme vu plus haut avec l'utilisation de l'argument -layers OptimizePlus
. L'ajout d'un simple -coalesce
à l'extraction résoudra le problème.
Remerciements
J’ai été très bien acceuilli et beaucoup aidé par la population du canal IRC ImageMagick sur Freenode, et particulièrement par doub
et b_jonas
qui ont pris de leur temps pour un problème qui n’était même pas lié à ImageMagick mais à GIMP qui gérait mal la transparence des BMP 32 bits. :)