web-dev-qa-db-fra.com

Comment dessiner progressivement une trajectoire vectorielle? (Raphael.js)

Comment animer progressivement un chemin vectoriel comme s'il était tracé? En d'autres termes, montrez lentement le chemin pixel par pixel.

J'utilise Raphaël.js, mais si votre réponse n'est pas spécifique à la bibliothèque - comme peut-être qu'il y a un schéma de programmation général pour faire ce genre de chose (je suis assez nouveau dans l'animation vectorielle) - c'est la bienvenue!


C'est facile à faire avec des chemins droits, aussi simple qu'un exemple sur cette page ::

path("M114 253").animate({path: "M114 253 L 234 253"});

Mais essayez de changer le code sur cette page, par exemple, de cette façon:

path("M114 26").animate({path: "M114 26 C 24 23 234 253 234 253"});

Et vous verrez ce que je veux dire. Le chemin est certainement animé de son état initial (point "M114 26") à l'état final (courbe "C 24 23 234 253 234 253" commençant au point "M114 26"), mais pas d'une manière spécifiée en question, pas comme il est dessiné.

Je ne vois pas comment animateAlong peut faire cela. Il peut animer un objet le long d'un chemin, mais comment puis-je faire en sorte que ce chemin se montre progressivement pendant que l'objet est animé le long de celui-ci?


La solution?

(Via réponse de Peteorpeter .)

Il semble qu'actuellement la meilleure façon de le faire soit via de "faux" tirets en utilisant le SVG brut. Pour l'explication, voir cette démo ou ce document , page 4.

Comment produire un dessin progressif?

Nous devons utiliser stroke-dasharray et stroke-dashoffset et connaître la longueur de la courbe à tracer. Ce code ne dessine rien à l'écran pour un cercle, une ellipse, une polyligne, un polygone ou un chemin:

<[element] style="stroke-dasharray:[curve_length],[curve_length]; stroke-dashoffset:[curve_length]"/>

Si, dans l'élément d'animation, le trait de course-tiret diminue à 0, nous obtenons un dessin progressif de la courbe.

<circle cx="200" cy="200" r="115"
    style="fill:none; stroke:blue; stroke-dasharray:723,723; stroke-dashoffset:723">
    <animate begin="0" attributeName="stroke-dashoffset"
        from="723" to="0" dur="5s" fill="freeze"/>
</circle>

Si vous connaissez un meilleur moyen, veuillez laisser une réponse.


Mise à jour (26 avril 2012): J'ai trouvé un exemple qui illustre bien l'idée, voir Courbes de Bézier animées .

31
Anton Strogonoff

J'ai créé un script pour cela: Scribble.js , basé sur ce grand dasharray/dashoffset technique .

Il suffit de l'instancier sur un tas de SVG <path>s:

var scribble = new Scribble(paths, {duration: 3000});
scribble.erase();
scribble.draw(function () {
    // done
});

-

NB: Code complet USAGE ici: https://Gist.github.com/abernier/e082a201b0865de1a41f#file-index-html-L31

Prendre plaisir ;)

5
abernier

Peut-être que quelqu'un cherche une réponse, comme moi depuis deux jours maintenant:

// Draw a path and hide it:
var root = paper.path('M0 50L30 50Q100 100 50 50').hide();
var length = root.getTotalLength();

// Setup your animation (in my case jQuery):
element.animate({ 'to': 1 }, {
    duration: 500,
    step: function(pos, fx) {
        var offset = length * fx.pos;
        var subpath = root.getSubpath(0, offset);
        paper.clear();
        paper.path(subpath);
    }
});

Cela a fait l'affaire pour moi, uniquement en utilisant les méthodes RaphaelJS.

Voici un exemple jsFiddle comme demandé dans les commentaires, http://jsfiddle.net/eA8bj/

26
davidenke

Eureka! (Peut-être - en supposant que vous êtes à l'aise de sortir du royaume amical de Raphaël en terre SVG pure ...)

Vous pouvez utiliser SVG keyTimes et keySplines .

Voici un exemple de travail:

http://www.carto.net/svg/samples/animated_bustrack.shtml

... et voici quelques explications potentiellement utiles:

http://msdn.Microsoft.com/en-us/library/ms533119 (v = vs.85) .aspx

17
peteorpeter

Je voudrais proposer une solution alternative, Raphael + JS uniquement, que j'ai largement utilisée dans mon propre travail. Elle présente plusieurs avantages par rapport à la solution de davidenke:

  1. N'efface pas le papier à chaque cycle, permettant au chemin animé de bien coexister avec d'autres éléments;
  2. Réutilise un seul chemin avec la propre animation progressive de Raphaël, ce qui permet des animations plus fluides;
  3. Beaucoup moins de ressources.

Voici la méthode (qui pourrait facilement être réoutillée dans une extension):

function drawpath( canvas, pathstr, duration, attr, callback )
{
    var guide_path = canvas.path( pathstr ).attr( { stroke: "none", fill: "none" } );
    var path = canvas.path( guide_path.getSubpath( 0, 1 ) ).attr( attr );
    var total_length = guide_path.getTotalLength( guide_path );
    var last_point = guide_path.getPointAtLength( 0 );
    var start_time = new Date().getTime();
    var interval_length = 50;
    var result = path;        

    var interval_id = setInterval( function()
    {
        var elapsed_time = new Date().getTime() - start_time;
        var this_length = elapsed_time / duration * total_length;
        var subpathstr = guide_path.getSubpath( 0, this_length );            
        attr.path = subpathstr;

        path.animate( attr, interval_length );
        if ( elapsed_time >= duration )
        {
            clearInterval( interval_id );
            if ( callback != undefined ) callback();
                guide_path.remove();
        }                                       
    }, interval_length );  
    return result;
}

Et voici deux exemples de son utilisation sur mon site: un pour Path Transformation , et l'autre pour Progressive Lettering .

11
Kevin Nielsen

En utilisant l'attribut " pathLength ", nous pouvons définir la longueur virtuelle du chemin. A partir de là, nous pouvons utiliser cette longueur virtuelle dans "stroke-dasharray". Donc, si nous définissons "pathLength" sur 100 unités, nous pouvons alors définir "stroke-dasharray" sur "50,50", ce qui correspond exactement à 50%, 50% du chemin!

Il y a un problème avec cette approche: le seul navigateur qui prend en charge cet attribut est Opera 11.

Ici est un exemple d'animation de tracé de courbe lisse sans javascript ni longueur codée en dur. (Fonctionne correctement uniquement dans Opera 11)

6
Brazhnyk Yuriy

La solution d'Anton & Peteorpeter se décompose malheureusement en Chrome lorsque les chemins se compliquent. C'est bien pour la carte de bus dans cette démo liée. Découvrez ce "pétales de fleurs" animé jsfiddle que j'ai créé, qui dessine correctement dans FF10 et Safari5, mais scintille de manière incontrôlable dans Chrome:

http://jsfiddle.net/VjMvz/

(Ceci est tout HTML et SVG en ligne, pas de javascript.)

Je suis toujours à la recherche d'une solution non Flash pour cela. AnimateAlong ne le coupera évidemment pas pour ce que je fais. Raphael.js pourrait fonctionner, bien qu'il menace de se transformer en spaghetti de rappel très rapidement.

Davidenke, pouvez-vous publier un jsfiddle fonctionnel avec votre solution? Je n'arrive pas à le faire fonctionner. Je reçois une erreur dans Chrome 18 que les nœuds définis sur "display: none" avec votre ".hide" n'ont pas de méthode "getTotalLength".

3
Ben

Malheureusement, comme vous semblez être d'accord, vous ne pouvez probablement pas faire cela avec élégance à Raphaël.

Cependant , si, par un coup de %deity% vous n'avez pas besoin de prendre en charge IE pour cette fonctionnalité particulière, vous pouvez renoncer à l'API Raphael et manipuler directement le SVG . Ensuite, vous pourriez peut-être truquer un masque pour parcourir le chemin et révéler la ligne à un rythme naturel.

Vous pouvez vous dégrader gracieusement dans IE pour simplement montrer le chemin en utilisant Raphael, sans animation.

2
peteorpeter

je faisais exactement cela. La première chose que j'ai essayée était la solution d'Anton mais les performances sont nulles.

En fin de compte, le moyen le plus simple d'obtenir le résultat que je voulais était d'utiliser la syntaxe alternative "image clé" pour la fonction d'animation.

dessinez le chemin final de manière invisible, puis générez un tas d'images clés en utilisant getSubpath dans une boucle.

créer un nouveau chemin visible et égal à la première image clé.

puis faites quelque chose comme:

path.anmimate ({keyFrameObject, timeframe});

vous ne devriez pas avoir besoin d'une image clé pour chaque pixel que vous souhaitez dessiner. Après avoir joué avec les paramètres, j'ai constaté qu'une valeur de 100 pixels par image clé fonctionnait pour la complexité/taille de ce que j'essayais de "dessiner"

1
David Meister

Juste une mise à jour, vous pouvez essayer Lazy Line Painter

1
JackSD

D'accord, voici mes réflexions à ce sujet… La solution est trop loin d'être idéale.

Pour montrer progressivement le chemin, nous devons le montrer, point par point. Et les trajectoires vectorielles ne sont pas constituées de points, mais de courbes. Il me semble donc qu’il n’existe pas de moyen "naturel" de "tracer" progressivement le tracé dans les graphiques vectoriels. (Bien que je sois assez nouveau dans ce domaine et que je puisse me tromper.)

La seule façon serait de convertir en quelque sorte un chemin en plusieurs points et de les afficher un par un.

Actuellement, ma solution consiste à dessiner un chemin, à le rendre invisible, à le diviser en plusieurs sous-chemins et à montrer ces sous-chemins un par un.

Ce n'est pas difficile à faire avec Raphael, mais ce n'est pas élégant non plus, et assez lent sur de grands chemins. Ne pas accepter ma réponse, en espérant qu'il y a une meilleure façon…

0
Anton Strogonoff

Avez-vous essayé Raphael's animateAlong ? Vous pouvez le voir en action sur une page de démonstration .

0
user123444555621