pixijs - Draw a line with gradient in canvas -
i hope post not duplicated.
i draw line, image shown, may have different line width , gradient. tried createlineargradient not expected. shall use image instead? or how can render line above?
i may work pixijs.
update: can generate line gradient color how can create dynamic width ones?
$(function() { var canvas = document.getelementbyid("canvas"), ctx = canvas.getcontext("2d"), painting = false, lastx = 0, lasty = 0; canvas.onmousedown = function (e) { if (!painting) { painting = true; } else { painting = false; } lastx = e.pagex - this.offsetleft; lasty = e.pagey - this.offsettop; ctx.linejoin = ctx.linecap = 'round'; }; var img = new image(); img.src = "http://i.imgur.com/k6qxhjm.png"; canvas.onmousemove = function (e) { if (painting) { mousex = e.pagex - this.offsetleft; mousey = e.pagey - this.offsettop; // var grad= ctx.createlineargradient(lastx, lasty, mousex, mousey); // grad.addcolorstop(0, "red"); // grad.addcolorstop(1, "green"); //ctx.strokestyle = grad; ctx.linewidth = 15; //ctx.createpattern(img, 'repeat'); ctx.strokestyle = ctx.createpattern(img, 'repeat'); ctx.beginpath(); ctx.moveto(lastx, lasty); ctx.lineto(mousex, mousey); ctx.stroke(); $('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+lastx+', '+lasty+'<br/>mousedown: '+"mousedown"); lastx = mousex; lasty = mousey; } } function fadeout() { ctx.fillstyle = "rgba(255,255,255,0.3)"; ctx.fillrect(0, 0, canvas.width, canvas.height); settimeout(fadeout,100); } fadeout(); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <canvas id="canvas" width="800" height="500"></canvas> <div id="output"></div>
custom line rendering using 2d canvas api
there no simple way create type of line want without sacrificing lot of quality.
for best quality need render line set of small strips perpendicular line , way along length of line. each part calculate width , colour , render strip.
the following image explain mean.
the line in middle defining curve. outer lines show changing width. section marked single strip (enlarged)
you divide line equally small parts, every point along line need find position on line , vector perpendicular point on line. find point above , below point @ correct distance make width line point.
you draw each strip @ correct colour.
the problem 2d api bad @ joining separate rendered paths, method produce pattern of perpendicular lines due antialiasing between each strip.
you can combat outlining each strip same colours stroke, destroy quality of outer edge, producing small bumps @ each seam on outer edge of line.
this can stopped if set clip region line. tracing out outline of line , setting clip.
you can render line @ passable quality
there math explained in single answer. need find points , tangents on bezier curve, need interpolate gradient, , need way of defining smooth width function (another bezier) or in example complex parabola (the function curve
)
example
the following example create type of line after single bezier (2nd , 3rd order). can adapt use multiple curves , line segments.
this best quality can (though can render 2 or 4 times res , down sample slight improvement)
for pixel perfect antialiased result have use webgl render final path (but still need generate path in example)
const ctx = canvas.getcontext("2d"); canvas.height = canvas.width = 400; // minimum groover.geom library needed use vecat , tangentasvec bezier curves. const geom = (()=>{ const v1 = new vec(); const v2 = new vec(); const v3 = new vec(); const v4 = new vec(); function vec(x,y){ this.x = x; this.y = y; }; function bezier(p1,p2,cp1,cp2){ this.p1 = p1; this.p2 = p2; this.cp1 = cp1; this.cp2 = cp2; } bezier.prototype = { //====================================================================================== // single dimension polynomials 2nd (a,b,c) , 3rd (a,b,c,d) order bezier //====================================================================================== // quadratic f(t) = a(1-t)^2+2b(1-t)t+ct^2 // = a+2(-a+b)t+(a-2b+c)t^2 // derivative f'(t) = 2(1-t)(b-a)+2(c-b)t //====================================================================================== // cubic f(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3 // = a+(-2a+3b)t+(2a-6b+3c)t^2+(-a+3b-3c+d)t^3 // derivative f'(t) = -3a(1-t)^2+b(3(1-t)^2-6(1-t)t)+c(6(1-t)t-3t^2) +3dt^2 // 2nd derivative f"(t) = 6(1-t)(c-2b+a)+6t(d-2c+b) //====================================================================================== p1 : undefined, p2 : undefined, cp1 : undefined, cp2 : undefined, vecat(position,vec){ var c; if (vec === undefined) { vec = new vec() } if (position === 0) { vec.x = this.p1.x; vec.y = this.p1.y; return vec; }else if (position === 1) { vec.x = this.p2.x; vec.y = this.p2.y; return vec; } v1.x = this.p1.x; v1.y = this.p1.y; c = position; if (this.cp2 === undefined) { v2.x = this.cp1.x; v2.y = this.cp1.y; v1.x += (v2.x - v1.x) * c; v1.y += (v2.y - v1.y) * c; v2.x += (this.p2.x - v2.x) * c; v2.y += (this.p2.y - v2.y) * c; vec.x = v1.x + (v2.x - v1.x) * c; vec.y = v1.y + (v2.y - v1.y) * c; return vec; } v2.x = this.cp1.x; v2.y = this.cp1.y; v3.x = this.cp2.x; v3.y = this.cp2.y; v1.x += (v2.x - v1.x) * c; v1.y += (v2.y - v1.y) * c; v2.x += (v3.x - v2.x) * c; v2.y += (v3.y - v2.y) * c; v3.x += (this.p2.x - v3.x) * c; v3.y += (this.p2.y - v3.y) * c; v1.x += (v2.x - v1.x) * c; v1.y += (v2.y - v1.y) * c; v2.x += (v3.x - v2.x) * c; v2.y += (v3.y - v2.y) * c; vec.x = v1.x + (v2.x - v1.x) * c; vec.y = v1.y + (v2.y - v1.y) * c; return vec; }, tangentasvec (position, vec ) { var a, b, c, u; if (vec === undefined) { vec = new vec(); } if (this.cp2 === undefined) { = (1-position) * 2; b = position * 2; vec.x = * (this.cp1.x - this.p1.x) + b * (this.p2.x - this.cp1.x); vec.y = * (this.cp1.y - this.p1.y) + b * (this.p2.y - this.cp1.y); }else{ = (1-position) b = 6 * * position; // (6*(1-t)*t) *= 3 * a; // 3 * ( 1 - t) ^ 2 c = 3 * position * position; // 3 * t ^ 2 vec.x = -this.p1.x * + this.cp1.x * (a - b) + this.cp2.x * (b - c) + this.p2.x * c; vec.y = -this.p1.y * + this.cp1.y * (a - b) + this.cp2.y * (b - c) + this.p2.y * c; } u = math.sqrt(vec.x * vec.x + vec.y * vec.y); vec.x /= u; vec.y /= u; return vec; }, } return { vec, bezier,} })() // function used define width of curve // creates smooth transition. // power changes rate of change function curve(x,power){ // simple smooth curve x range 0-2 return value between 0 , 1 x = 1 - math.abs(x - 1); return math.pow(x,power); } // function returns colour @ point in gradient // pos 0 - 1 // grad array of positions , colours each // array [position, red, green, blue] position position in gradient // simple 2 colour gradient black (start position = 0) white (end position = 1) // [[0,0,0,0],[1,255,255,255]] // bool ishsl if true interpolate values hue saturation , luminiance function getcolfromgrad(pos,grad,ishsl){ // pos 0 - 1, grad array of [pos,r,g,b] var = 0; while(i < grad.length -1 && grad[i][0] <= pos && grad[i+1][0] < pos){ ++ } var g1 = grad[i]; var g2 = grad[i + 1]; var p = (pos - g1[0]) / (g2[0] - g1[0]); var r = (g2[1]-g1[1]) * p + g1[1]; var g = (g2[2]-g1[2]) * p + g1[2]; var b = (g2[3]-g1[3]) * p + g1[3]; if(ishsl){ return `hsl(${(r|0)%360},${g|0}%,${b|0}%)` } return `rgb(${r|0},${g|0},${b|0})` } function drawline(path,width,gradient){ var steps = 300; var step = 1/steps; var = 0; var pos = v(0,0); var tangent = v(0,0); var p = []; // holds points // <= 1 + step/2 // stop floating point error missing end value for(i = 0; <= 1 + step/2; += step){ path.vecat(i,pos); // position along curve path.tangentasvec(i,tangent); // tangent @ point] var w = curve(i * 2,1/2) * width; // line width point p.push(v(pos.x -tangent.y * w, pos.y + tangent.x * w)); // add edge point above line p.push(v(pos.x +tangent.y * w, pos.y - tangent.x * w)); // add edge point below } // save context , create clip path ctx.save(); ctx.beginpath(); // path alone top edge for(i = 0; < p.length; += 2){ ctx.lineto(p[i].x,p[i].y); } // along bottom for(i = 1; < p.length; += 2){ ctx.lineto(p[p.length - i].x,p[p.length - i].y); } // set clip ctx.clip(); // each strip ctx.linewidth = 1; for(i = 0; < p.length-4; += 2){ ctx.beginpath(); // colour strip ctx.strokestyle = ctx.fillstyle = getcolfromgrad(i / (p.length-4),gradient); // define path ctx.lineto(p[i].x,p[i].y); ctx.lineto(p[i+1].x,p[i+1].y); ctx.lineto(p[i+3].x,p[i+3].y); ctx.lineto(p[i+2].x,p[i+2].y); // cover seams ctx.stroke(); // fill strip ctx.fill(); } // remove clip ctx.restore(); } // create quick shortcut create vector object var v = (x,y)=> new geom.vec(x,y); // create quadratice bezier var b = new geom.bezier(v(50,50),v(50,390),v(500,10)); // create gradient var grad = [[0,0,0,0],[0.25,0,255,0],[0.5,255,0,255],[1,255,255,0]]; // draw gradient line drawline(b,10,grad); // , cubic bezier make sure works. var b = new geom.bezier(v(350,50),v(390,390),v(300,10),v(10,0)); var grad = [[0,255,0,0],[0.25,0,255,0],[0.5,0,255,255],[1,0,0,255]]; drawline(b,20,grad);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>
Comments
Post a Comment