javascript - metodos - Implementando un boceto suave y dibujando en el elemento<canvas>
metodos canvas javascript (6)
¿Por qué no usas croquis.js ?
Tiene una implementación ordenada de pincel como photoshop :)
Y aquí está Demo que está usando croquis.js.
Estoy tratando de crear un área de dibujo con lienzo. Estoy teniendo problemas para que las líneas se vean suaves al dibujar curvas y también tengo un grosor de línea cambiante en mi algoritmo, que también se ve mal porque el tamaño también aumenta demasiado y se puede ver dónde cambió el tamaño. Encontré este enlace en stackoverflow, pero esto fue para una aplicación nativa de iPhone y no puedo entenderlo.
Aquí está mi código JS actual. y Aquí está corriendo en 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;
}
Gracias a todos de antemano.
Esto es lo que parece ahora si dibujas.
... y esto es lo que me gustaría lograr:
Eche un vistazo a este código:
Lo que estoy haciendo es comenzar una nueva lista de puntos en mouseDown, luego para cada mousemove agrego un punto a la lista. Una vez que tengo suficientes puntos (6 o más) empiezo a dibujar curvas cuadráticas, con el punto de control de la curva como el promedio del punto actual y el punto siguiente.
drawPoints
es la parte que funciona esta magia:
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()
}
Hice algo como esto hace un tiempo y lo convertí en un plugin jquery. echa un vistazo aquí, si es lo que buscas, publicaré una respuesta más detallada y extraeré la versión simplificada de jquery de mis archivos:
EDITAR
OK, lo siento, no pude hacer esto ayer:
Originalmente, el código anterior se bifurcó del dibujante de armonía del Sr. Doob aquí: http://mrdoob.com/projects/harmony/#ribbon
(que creo que es la mejor solución). Pero lo descompuse y lo rehice para mis propios fines en otro proyecto. He pirateado mi propio complemento un poco para que sea un poco más fácil aún aquí:
Lo único que podría querer cambiar es cambiarlo para que funcione en mousedown / mouseup, que debería ser fácil, eche un vistazo a la configuración en la parte inferior del complemento, debería poder obtener el efecto que desea jugando con el tamaño del pincel, color, alfa (rgba), etc.
Espero que ayude
Para aquellos interesados en una versión de clic del código proporcionado por @Alex, he reescrito su script aquí:
Parece que necesitas usar algunos pinceles en tu lienzo. Es difícil decir qué tipo de pincel exactamente necesita, pero hay muchas bibliotecas JS que ya han implementado la tecnología de pincel.
Por ejemplo, ¿miraste estas bibliotecas?
- Processing.js
- HTML5-Canvas-Brush-Sketch ( demo y article cómo funciona)
Laso en la web puede encontrar muchos pinceles implementados en el proyecto Mr. Doob Harmony . Por ejemplo, Proyecto stringy o Harmony-Brushes de armonía en github.
Sugiera que la representación se realice con una cadena de curvas bezier que rodean la curva que de este modo se llena. (es decir, terminar con ctx.fill) Aún hay mucho trabajo por hacer, pero espero que esto ayude.
Adaptado una buena aplicación de demostración para curvas bezier
lo agregó a un tenedor de tu violín http://jsfiddle.net/d3zFU/1/
El código es
/*
* 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.
*/
(función () {
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");
}
}) ();