Вопрос по html5, drawing, javascript, canvas, paint – Реализация плавных зарисовок и рисование на <canvas> элемент

29

Я пытаюсь создать область рисования с помощью canvas. У меня возникают проблемы с тем, чтобы линии выглядели гладкими при рисовании кривых, и у меня также есть изменение толщины линий в моем алгоритме, который также выглядит плохо, потому что размер также сильно увеличивается, и вы можете видеть, где изменился размер. Я нашел этоссылка на стеке потока но это было для родного приложения iPhone, и я не могу понять это.

Вот мой текущий код JS. и вот оно работаетна jsFiddle

<code>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;                  
}
</code>

Спасибо всем заранее.

Вот как это выглядит прямо сейчас, если вы рисуете.current (jagged) implementation

... и это то, что я хотел бы достичь:

smooth brushstrokes

Ваш Ответ

6   ответов
2

кто заинтересован в версии клика, предоставленной @Alex, я переписал его сценарий здесь:

http: //jsbin.com/aqoqad/3

5
Почему ты не используешь Croquis.js?

еализация @brush как фотошоп:)

А вот и Demo который использует croquis.js.

Могу ли я получить исходный код для этого? У Croquis нет документации, и мне нужно найти способ рисовать один круг при каждом движении мыши. Arslan Ali
21

трите здесь, если это то, что вам нужно, я выложу более подробный ответ и выкопаю упрощенную версию jquery из моих архивов:

http: //jsfiddle.net/95tft

РЕДАКТИРОВАТ

Ладно, извини, я не смог сделать это вчера:

Первоначально приведенный выше код был разветвлен от эскиза мистера Дуба здесь:http: //mrdoob.com/projects/harmony/#ribbo

(который я считаю лучшим решением). Но я как бы разбил его и переделал для своих собственных целей в другом проекте. Я немного взломал свой собственный плагин, чтобы сделать его еще проще:

http: //jsfiddle.net/dh3bj

Единственное, что вы можете изменить, это изменить его на работу с mousedown / mouseup, что должно быть легко, также посмотрите на настройки в нижней части плагина, вы сможете получить желаемый эффект, играя с размер кисти, цвет, альфа (rgba) и т. д.

Надеюсь, это поможе

Если вы нажмете «О» на mrdoob.com, одной из ссылок будет «Исходный код», который приведет вас к Github.com / mrdoob / гармония Dale
@ ryuutatsuo добавлено выше. Дайте мне знать, если вам нужна дополнительная информация? Alex
первая ссылка. как это работает. mrdoob.com показывает только примеры, но без кода ... dit
Да, это определенно в правильном направлении. Спасибо, что выкопали ваш код, я буду ждать ваших подробных ответов. ryuutatsuo
9

http: //jsfiddle.net/aMmVQ

То, что я делаю, запускает новый список точек на mouseDown, затем для каждого перемещения мыши я добавляю точку в список. Как только я наберу достаточно точек (примерно 6), я начну рисовать квадратичные кривые, причем контрольная точка кривой будет являться средним значением текущей точки и следующей точки.

drawPoints это бит, который творит эту магию:

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()
}
ok Я обновил его, чтобы отразить то, чего я действительно хочу достичь ryuutatsuo
Для записи изображения, которое вы опубликовали, и которое вы хотите достин изменить толщину, только непрозрачность на концах. Simon Sarris
Your код прекрасно работает для создания плавных линий, но как насчет изменения толщины линий, то есть ctx.lineWidth при рисовании. Проблема, которую я имею с вашим методом, состоит в том, что вы рисуете все точки, и как только вы закончите рисовать их все, вы заканчиваете обводку (). Чтобы изменить толщину линии, ее код должен выглядеть как beginPath () moveTo () quadraticCurveTo () stroke (), все внутри цикла, но когда я его переместил, он просто делает что-то интересное. ryuutatsuo
Потрясающе, краду прямо сейчас! Julik
2

Кисти на вашем холсте. Трудно сказать, какая именно кисть вам нужна, но во многих библиотеках JS уже реализована технология кистей.

Например, ты смотрел на эти библиотеки?

Processing.js HTML5-Canvas-Brush-эскиз ( Демо а такжестать как это работает

Ласо в сети, вы можете найти много кистей, реализованных в Mr. Doob Harmony проект. Например Тягучий или Harmony-Кисти проект на github.

Вот код сверху на jsFiddleссылк как вы увидите, это действительно изменчиво, линии не гладкие, и вы можете точно сказать, где изменяется толщина линий в обводке ryuutatsuo
2

что рендеринг выполняется с помощью цепочки кривых Безье, которые окружают кривую, которая таким образом заполнена. (т. е. заканчивается ctx.fill) Еще много работы, но надеюсь, что это поможет.

Адаптировано хорошее демо-приложение для кривых Безье

добавил это к развилке твоей скрипкиhttp: //jsfiddle.net/d3zFU/1

Код

/*
 * 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.
 */

(function ()

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");
}

}) ();

Похожие вопросы