35 votes

Implémentation d'esquisses et de dessins fluides sur <canvas> élément

Je suis en train de créer une zone de dessin avec de la toile. Je vais avoir des problèmes avec les lignes fluides lors de l'élaboration des courbes et j'ai aussi changer l'épaisseur du trait dans mon algorithme qui est de mauvaise qualité, car la taille des sauts de beaucoup de et ainsi vous pouvez voir où le changement de la taille. J'ai trouvé ce lien sur stackoverflow mais c'était pour une appli iPhone et je ne peux pas le comprendre.

Voici mon code JS. et Ici est en cours d'exécution sur jsFiddle

var xStart,
xEnd,
yStart,
yEnd,
paint,
ctx;
$(document).ready(function (){

   ctx = $('canvas')[0].getContext("2d");
   ctx.strokeStyle = '#000';
   ctx.lineJoin="round";
   ctx.lineCap="round";
   ctx.lineWidth = 1;


   $('canvas').bind('mousedown mousemove mouseup mouseleave touchstart touchmove touchend', function(e){
        var orig = e.originalEvent;

        if(e.type == 'mousedown'){
            e.preventDefault(); e.stopPropagation();

            xStart = e.clientX - $(this).offset().left;
            yStart = e.clientY - $(this).offset().top;
            xEnd = xStart;
            yEnd = yStart;

            paint = true;
            draw(e.type);

        }else if(e.type == 'mousemove'){
            if(paint==true){
                xEnd = e.clientX - $(this).offset().left;
                yEnd = e.clientY - $(this).offset().top;


               lineThickness = 1 + Math.sqrt((xStart - xEnd) *(xStart-xEnd) + (yStart - yEnd) * (yStart-yEnd))/5;

               if(lineThickness > 10){
                    lineThickness = 10;   
               }

                ctx.lineWidth = lineThickness;
                draw(e.type);
            }
        }else if(e.type == 'mouseup'){
            paint = false;
        }else if(e.type == 'mouseleave'){
            paint = false;
        }else if(e.type == 'touchstart'){
            if(orig.touches.length == 1){
                e.preventDefault(); e.stopPropagation();

                xStart = orig.changedTouches[0].pageX - $(this).offset().left;
                yStart = orig.changedTouches[0].pageY - $(this).offset().top;
                xEnd = xStart;
                yEnd = yStart; 

                paint = true;
                draw(e.type);
            }
        }else if(e.type == 'touchmove'){
            if(orig.touches.length == 1){
                if(paint==true){
                    xEnd = orig.changedTouches[0].pageX - $(this).offset().left;
                    yEnd = orig.changedTouches[0].pageY - $(this).offset().top;


                            lineThickness = 1 + Math.sqrt((xStart - xEnd) *(xStart-xEnd) + (yStart - yEnd) * (yStart-yEnd))/6;
                       if(lineThickness > 10){
                          lineThickness = 10;   
                       }


                      ctx.lineWidth = lineThickness;


                    draw(e.type);
                }
            }
        }else if(e.type == 'touchend'){
            paint = false;
        }

      });
    });


    function draw(event){

    if(event == 'mousedown'){
        ctx.beginPath();
        ctx.moveTo(xStart, yStart);
        ctx.lineTo(xEnd, yEnd);
        ctx.stroke();
    }else if(event == 'mousemove'){
        ctx.beginPath();
        ctx.moveTo(xStart, yStart);
        ctx.lineTo(xEnd, yEnd);
        ctx.stroke();
    }else if(event == 'touchstart'){
        ctx.beginPath();
        ctx.moveTo(xStart, yStart);
        ctx.lineTo(xEnd, yEnd);
        ctx.stroke();
    }else if(event == 'touchmove'){
        ctx.beginPath();
        ctx.moveTo(xStart, yStart);
        ctx.lineTo(xEnd, yEnd);
        ctx.stroke();
    }
    xStart = xEnd;
    yStart = yEnd;                  
}

Je vous remercie tous à l'avance.

C'est à quoi il ressemble pour l'instant, si vous dessinez. current (jagged) implementation

... et c'est ce que j'aimerais obtenir:

smooth brushstrokes

24voto

Alex Points 2048

J'ai fait quelque chose comme ça il y a longtemps et l'a transformé en un plugin jquery. jetez un oeil ici, si c'est ce que vous êtes après, je vais poster une réponse plus détaillée et creuser la simplification de la version jquery à partir de mes archives:

http://jsfiddle.net/95tft/

MODIFIER

OK, désolé, je ne pouvais pas faire ça hier:

À l'origine, le code ci-dessus a été forké M. Dood de "l'harmonie" l'esquisse ici: http://mrdoob.com/projects/harmony/#ribbon

(qui je pense est la meilleure solution). Mais j'ai un peu cassé vers le bas et il refait pour mes propres fins, sur un autre projet. J'ai piraté mon propre plugin un peu pour le rendre un peu plus facile encore ici:

http://jsfiddle.net/dh3bj/

La seule chose que vous pouvez changer, c'est changer pour travailler sur mousedown/mouseup qui devrait être facile à découvrir également les paramètres au bas du plugin, vous devriez être en mesure d'obtenir l'effet que vous souhaitez en jouant avec la taille du pinceau, la couleur, alpha (rgba) etc.

Espère que ça aide

11voto

Simon Sarris Points 33799

Jetez un œil à ce code:

http://jsfiddle.net/aMmVQ/

Ce que je fais, c'est commencer une nouvelle liste de points sur mouseDown, puis pour chaque mousemove j'ajoute un point à la liste. Une fois que j'ai obtenu suffisamment de points (environ 6), je commence à dessiner des courbes quadratiques, le point de contrôle de la courbe étant la moyenne du point actuel et du point suivant.

drawPoints est le bit qui opère cette magie:

 function drawPoints(ctx, points) {
    // draw a basic circle instead
    if (points.length < 6) {
        var b = points[0];
        ctx.beginPath(), ctx.arc(b.x, b.y, ctx.lineWidth / 2, 0, Math.PI * 2, !0), ctx.closePath(), ctx.fill();
        return
    }
    ctx.beginPath(), ctx.moveTo(points[0].x, points[0].y);
    // draw a bunch of quadratics, using the average of two points as the control point
    for (i = 1; i < points.length - 2; i++) {
        var c = (points[i].x + points[i + 1].x) / 2,
            d = (points[i].y + points[i + 1].y) / 2;
        ctx.quadraticCurveTo(points[i].x, points[i].y, c, d)
    }
    ctx.quadraticCurveTo(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y), ctx.stroke()
}
 

6voto

Jong-Chan Choi Points 101

Pourquoi n'utilisez-vous pas croquis.js ?

Il a une implémentation soignée comme Photoshop :)

Et voici la démo qui utilise croquis.js.

aperçu du pinceau

2voto

antyrat Points 14294

Semble que vous devez utiliser des pinceaux dans votre toile. Il est difficile de dire quel type de brosse exactement vous avez besoin, mais il y a beaucoup de bibliothèques JS qui a déjà de mettre en œuvre la brosse de la technologie.

Par exemple, avez-vous regarder ce les bibliothèques?

Laso dans le web, vous pouvez trouver de nombreux pinceaux mis en œuvre dans M. Dood Harmonie projet. Par exemple, de filaments ou de l'Harmonie-Brosses projet sur github.

2voto

george calvert Points 181

Suggérez le rendu avec une chaîne de courbes de Bézier qui entoure la courbe ainsi remplie. (c'est-à-dire se terminer par ctx.fill) Encore beaucoup de travail à faire mais j'espère que cela vous aidera.

A adapté une belle application de démonstration pour les courbes de Bézier

l'a ajouté à une fourchette de votre violon http://jsfiddle.net/d3zFU/1/

Le code est

 /*
 * Canvas curves example
 *
 * By Craig Buckler,        http://twitter.com/craigbuckler
 * of OptimalWorks.net        http://optimalworks.net/
 * for SitePoint.com        http://sitepoint.com/
 *
 * Refer to:
 * http://blogs.sitepoint.com/html5-canvas-draw-quadratic-curves/
 * http://blogs.sitepoint.com/html5-canvas-draw-bezier-curves/
 *
 * This code can be used without restriction.
 */
 

(une fonction() {

 var canvas, ctx, code, point, style, drag = null, dPoint;

// define initial points
function Init(quadratic) {

    point = {
        p1: { x:100, y:250 },
        p2: { x:400, y:250 }
    };

    if (quadratic) {
        point.cp1 = { x: 250, y: 100 };
    }
    else {
        point.cp1 = { x: 150, y: 100 };
        point.cp2 = { x: 350, y: 100 };
    }

    // default styles
    style = {
        curve:    { width: 6, color: "#333" },
        cpline:    { width: 1, color: "#C00" },
        point: { radius: 10, width: 2, color: "#900", fill: "rgba(200,200,200,0.5)", arc1: 0, arc2: 2 * Math.PI }
    }

    // line style defaults
    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    // event handlers
    canvas.onmousedown = DragStart;
    canvas.onmousemove = Dragging;
    canvas.onmouseup = canvas.onmouseout = DragEnd;

    DrawCanvas();
}


// draw canvas
function DrawCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // control lines
    ctx.lineWidth = style.cpline.width;
    ctx.strokeStyle = style.cpline.color;
    ctx.fillStyle = style.cpline.color;
    ctx.beginPath();
    ctx.moveTo(point.p1.x, point.p1.y);
    ctx.lineTo(point.cp1.x, point.cp1.y);
    if (point.cp2) {
        ctx.moveTo(point.p2.x, point.p2.y);
        ctx.lineTo(point.cp2.x, point.cp2.y);
    }
    else {
        ctx.lineTo(point.p2.x, point.p2.y);
    }
    ctx.stroke();

    // curve
ctx.lineWidth = 1 ; //style.curve.width;
    ctx.strokeStyle = style.curve.color;
    ctx.beginPath();
    ctx.moveTo(point.p1.x, point.p1.y);
    if (point.cp2) {
        ctx.bezierCurveTo(point.cp1.x, point.cp1.y, point.cp2.x, point.cp2.y, point.p2.x, point.p2.y);
        ctx.bezierCurveTo(point.cp2.x, point.cp2.y+12, point.cp1.x, point.cp1.y+12, point.p1.x, point.p1.y);

    }
    else {
        ctx.quadraticCurveTo(point.cp1.x, point.cp1.y, point.p2.x, point.p2.y);
    }
//ctx.stroke();
ctx.fill();

    // control points
    for (var p in point) {
        ctx.lineWidth = style.point.width;
        ctx.strokeStyle = style.point.color;
        ctx.fillStyle = style.point.fill;
        ctx.beginPath();
        ctx.arc(point[p].x, point[p].y, style.point.radius, style.point.arc1, style.point.arc2, true);
        ctx.fill();
        ctx.stroke();
    }

    ShowCode();
}


// show canvas code
function ShowCode() {
    if (code) {
        code.firstChild.nodeValue =
            "canvas = document.getElementById(\"canvas\");\n"+
            "ctx = canvas.getContext(\"2d\")\n"+
            "ctx.lineWidth = " + style.curve.width +
            ";\nctx.strokeStyle = \"" + style.curve.color +
            "\";\nctx.beginPath();\n" +
            "ctx.moveTo(" + point.p1.x + ", " + point.p1.y +");\n" +
            (point.cp2 ?
                "ctx.bezierCurveTo("+point.cp1.x+", "+point.cp1.y+", "+point.cp2.x+", "+point.cp2.y+", "+point.p2.x+", "+point.p2.y+");" :
                "ctx.quadraticCurveTo("+point.cp1.x+", "+point.cp1.y+", "+point.p2.x+", "+point.p2.y+");"
            ) +
            "\nctx.stroke();"
        ;
    }
}


// start dragging
function DragStart(e) {
    e = MousePos(e);
    var dx, dy;
    for (var p in point) {
        dx = point[p].x - e.x;
        dy = point[p].y - e.y;
        if ((dx * dx) + (dy * dy) < style.point.radius * style.point.radius) {
            drag = p;
            dPoint = e;
            canvas.style.cursor = "move";
            return;
        }
    }
}


// dragging
function Dragging(e) {
    if (drag) {
        e = MousePos(e);
        point[drag].x += e.x - dPoint.x;
        point[drag].y += e.y - dPoint.y;
        dPoint = e;
        DrawCanvas();
    }
}


// end dragging
function DragEnd(e) {
    drag = null;
    canvas.style.cursor = "default";
    DrawCanvas();
}


// event parser
function MousePos(event) {
    event = (event ? event : window.event);
    return {
        x: event.pageX - canvas.offsetLeft,
        y: event.pageY - canvas.offsetTop
    }
}


// start
canvas = document.getElementById("canvas");
code = document.getElementById("code");
if (canvas.getContext) {
    ctx = canvas.getContext("2d");
    Init(canvas.className == "quadratic");
}
 

}) ();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X