2016-01-16 23 views
0

我想指出了正确的方向在算法方面在演示下面这里http://sta.sh/muro /。此外,它是使用画布的工具 - 即它被画线或绘图许多弧等实施刷厚度变化(例如要内仿真)

具体来说,我想模仿刷转动,将导致整个“刷行程”,以更厚。查看图像以了解我想要模拟的笔刷设置。

最终,我想一个创建涂料刷在导通时,酷似下面的行为,将在厚度上变化。

enter image description here

回答

2

要做到这一点,你需要记录鼠标点时的按键时。然后,您需要检查每个线段以查找方向,线的长度和线段的归一化矢量,以便您可以过度采样鼠标样本。

所以,如果你有一组从鼠标取点以下将获得所需的细节。

for(var i = 0; i < len-1; i++){ 
     var p1 = line[i]; 
     var p2 = line[i+1]; 
     var nx = p2.x - p1.x; 
     var ny = p2.y - p1.y; 
     p1.dir = ((Math.atan2(ny,nx)%PI2)+PI2)%PI2; // get direction 
     p1.len = Math.hypot(nx,ny); // get length 
     p1.nx = nx/p1.len; // get normalised vector 
     p1.ny = ny/p1.len; 
    } 

一旦你有每条线段的细节是一件简单的事情的变化根据图纸值参数。

我已添加演示。它可能会让我很难接近这个例子,因为您提供的线路没有给出选项来绘制您的图像。图像显示他们也使用阴影,并进行子鼠标采样。他们绘制的圆形框可能是一个图像,比我绘制的框要快得多。我也平滑了这一行,所以在绘制和最终确定背景图像之间存在一点点滞后。

演示顶部是一组控制各种设置的常量。如果您发现代码有用,那么添加输入选项和审查将花费太多时间,因此请与他们一起玩。

对不起,代码是混乱的,但它只是一个例子,你将不得不把它分开自己。

/** hypot.js begin **/ 
 
// ES6 new math function hypot. Sqrt of the sum of the squares 
 
var hypot = Math.hypot; 
 
if(typeof hypot === 'undefined'){ 
 
    hypot = function(x,y){ 
 
     return Math.sqrt(Math.pow(x,2)+Math.pow(y,2)); 
 
    } 
 
} 
 

 
/** hypot.js end **/ 
 

 
// draw options 
 
const SUB_SECTIONS = 5; // points between mouse samples 
 
const SIZE_MULT = 3; // Max size multiplier 
 
const SIZE_MIN = 0.1 // min size of line 
 
const BIG_DIR = 0.6; // direction in radians for thickest line 
 
const SMOOTH_MAX = 7; // number of smoothing steps performed on a line. Bigger that 20 will slow the rendering down 
 
const SHAPE_ALPHA = 0.5; // the stoke alpha 
 
const SHAPE_FILL_ALPHA = 0.75; // the fill alpha 
 
const SHADOW_ALPHA = 0.1; // the shadow alpha 
 
const SHADOW_BLUR = 5; // the shadow blur 
 
const SHADOW_OFFX = 6; // shoadow offest x and y 
 
const SHADOW_OFFY = 6; 
 
const SHAPE_LINE_WIDTH = 0.6; // stroke width of shape. This is constant and is not scaled 
 
const SHAPE_WIDTH = 4; // shape drawn width; 
 
const SHAPE_LENGTH = 20; // shape drawn length 
 
const SHAPE_ROUNDING = 2; // shape rounded corner radius. Warning invalid results if rounding is greater than half width or height which ever is the smallest 
 
const SHAPE_TRAIL = 0; // offset draw shape. Negivive numbers trail drawing positive are infront 
 

 
var div = document.createElement("div"); 
 
div.textContent = "Click drag mouse to draw, Right click to clear." 
 
document.body.appendChild(div); 
 

 
var mouse; 
 
var demo = function(){ 
 
    
 
    /** fullScreenCanvas.js begin **/ 
 
    var canvas = (function(){ 
 
     var canvas = document.getElementById("canv"); 
 
     if(canvas !== null){ 
 
      document.body.removeChild(canvas); 
 
     } 
 
     // creates a blank image with 2d context 
 
     canvas = document.createElement("canvas"); 
 
     canvas.id = "canv";  
 
     canvas.width = window.innerWidth; 
 
     canvas.height = window.innerHeight; 
 
     canvas.style.position = "absolute"; 
 
     canvas.style.top = "0px"; 
 
     canvas.style.left = "0px"; 
 
     canvas.style.zIndex = 1000; 
 
     canvas.ctx = canvas.getContext("2d"); 
 
     document.body.appendChild(canvas); 
 
     return canvas; 
 
    })(); 
 
    var ctx = canvas.ctx; 
 
    
 
    /** fullScreenCanvas.js end **/ 
 
    /** MouseFull.js begin **/ 
 
    if(typeof mouse !== "undefined"){ // if the mouse exists 
 
     if(mouse.removeMouse !== undefined){ 
 
      mouse.removeMouse(); // remove previouse events 
 
     } 
 
    } 
 
    var canvasMouseCallBack = undefined; // if needed 
 
    mouse = (function(){ 
 
     var mouse = { 
 
      x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, 
 
      interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0, 
 
      over : false, // mouse is over the element 
 
      bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits; 
 
      getInterfaceId : function() { return this.interfaceId++; }, // For UI functions 
 
      startMouse:undefined, 
 
      mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",") 
 
     }; 
 
     function mouseMove(e) { 
 
      var t = e.type, m = mouse; 
 
      m.x = e.offsetX; m.y = e.offsetY; 
 
      if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; } 
 
      m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey; 
 
      if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; 
 
      } else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; 
 
      } else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; 
 
      } else if (t === "mouseover") { m.over = true; 
 
      } else if (t === "mousewheel") { m.w = e.wheelDelta; 
 
      } else if (t === "DOMMouseScroll") { m.w = -e.detail;} 
 
      if (canvasMouseCallBack) { canvasMouseCallBack(mouse); } 
 
      e.preventDefault(); 
 
     } 
 
     function startMouse(element){ 
 
      if(element === undefined){ 
 
       element = document; 
 
      } 
 
      mouse.element = element; 
 
      mouse.mouseEvents.forEach(
 
       function(n){ 
 
        element.addEventListener(n, mouseMove); 
 
       } 
 
      ); 
 
      element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false); 
 
     } 
 
     mouse.removeMouse = function(){ 
 
      if(mouse.element !== undefined){ 
 
       mouse.mouseEvents.forEach(
 
        function(n){ 
 
         mouse.element.removeEventListener(n, mouseMove); 
 
        } 
 
       ); 
 
       canvasMouseCallBack = undefined; 
 
      } 
 
     } 
 
     mouse.mouseStart = startMouse; 
 
     return mouse; 
 
    })(); 
 
    if(typeof canvas !== "undefined"){ 
 
     mouse.mouseStart(canvas); 
 
    }else{ 
 
     mouse.mouseStart(); 
 
    } 
 
    /** MouseFull.js end **/ 
 
    /** CreateImage.js begin **/ 
 
    // creates a blank image with 2d context 
 
    var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;} 
 
    
 
    /** CreateImage.js end **/ 
 
    /** FrameUpdate.js begin **/ 
 
    var w = canvas.width; 
 
    var h = canvas.height; 
 
    var cw = w/2; 
 
    var ch = h/2; 
 
    var line = []; // line to hold drawing points 
 
    var image = createImage(w,h); // Background image to dump point to when soothed 
 
    
 
    var PI2 = Math.PI * 2; // 360 to save typing 
 
    var PIh = Math.PI/2; // 90 
 
    
 
    // draws a rounded rectangle path 
 
    function roundedRect(ctx,x, y, w, h, r){ 
 

 
     ctx.beginPath(); 
 
     ctx.arc(x + r, y + r, r, PIh * 2, PIh * 3); 
 
     ctx.arc(x + w - r, y + r, r, PIh * 3, PI2); 
 
     ctx.arc(x + w - r, y + h - r, r, 0, PIh); 
 
     ctx.arc(x + r, y + h - r, r, PIh, PIh * 2); 
 
     ctx.closePath(); 
 
    } 
 

 
    // this draws a section of line 
 
    function drawStroke(ctx,line){ 
 
     var len = line.length; 
 

 
     ctx.shadowBlur = SHADOW_BLUR; 
 
     ctx.shadowOffsetX = SHADOW_OFFX; 
 
     ctx.shadowOffsetY = SHADOW_OFFY; 
 
     ctx.shadowColor = "rgba(0,0,0," + SHADOW_ALPHA + ")"; 
 
     ctx.strokeStyle = "rgba(0,0,0," + SHAPE_FILL_ALPHA + ")"; 
 
     ctx.fillStyle = "rgba(255,255,255," + SHAPE_ALPHA + ")"; 
 
     for (var i = 0; i < len - 1; i++) { // for each point minus 1 
 
      var p1 = line[i]; 
 
      var p2 = line[i + 1]; // get the point and one ahead 
 
      if (p1.dir && p2.dir) { // do both points have a direction 
 
       // divide the distance between the points by 5 and draw each sub section 
 
       for (var k = 0; k < p1.len; k += p1.len/SUB_SECTIONS) { 
 
        // get the points between mouse samples 
 
        var x = p1.x + p1.nx * k; 
 
        var y = p1.y + p1.ny * k; 
 
        var kk = k/p1.len; // get normalised distance 
 
        // tween direction but need to check cyclic 
 
        if (p1.dir > Math.PI * 1.5 && p2.dir < Math.PI/2) { 
 
         var dir = ((p2.dir + Math.PI * 2) - p1.dir) * kk + p1.dir; 
 
        } else 
 
        if (p2.dir > Math.PI * 1.5 && p1.dir < Math.PI/2) { 
 
         var dir = ((p2.dir - Math.PI * 2) - p1.dir) * kk + p1.dir; 
 
        } else { 
 
         var dir = (p2.dir - p1.dir) * kk + p1.dir; 
 
        } 
 

 
        // get size dependent on direction 
 
        var size = (Math.abs(Math.sin(dir + BIG_DIR)) + SIZE_MIN) * SIZE_MULT; 
 
        // caculate the transform requiered. 
 
        var xdx = Math.cos(dir) * size; 
 
        var xdy = Math.sin(dir) * size; 
 
        // set the line width to the invers scale so it remains constant 
 
        ctx.lineWidth = SHAPE_LINE_WIDTH * (1/size); // make sure that the line width does not scale 
 
        // set the transform 
 
        ctx.setTransform(xdx, xdy, -xdy, xdx, x, y); 
 
        // draw the shape 
 
        roundedRect(ctx, -SHAPE_LENGTH/2 - SHAPE_TRAIL, -SHAPE_WIDTH/2, SHAPE_LENGTH, SHAPE_WIDTH, SHAPE_ROUNDING); 
 
        // fill and stroke 
 
        ctx.fill(); 
 
        ctx.stroke(); 
 
       } 
 
      } 
 
     } 
 
     // restore transform 
 
     ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
    } 
 

 
    // update function will try 60fps but setting will slow this down.  
 
    function update(){ 
 
     // restore transform 
 
     ctx.setTransform(1, 0, 0, 1, 0, 0); 
 
     // clear 
 
     ctx.clearRect(0, 0, w, h); 
 
     // get line length 
 
     var len = line.length; 
 
     
 
     if (mouse.buttonRaw !== 1) { // button up so draw all onto image 
 
      drawStroke(image.ctx, line) 
 
      line = []; 
 
     } else { 
 
      // remove trailing line segments that are no longer being smoothed 
 
      if (len > SMOOTH_MAX * 2) { 
 
       var a = line.splice(0, SMOOTH_MAX - 1) 
 
        a.push(line[0]); 
 
       drawStroke(image.ctx, a) 
 
      } 
 
     } 
 
     // draw background image 
 
     ctx.drawImage(image, 0, 0); 
 

 
     // is the button down 
 
     if (mouse.buttonRaw === 1) { 
 
      // if more than one point 
 
      if (line.length > 0) { 
 
       // only add a point if mouse has moved. 
 
       if (mouse.x !== line[line.length - 1].x || mouse.y !== line[line.length - 1].y) { 
 
        line.push({ 
 
         x : mouse.x, 
 
         y : mouse.y, 
 
         s : 0 
 
        }); 
 
       } 
 
      } else { 
 
       // add a point if no points exist 
 
       line.push({ 
 
        x : mouse.x, 
 
        y : mouse.y, 
 
        s : 0 
 
       }); 
 
      } 
 
     } 
 
     // get number of points 
 
     var len = line.length; 
 
     
 
     
 
     if(mouse.buttonRaw === 1){ // mouse down the do simple running average smooth 
 
      // This smooth will continue to refine points untill the it is outside the 
 
      // smoothing range/ 
 
      for (var i = 0; i < len - 3; i++) { 
 
       var p1 = line[i]; 
 
       var p2 = line[i + 1]; 
 
       var p3 = line[i + 2]; 
 
       if (p1.s < SMOOTH_MAX) { 
 
        p1.s += 1; 
 
        p2.x = ((p1.x + p3.x)/2 + p2.x * 2)/3; 
 
        p2.y = ((p1.y + p3.y)/2 + p2.y * 2)/3; 
 
       } 
 
      } 
 
      // caculate the direction, length and normalised vector for 
 
      // each line segment and add to the point 
 
      for(var i = 0; i < len-1; i++){ 
 
       var p1 = line[i]; 
 
       var p2 = line[i + 1]; 
 
       var nx = p2.x - p1.x; 
 
       var ny = p2.y - p1.y; 
 
       p1.dir = ((Math.atan2(ny, nx) % PI2) + PI2) % PI2; // get direction 
 
       p1.len = hypot(nx, ny); // get length 
 
       p1.nx = nx/p1.len; // get normalised vector 
 
       p1.ny = ny/p1.len; 
 
    
 
      } 
 
      // draw the line points onto the canvas. 
 
      drawStroke(ctx,line) 
 
     } 
 
     if((mouse.buttonRaw & 4)=== 4){ 
 
      line = []; 
 
      image.ctx.clearRect(0,0,w,h); 
 
      ctx.clearRect(0,0,w,h); 
 
      mouse.buttonRaw = 0; 
 
     } 
 
     if(!STOP){ 
 
      requestAnimationFrame(update); 
 
     }else{ 
 
      var can = document.getElementById("canv"); 
 
      if(can !== null){ 
 
       document.body.removeChild(can); 
 
      }  
 
      STOP = false; 
 
      
 
     } 
 
    } 
 

 
    update(); 
 

 
} 
 
var STOP = false; // flag to tell demo app to stop 
 
function resizeEvent(){ 
 
    var waitForStopped = function(){ 
 
     if(!STOP){ // wait for stop to return to false 
 
      demo(); 
 
      return; 
 
     } 
 
     setTimeout(waitForStopped,200); 
 
    } 
 
    STOP = true; 
 
    setTimeout(waitForStopped,100); 
 
} 
 
window.addEventListener("resize",resizeEvent); 
 
demo(); 
 
/** FrameUpdate.js end **/

+0

这是非常有益的,正是我一直在寻找 - 找不到anythign像这样的任何地方。非常感谢你。 – eirikrl