2013-07-26 5 views
3

Comme beaucoup de gens le savaient, HTML5 Canvas lineTo() va vous donner une ligne très jaggy à chaque coin. À ce stade, une solution plus préférable serait de mettre en œuvre quadraticCurveTo(), ce qui est un très bon moyen de générer un dessin lisse. Cependant, je souhaite créer un dessin sur toile HTML5, lisse et précis. L'approche de courbe quadratique fonctionne bien pour lisser le tirage, mais elle ne passe pas par tous les points d'échantillonnage. En d'autres termes, lorsque j'essaie de tracer une courbe rapide en utilisant une courbe quadratique, il arrive que la courbe soit "corrigée" par l'application. Au lieu de suivre mon chemin de dessin, une partie du segment est incurvée hors de son chemin d'origine pour suivre une courbe quadratique.Mimick photoshop/peintre dessiner en douceur sur toile HTML5?

Mon application est destinée à un dessin professionnel sur toile HTML5, il est donc crucial que le dessin soit à la fois lisse et précis. Je ne sais pas si je vous demande l'impossible en essayant de mettre la toile HTML5 sur le même niveau que Photoshop ou toute autre application de peintre (SAI, painterX, etc.)

Merci

+0

Pas tout à fait clair à ce que vous essayez. Tracez-vous une courbe avec la souris et attendez-vous à ce que la toile trace la courbe exacte que la souris a suivie? – jing3142

+0

mmm, +1: très intéressant. Cependant, si vous envisagez de faire une application de dessin professionnel, à mon humble avis, vous feriez mieux d'opter pour une application native. Le seul avantage d'une application de navigateur est la compatibilité croisée, mais cela a un prix énorme. – Saturnix

Répondre

3

Ce que vous voulez est Cardinal spline car les splines cardinaux passent par les points réels que vous dessinez. Remarque: pour obtenir un résultat professionnel, vous devez également implémenter une moyenne mobile pour les seuils courts tout en utilisant des splines cardinales pour les seuils plus élevés et en utilisant les valeurs du genou pour casser les lignes au coin aigu afin de ne pas lisser toute la ligne. Je ne vais pas m'adresser à la moyenne mobile ou au genou ici (ni effiler) car ceux-ci sont en dehors de la portée, mais montrent une façon d'utiliser la spline cardinale.

Une note de côté aussi - l'effet que l'application semble modifier la ligne est in évitable que le lissage se produit post. Il existe des algorithmes qui lissent pendant que vous dessinez mais ils ne conservent pas les valeurs de genou et les lignes semblent "osciller" pendant que vous dessinez. C'est une question de préférence, je suppose.

Voici un violon à démontrer ce qui suit:
ONLINE DEMO

D'abord quelques conditions préalables (j'utilise ma bibliothèque easyCanvas pour configurer l'environnement dans la démo car il me permet de gagner beaucoup de travail, mais ce n'est pas une exigence pour que cette solution fonctionne):

  • Je vous recommande de dessiner le nouveau trait sur un canevas séparé qui est au-dessus de celui principal.
  • Lorsque le mouvement est terminé (souris vers le haut), passez-le dans le lisseur et stockez-le dans la pile de traits.
  • Dessinez ensuite la ligne lissée à la ligne principale.

Lorsque vous avez des points dans un ordre de tableau par X/Y (c.-à-[x1, y1, x2, y2, ... xn, yn]), vous pouvez utiliser cette fonction pour lisser:

La tension valeur(ts, par défaut 0,5) est ce que lisse la courbe. Plus le nombre est élevé, plus la courbe devient ronde. Vous pouvez sortir de l'intervalle normal [0, 1] pour faire des boucles. Le segment (nos, ou nombre de segments) est la résolution entre chaque point. Dans la plupart des cas, vous n'aurez probablement pas besoin de plus de 9-10. Mais sur les ordinateurs plus lents ou où vous dessinez rapidement des valeurs plus élevées est nécessaire.

La fonction (optimisé):

/// cardinal spline by Ken Fyrstenberg, CC-attribute 
function smoothCurve(pts, ts, nos) { 

    // use input value if provided, or use a default value 
    ts = (typeof ts === 'undefined') ? 0.5 : ts; 
    nos = (typeof nos === 'undefined') ? 16 : nos; 

    var _pts = [], res = [],  // clone array 
     x, y,      // our x,y coords 
     t1x, t2x, t1y, t2y,   // tension vectors 
     c1, c2, c3, c4,    // cardinal points 
     st, st2, st3, st23, st32, // steps 
     t, i, r = 0, 
     len = pts.length, 
     pt1, pt2, pt3, pt4; 

    _pts.push(pts[0]); //copy 1. point and insert at beginning 
    _pts.push(pts[1]); 

    _pts = _pts.concat(pts); 

    _pts.push(pts[len - 2]); //copy last point and append 
    _pts.push(pts[len - 1]); 

    for (i = 2; i < len; i+=2) { 

     pt1 = _pts[i]; 
     pt2 = _pts[i+1]; 
     pt3 = _pts[i+2]; 
     pt4 = _pts[i+3]; 

     t1x = (pt3 - _pts[i-2]) * ts; 
     t2x = (_pts[i+4] - pt1) * ts; 

     t1y = (pt4 - _pts[i-1]) * ts; 
     t2y = (_pts[i+5] - pt2) * ts; 

     for (t = 0; t <= nos; t++) { 

      // pre-calc steps 
      st = t/nos; 
      st2 = st * st; 
      st3 = st2 * st; 
      st23 = st3 * 2; 
      st32 = st2 * 3; 

      // calc cardinals 
      c1 = st23 - st32 + 1; 
      c2 = st32 - st23; 
      c3 = st3 - 2 * st2 + st; 
      c4 = st3 - st2; 

      res.push(c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x); 
      res.push(c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y); 

     } //for t 
    } //for i 

    return res; 
} 

Ensuite, appelez simplement de l'événement mouseup après des points a été stockés:

stroke = smoothCurve(stroke, 0.5, 16); 
strokes.push(stroke); 

commentaires courts sur les valeurs du genou:

A la valeur du genou dans ce contexte est l'angle entre les points (en tant que partie d'un segment de ligne) dans la ligne est supérieur à un certain seuil (typiquement entre 45 - 60 deg rees). Quand un genou se produit, les lignes sont divisées en une nouvelle ligne de sorte que seule la ligne composée de points avec un angle inférieur au seuil entre eux est utilisée (vous voyez les petites boucles dans la démo en raison de ne pas utiliser de genoux).

commentaire court sur la moyenne mobile:

Moving average est généralement utilisé à des fins statistiques, mais il est très utile pour dessiner des applications. Lorsque vous avez un groupe de plusieurs points avec une courte distance entre eux, les splines ne fonctionnent pas très bien. Donc, ici, vous pouvez utiliser MA pour lisser les points.

Il existe également des algorithmes de réduction de points pouvant être utilisés, tels que le Ramer/Douglas/Peucker, mais ils sont davantage utilisés à des fins de stockage pour réduire la quantité de données.

+0

La démo est en panne à cause de la toile que tu construis va disparaître - Je suis très, très intéressé de voir un exemple de moyenne mobile pour lisser le bruit de la souris en temps réel - Est-ce quelque chose que tu as comme démo? –