pixijs - Draw a line with gradient in canvas -


i hope post not duplicated.

enter image description here

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.

enter image description here

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

Popular posts from this blog

ubuntu - PHP script to find files of certain extensions in a directory, returns populated array when run in browser, but empty array when run from terminal -

php - How can i create a user dashboard -

javascript - How to detect toggling of the fullscreen-toolbar in jQuery Mobile? -