Comment faire une animation de rotation des bordures en pointillés comme les «fourmis en marche»

Je travaille sur une animation CSS qui utilise des «pignons et des chaînes», mais je ne parviens pas à créer une séquence de rotation des frontières «lisse».

Vous pouvez voir dans ce violon Comment (actuellement) j’utilise un pseudo-élément pour générer un effet de «rotation». Cela se fait en «basculant» entre une bordure en tirets blanche et en or pointillé, donnant l’impression que la «bordure tourne».

Ce que j’ai

#one{ -webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */ -moz-animation: rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */ -o-animation: rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */ animation: rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */ } #two{ -webkit-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Safari 4+ */ -moz-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Fx 5+ */ -o-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Opera 12+ */ animation: rotateAntiClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */ position:absolute; top:30px; left:42px; width:80px; } @-webkit-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-moz-keyframes rotateClockwiseAnimation{ 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-o-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-webkit-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @-moz-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @-o-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } /******************************************************************************/ .chain{ height:70px; width:80%; border:5px dashed gold; border-radius:30px; position:absolute; top:30px; left:40px; -webkit-animation: switchGoldBlackBorder 0.8s infinite; /* Safari 4+ */ -moz-animation: switchGoldBlackBorder 0.8s infinite; /* Fx 5+ */ -o-animation: switchGoldBlackBorder 0.8s infinite; /* Opera 12+ */ animation: switchGoldBlackBorder 0.8s infinite; /* IE 10+, Fx 29+ */ } @-webkit-keyframes switchBlackGoldBorder { 0% { border: 5px dashed transparent; } 49% { border: 5px dashed transparent; } 50% { border: 5px dashed gold; } 100% { border: 5px dashed gold; } } @-moz-keyframes switchBlackGoldBorder{ 0% { border: 5px dashed transparent; } 49% { border: 5px dashed transparent; } 50% { border: 5px dashed gold; } 100% { border: 5px dashed gold; } } @-o-keyframes switchBlackGoldBorder { 0% { border: 5px dashed transparent; } 49% { border: 5px dashed transparent; } 50% { border: 5px dashed gold; } 100% { border: 5px dashed gold; } } @keyframes switchBlackGoldBorder { 0% { border: 5px dashed transparent; } 49% { border: 5px dashed transparent; } 50% { border: 5px dashed gold; } 100% { border: 5px dashed gold; } } .chain:after{ content:""; position:absolute; height:70px; border-radius:30px; width:100%; top:-5px; left:-5px; border:5px solid gold; z-index:-1; -webkit-animation: switchBlackGoldBorder 0.8s infinite; /* Safari 4+ */ -moz-animation: switchBlackGoldBorder 0.8s infinite; /* Fx 5+ */ -o-animation: switchBlackGoldBorder 0.8s infinite; /* Opera 12+ */ animation: switchBlackGoldBorder 0.8s infinite; /* IE 10+, Fx 29+ */ } @-webkit-keyframes switchGoldBlackBorder { 0% { border: 5px solid gold; } 49% { border: 5px solid gold; } 50% { border: 5px solid white; } 100% { border: 5px solid white; } } @-moz-keyframes switchGoldBlackBorder{ 0% { border: 5px solid gold; } 49% { border: 5px solid gold; } 50% { border: 5px solid white; } 100% { border: 5px solid white; } } @-o-keyframes switchGoldBlackBorder { 0% { border: 5px solid gold; } 49% { border: 5px solid gold; } 50% { border: 5px solid white; } 100% { border: 5px solid white; } } @keyframes switchGoldBlackBorder { 0% { border: 5px solid gold; } 49% { border: 5px solid gold; } 50% { border: 5px solid white; } 100% { border: 5px solid white; } } 
             

Ainsi, dans la partie inférieure de l’extrait de code, vous pouvez voir comment j’ai généré l’effet de «chaîne en rotation» à l’aide d’images clés.


Ce que j’aimerais

Mon souhait général serait de générer quelque chose comme:

Pensez à une section transversale d’un tapis roulant et à la façon dont les «engrenages à l’extrémité entraînent la ceinture». J’essaie de reproduire ça. (c.-à-d. les morceaux d’or de la bordure en pointillés doivent se trouver dans les creux de l’engin et être tirés par lui)

 #one{ -webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */ -moz-animation: rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */ -o-animation: rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */ animation: rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */ border:5px dashed gold; border-radius:50%; } @-webkit-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-moz-keyframes rotateClockwiseAnimation{ 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-o-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } 
          

mais avec des tirets dorés pour tenir dans les creux de l’engrenage, ainsi que 80% de largeur de l’écran (si cela a du sens).

En fin de compte, je voudrais générer quelque chose comme cette image dépeint:

entrer la description de l'image ici

Voyez comment je veux que la chaîne «tourne»?


Mes problèmes actuels

  • Comme l’animation est “piratée” via l’utilisation d’un pseudo-élément, j’ai eu du mal à synchroniser la rotation de cette “chaîne”.
  • J’apprends toujours l’animation par image clé, donc je suis sûr que ce n’est pas la meilleure méthode pour cela
  • Encore une fois, svg est un nouveau concept pour moi, alors supportez ma réticence à l’utiliser (d’où pourquoi j’utilise css pour la ‘chaîne’)
  • En fin de compte, je veux faire en sorte que le matériel tourne la chaîne, mais en ce moment, elles ressemblent complètement à des animations séparées (et mal faites).

Animation Cog et chaîne:

J’ai complètement refait le code ( CSS et HTML ), c’est maintenant:

  • plus court (en particulier le css)
  • plus simple
  • plus réaliste: corrigé le problème de synchronisation entre la chaîne et les rouages ​​et ajouté un rouage manquant à droite car votre chaîne semblait flotter dans les airs:

DEMO

L’approche est la même, en animant l’angle de rotation pour les rouages ​​et le dash-offset pour le chemin de la chaîne. J’ai modifié le timing entre les deux animations pour donner l’impression que les rouages ​​tiraient la chaîne.

Support du navigateur:

Comme IE ne prend pas en charge l’élément svg animate, j’ai également réalisé cette version de l’animation avec la bibliothèque snap.svg qui prend également en charge IE9 et les versions ultérieures (testées dans IE9 avec crossbrowsertestesting ).

DEMO avec support IE

 var cont = new Snap('#svg'), chain = cont.select('#chain'), cogAcw = cont.select('#cog_acw'), cogCw = cont.select('#cog_cw'), speed = 500; // Lower this number to make the animation faster function infChain(el) { var len = el.getTotalLength(); el.attr({"stroke-dasharray": len/62,"stroke-dashoffset": 0}); el.animate({"stroke-dashoffset": -len/31}, speed, mina.linear, infChain.bind(null, el)); } function rotateAcw(el) { el.transform('r22.5,20,20'); el.animate({ transform: 'r-22.5,20,20' }, speed, mina.linear, rotateAcw.bind( null, el)); } function rotateCw(el) { el.transform('r0,20,20'); el.animate({ transform: 'r45,20,20' }, speed, mina.linear, rotateCw.bind( null, el)); } infChain(chain); rotateAcw(cogAcw); rotateCw(cogCw); 
 svg { width:100%; } 
                    

Que diriez-vous de cette approche? J’utilise SVG pour les engrenages et le convoyeur. Les engrenages tournent selon votre exemple, mais j’utilise la fonction stroke-dasharray et l’animation stroke-dash-offset pour faire bouger la courroie du convoyeur.

Il a fallu un peu de bidouillage pour que la longueur du convoyeur et le timing du tiret soient corrects, ce que vous devrez modifier à nouveau si vous modifiez la taille de l’engrenage ou la longueur du convoyeur.

 #one{ -webkit-animation: rotateClockwiseAnimation 4s linear infinite; /* Safari 4+ */ -moz-animation: rotateClockwiseAnimation 4s linear infinite; /* Fx 5+ */ -o-animation: rotateClockwiseAnimation 4s linear infinite; /* Opera 12+ */ animation: rotateClockwiseAnimation 4s linear infinite; /* IE 10+, Fx 29+ */ } #two{ -webkit-animation: rotateAntiClockwiseAnimation 4s linear infinite; /* Safari 4+ */ -moz-animation: rotateAntiClockwiseAnimation 4s linear infinite; /* Fx 5+ */ -o-animation: rotateAntiClockwiseAnimation 4s linear infinite; /* Opera 12+ */ animation: rotateAntiClockwiseAnimation 4s linear infinite; /* IE 10+, Fx 29+ */ position:absolute; top:30px; left:42px; width:80px; } @-webkit-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-moz-keyframes rotateClockwiseAnimation{ 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-o-keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes rotateClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @-webkit-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @-moz-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @-o-keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @keyframes rotateAntiClockwiseAnimation { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } /******************************************************************************/ #chain { -webkit-animation: conveyor 0.5s linear infinite; /* Safari 4+ */ -moz-animation: conveyor 0.5s linear infinite; /* Fx 5+ */ -o-animation: conveyor 0.5s linear infinite; /* Opera 12+ */ animation: conveyor 0.5s linear infinite; /* IE 10+, Fx 29+ */ } @-webkit-keyframes conveyor { 0% { stroke-dashoffset: -9; } 100% { stroke-dashoffset: 20.06; } } @-moz-keyframes conveyor { 0% { stroke-dashoffset: -9; } 100% { stroke-dashoffset: 20.06; } } @-o-keyframes conveyor { 0% { stroke-dashoffset: -9; } 100% { stroke-dashoffset: 20.06; } } @keyframes conveyor { 0% { stroke-dashoffset: -9; } 100% { stroke-dashoffset: 20.06; } } 
                        

Note: J’ai refait l’ensemble de l’animation en ombrage, car l’utilisation de bordures en pointillés n’a pas une sortie cohérente sur tous les navigateurs.

Travail

.. et Works inter-navigateur.
FF 5+, GC 4+, IE9 +, Safari 4+, Opera 12.1+

Vous pouvez essayer ceci en utilisant box-shadow:

  • Pour faire en sorte que les engrenages aient des dents, utilisez une ombre de boîte avec un rayon de diffusion négatif. La taille de mon équipement était de 50px par exemple, donc pour obtenir un box-shadow rond avec d=8px , j’ai utilisé -46px comme rayon de propagation.

  • J’ai positionné les dents en utilisant des coordonnées géo, et je n’ai utilisé que 8 dents pour simplifier.

  • Maintenant, pour le convoyeur droit, nous devons connaître la distance entre les dents. Nous obtenons cela par les éléments suivants:

  • 2*pi*(gear radius) / no. of teeth 2*pi*(gear radius) / no. of teeth = (pi * r) / 4
    Mine = (55 * 3.1415) / 4 = 43 (environ)
    J’ai pris le rayon comme 55 parce que les dents ont un rayon de 4px et sont à 1px de la circonférence de l’engrenage.

  • Pour aligner les convoyeurs droits supérieur et inférieur, ils doivent être traduits par un multiple quelconque de leur distance. Pour mon matériel, je les traduis par 43px.

L’échafaudage

VIOLON

 body { background: rgba(25,80,175, 0.4); } .rect { height: 116px; width: 401px; border-radius: 58px; position: relative; overflow: hidden; } .rect:before, .rect:after { content: ''; position: absolute; left: 46px; /*50-half width*/ height: 8px; width: 8px; border-radius: 50%; background: transparent; box-shadow: 43px 0 0 0 white, 86px 0 0 0 white, 129px 0 0 0 white, 172px 0 0 0 white, 215px 0 0 0 white, 258px 0 0 0 white, 301px 0 0 0 white; -webkit-animation: apple 0.3s linear infinite; -moz-animation: apple 0.3s linear infinite; animation: apple 0.3s linear infinite; } .rect:before { top: 0px; } .rect:after { bottom: 0px; -webkit-animation-direction: reverse; -moz-animation-direction: reverse; animation-direction: reverse; } @-webkit-keyframes apple { 0% {-webkit-transform: translatex(0px);} 100% {-webkit-transform: translateX(-43px);} } @-moz-keyframes apple { 0% {-moz-transform: translatex(0px);} 100% {-moz-transform: translateX(-43px);} } @keyframes apple { 0% {transform: translatex(0px);} 100% {transform: translateX(-43px);} } .left, .right { content: ''; position: relative; height: 100px; width: 100px; border-radius: 50px; background-color: #222; box-shadow: 0 55px 0 -46px white, 55px 0 0 -46px white, 0 -55px 0 -46px white, -55px 0 0 -46px white, 39px 39px 0 -46px white, -39px -39px 0 -46px white, 39px -39px 0 -46px white, -39px 39px 0 -46px white; -webkit-animation: mango 2.4s linear infinite; -moz-animation: mango 2.4s linear infinite; animation: mango 2.4s linear infinite; } .left { top: -108px; left: 0px; } .right { top: -208px; left: 301px; } @-webkit-keyframes mango { 0% {-webkit-transform: rotate(0deg);} 100% {-webkit-transform: rotate(-360deg);} } @-moz-keyframes mango { 0% {-moz-transform: rotate(0deg);} 100% {-moz-transform: rotate(-360deg);} } @keyframes mango { 0% {transform: rotate(0deg);} 100% {transform: rotate(-360deg);} } 
 

Voici une méthode différente pour réaliser une animation Cog en utilisant CSS. Cette méthode a été testée dans IE11, IE10, Firefox, Chrome, Opera et Safari.

  • Deux éléments circulaires pour engrenages / pignons avec une box-shadow en box-shadow pour produire le cercle intérieur. Les dents sont produites par des éléments enfants (normal + pseudo) qui tournent autour de l’axe.
  • La partie incurvée de la ceinture est obtenue en utilisant la même technique que les rayons de l’engrenage et est positionnée de telle manière qu’elle se trouve toujours entre les dents.
  • Un élément conteneur rectangular dont les bordures supérieure et inférieure sont imitées à l’aide d’un dégradé linéaire. L’arrière-plan de cet élément (autre que le dégradé en haut et en bas) est une couleur unie qui constitue en quelque sorte un inconvénient. Cette couleur unie est utilisée pour cacher la moitié de l’élément circulaire de chaque côté.
  • L’animation est réalisée de deux manières (a) en faisant constamment pivoter les deux éléments circulaires et (b) en changeant constamment la position de l’arrière-plan des arrière-plans dégradés.
 .chain { margin: 45px auto; height: 100px; width: 310px; position: relative; background: -webkit-linear-gradient(0deg, gold 50%, transparent 50%), -webkit-linear-gradient(0deg, gold 50%, transparent 50%), white; background: -moz-linear-gradient(90deg, gold 50%, transparent 50%), -moz-linear-gradient(90deg, gold 50%, transparent 50%), white; background: linear-gradient(90deg, gold 50%, transparent 50%), linear-gradient(90deg, gold 50%, transparent 50%), white; background-size: 41px 5px; background-repeat: repeat-x; background-position: 20px 0px, 20px 95px; -webkit-animation: bgPos 1s infinite linear; -moz-animation: bgPos 1s infinite linear; animation: bgPos 1s infinite linear; } .belt, .belt-after, .belt .spokes, .belt .spokes:before, .belt .spokes:after, .belt-after .spokes, .belt-after .spokes:before, .belt-after .spokes:after { position: absolute; content:''; height: 90px; width:15px; top: 0px; border-top: 5px solid gold; border-bottom: 5px solid gold; z-index: -1; } .belt, .belt-after { -webkit-animation: borderAnim 8s infinite linear; -moz-animation: borderAnim 8s infinite linear; animation: borderAnim 8s infinite linear; } .belt .spokes, .belt-after .spokes { top: -5px; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); } .belt .spokes:before, .belt-after .spokes:before { top: -5px; -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); transform: rotate(90deg); } .belt .spokes:after, .belt-after .spokes:after { top: -5px; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); } .belt { left: -16px; } .belt-after { right: -16px; } .gear { content:''; position: absolute; top: 5px; height: 90px; width: 90px; border-radius: 50%; -webkit-animation: borderAnim 8s infinite linear; -moz-animation: borderAnim 8s infinite linear; animation: borderAnim 8s infinite linear; box-shadow: inset 0px 0px 0px 30px gray; z-index: 4; } .gear:before, .gear .spokes, .gear .spokes:before, .gear .spokes:after { position: absolute; content:''; height: 88px; width:15px; top: -5px; border-top: 6px solid gray; border-bottom: 6px solid gray; } .gear:before { left: 37px; -webkit-transform: rotate(22.5deg); -moz-transform: rotate(22.5deg); transform: rotate(22.5deg); } .gear .spokes { left: 37px; -webkit-transform: rotate(67.5deg); -moz-transform: rotate(67.5deg); transform: rotate(67.5deg); } .gear .spokes:before { top: -6px; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); } .gear .spokes:after { -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); transform: rotate(90deg); } .chain .belt + .gear { left:-52px; } .chain .belt-after + .gear { right: -52.5px; } .gear-small { content:''; position: absolute; left: -92px; top: -20px; height: 50px; width: 50px; border-radius: 50%; -webkit-animation: borderAnim 6s infinite linear reverse; -moz-animation: borderAnim 6s infinite linear reverse; animation: borderAnim 6s infinite linear reverse; box-shadow: inset 0px 0px 0px 20px gray; z-index: -2; } .gear-small:before { position: absolute; content:''; left: 21px; top: -3px; height: 48px; width: 10px; border-top:4px solid gray; border-bottom: 4px solid gray; } .gear-small .spokes, .gear-small .spokes:before, .gear-small .spokes:after { position: absolute; content:''; left: 21px; top: -3px; height: 48px; width: 10px; border-top:4px solid gray; border-bottom: 4px solid gray; } .gear-small .spokes { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); } .gear-small .spokes:before { left: 0px; -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); transform: rotate(90deg); } .gear-small .spokes:after { left: 0px; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); transform: rotate(45deg); } @-webkit-keyframes borderAnim { 0% { -webkit-transform: rotate(360deg); } 100% { -webkit-transform: rotate(0deg); } } @-moz-keyframes borderAnim { 0% { -moz-transform: rotate(360deg); } 100% { -moz-transform: rotate(0deg); } } @keyframes borderAnim { 0% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } } @-webkit-keyframes bgPos { 0% { background-position: 20px 0px, -20px 95px; } 100% { background-position: -20px 0px, 20px 95px; } } @-moz-keyframes bgPos { 0% { background-position: 20px 0px, -20px 95px; } 100% { background-position: -20px 0px, 20px 95px; } } @keyframes bgPos { 0% { background-position: 20px 0px, -20px 95px; } 100% { background-position: -20px 0px, 20px 95px; } } 
 

You could try and edit cog so it fits better instead of tweaking div border to fall into place on cog. Its easier to manipulate graphics than css.

And from there maybe to split chain animation in three or four parts to make it more robust.

Then you could tweak speeds of cog and chain to match, hide a half of chain, add onto it div only with top and bottom border and do the same but opposite on other end. (using cliping, position and z-index).

Quelque chose comme ça:

xhtml

In theory at least, that would be my approach (not to mention that I would use JS instead of this workflow).

Using Canvas

The shapes (cog and chain) and the marching ants animation effect (dashed border) can also be achieved by using a Canvas drawing. The browser support for Canvas is quite good .

While Canvas has the disadvantage of being raster based (as opposed to SVG, which is shape based), it is not a big problem as long as the canvas is not scaled too much. Canvas is expected to be better when handling a large number of objects and real-time animations. Here is an interesting article from the MSDN on when to use Canvas or SVG.


Construction of Shapes

The following are the key parts/shapes involved in this animation:

  • Chaîne
  • Left Cog
  • Right Cog
  • Top Cog

Chain : The chain is created by drawing two horizontal lines (using the lineTo commands) that are connected at either end by semicircles (drawn using the arc command). The dashed border effect is achieved by using setLineDash method for the stroke. setLineDash method takes two parameters where the first represents the length of the dash and the second represents the gap between dashes.

Below snippet shows the minimal code required to create the chain:

 window.onload = function() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var chain = { offset: 0, paint: function() { ctx.beginPath(); ctx.moveTo(75, 50); ctx.lineTo(533, 50); ctx.arc(533, 100, 50, (Math.PI * 1.5), (Math.PI * 0.5), false); ctx.lineTo(75, 150); ctx.arc(75, 100, 50, (Math.PI * 0.5), (Math.PI * 1.5), false); ctx.lineWidth = 5; ctx.fillStyle = 'transparent'; ctx.setLineDash([12, 14.16]); ctx.lineDashOffset = this.offset; ctx.fill(); ctx.stroke(); } }; chain.paint(); } 
 /* CSS needed only for demo */ body { background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%); } canvas { margin: 50px auto; }