在FabricJS中画出一条波浪线 [英] Drawing a wavy line in FabricJS

查看:1108
本文介绍了在FabricJS中画出一条波浪线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用



我已经用箭头端点成功创建了这个版本,但找不到任何如何创建波浪线的示例。用户可以根据需要绘制线条,因此线条中的峰值和波谷数量需要相应调整(如上图所示的短线可能有4个峰值但是线条长度的两倍会有8个峰值,不仅仅是较短线的拉伸版本。)



这是我用来绘制带有箭头端点的直线的代码。请注意,该行的起点是在mousedown上绘制的,端点是在mouseup上绘制的。

 从'./导入LineWithArrow LineWithArrow; 

drawLineWithArrow =(item,points,color)=> (
new LineWithArrow(points,{
customProps:item,
strokeWidth:2,
stroke:color,
})


selectLine =(item,points)=> {
switch(item.type){
case'line_with_arrow':
return this.drawLineWithArrow(item,points,colors.BLACK);

case'wavy_line_with_arrow':
返回this.drawWavyLineWithArrow(item,points);
//无默认
}
返回null;
}

let line;
let isDown;

fabricCanvas.on('mouse:down',(options)=> {
isDown = true;
const pointer = fabricCanvas.getPointer(options.e);
const points = [pointer.x,pointer.y,pointer.x,pointer.y];
line = this.selectLine(item,points);
fabricCanvas
.add (line)
.setActiveObject(line)
.renderAll();
});

fabricCanvas.on('mouse:move',(options)=> {
if(!isDown)return;
const pointer = fabricCanvas.getPointer(options.e );
line.set({x2:pointer.x,y2:pointer.y});
fabricCanvas.renderAll();
});

fabricCanvas.on('mouse:up',()=> {
isDown = false;
line.setCoords();
fabricCanvas.setActiveObject( line).renderAll();
});

和LineWithArrow文件:

 从'fabric'导入{fabric}; 

const LineWithArrow = fabric.util.createClass(fabric.Line,{
type:'line_with_arrow',

initialize(element,options){
options ||(options = {});
this.callSuper('initialize',element,options);

//设置默认选项
this.set({
hasBorders:false,
hasControls:false,
});
},

_render(ctx){
this.callSuper( '_render',ctx);
ctx.save();
const xDiff = this.x2 - this.x1;
const yDiff = this.y2 - this.y1;
const angle = Math.atan2(yDiff,xDiff);
ctx.translate((this.x2 - this.x1)/ 2,(this.y2 - this.y1)/ 2);
ctx.rotate(angle);
ctx.beginPath();
//在行前移动5px以启动箭头,使其前面没有方形行结束(0,0)
ctx.moveTo(5,0);
ctx.lineTo(-5,5);
ctx.lineTo(-5,-5);
ctx.closePath( );
ctx.fillStyle = this.stroke;
ctx.fill();
ctx.restore();
},

toObject(){
返回fabric.util.object.extend(this.callSuper('toObject'),{
customProps:this.customProps ,
});
},
});

导出默认LineWithArrow;


解决方案

因为我们从两个角线画一条线,你可以在自定义类的 _render 方法中绘制波浪线。从最后我画一条线到中间,显示它与箭头相连。



DEMO



  var line,isDown,evented; var canvas = new fabric.Canvas('canvas',{ perPixelTargetFind:true}); draw(); function selection(){changeObjSelection(true); canvas.off(鼠标:向下); canvas.off(鼠标:移动); canvas.off(鼠标:向上); evented = false;} function draw(){changeObjSelection(false); if(!evented){canvas.on('mouse:down',onMouseDown); canvas.on('mouse:move',onMouseMove); canvas.on('mouse:up',onMouseUp); evented = true; function clearCanvas(){canvas.clear();} function changeObjSelection(value){canvas.selection = value; canvas.forEachObject(function(obj){obj.selectable = value;})canvas.requestRenderAll();} function onMouseDown(options){isDown = true; var pointer = canvas.getPointer(options.e); var points = [pointer.x,pointer.y,pointer.x,pointer.y]; line = selectLine(points); canvas.add(line);} function onMouseMove(options){if(!isDown)return; var pointer = canvas.getPointer(options.e); line.set({x2:pointer.x,y2:pointer.y}); canvas.renderAll();}函数onMouseUp(options){isDown = false; line.setCoords(); canvas.requestRenderAll();} function drawLineWithArrow(points,color){return new fabric.LineWithArrow(points,{strokeWidth:2,stroke:color,objectCaching:false,selectable:false})} function selectLine(points){return drawLineWithArrow (点,'黑');} //波浪线(函数(全局){'使用严格'; if(fabric.LineWithArrow){fabric.warn('fabric.LineWithArrow已经定义。'); return;} fabric .LineWithArrow = fabric.util.createClass(fabric.Line,{type:'line_with_arrow',initialize:function(element,options){options ||(options = {}); this.callSuper('initialize',element,options ); //设置默认选项this.set({hasBorders:false,hasControls:false,});},_ render:function(ctx){// this.callSuper('_ render',ctx); ctx.save() ; const xDiff = this.x2  -  this.x1; const yDiff = this.y2  -  this.y1; const angle = Math.atan2(yDiff,xDiff); ctx.trans迟到(xDiff / 2,yDiff / 2); ctx.rotate(角度); ctx.beginPath(); //在行前移动5px以启动箭头,使其没有前面显示的方线结束(0,0)ctx.moveTo(5,0); ctx.lineTo(-5,5); ctx.lineTo(-5,-5); ctx.closePath(); ctx.fillStyle = this.stroke; ctx.fill(); ctx.restore(); var p = this.calcLinePoints(); var point = this.pointOnLine(this.point(p.x2,p.y2),this.point(p.x1,p.y1),10)this.wavy(this.point(p.x1,p.y1) ),point,this.point(p.x2,p.y2),ctx); ctx.stroke(); },point:function(x,y){return {x:x,y:y}; },wavy:function(from,to,endPoint,ctx){var cx = 0,cy = 0,fx = from.x,fy = from.y,tx = to.x,ty = to.y,i = 0,step = 4,waveOffsetLength = 0,ang = Math.atan2(ty  -  fy,tx  -  fx),distance = Math.sqrt((fx  -  tx)*(fx  -  tx)+(fy  -  ty)*( fy-ty)),幅度= -10,f = Math.PI *距离/ 30; for(i; i< = distance; i + = step){waveOffsetLength = Math.sin((i / distance)* f)*幅度; cx = from.x + Math.cos(ang)* i + Math.cos(ang  -  Math.PI / 2)* waveOffsetLength; cy = from.y + Math.sin(ang)* i + Math.sin(ang  -  Math.PI / 2)* waveOffsetLength;我> 0? ctx.lineTo(cx,cy):ctx.moveTo(cx,cy); } ctx.lineTo(to.x,to.y); ctx.lineTo(endPoint.x,endPoint.y); },pointOnLine:function(point1,point2,dist){var len = Math.sqrt(((point2.x  -  point1.x)*(point2.x  -  point1.x))+((point2.y  -  point1。 y)*(point2.y  -  point1.y))); var t =(dist)/ len; var x3 =((1-t)* point1.x)+(t * point2.x),y3 =((1-t)* point1.y)+(t * point2.y);返回新的fabric.Point(x3,y3); },toObject:function(){return fabric.util.object.extend(this.callSuper('toObject'),{customProps:this.customProps,}); },});})(typeof exports!=='undefined'?exports:this);  

  canvas {border:2px dotted black;}  

 < script src =https://rawgit.com/kangax/fabric.js/master/dist/fabric.js>< / script>< button type =button onclick =selection()> selection< / button>< button type =buttononclick =draw()> draw< / button>< button type =buttononclick =clearCanvas() > clear< / button>< canvas id =canvaswidth =400height =400>< / canvas>  


I'm using FabricJS to create a canvas for drawing specific lines and shapes. One of the lines is a wavy line with an arrow similar to this:

I've successfully created a straight version of this with an arrow endpoint but can't find any examples of how to create a wavy line. The user can draw the line as long as they want so the number of 'peaks' and 'troughs' in the line will need to adapt accordingly (a short line like the image above might have 4 peaks but a line twice the length would have 8 peaks, not just be a stretched version of the shorter line).

Here is the code I'm using to draw the straight line with the arrow endpoint. Note that the start point of the line is drawn on mousedown and the endpoint is drawn on mouseup.

import LineWithArrow from './LineWithArrow';

drawLineWithArrow = (item, points, color) => (
  new LineWithArrow(points, {
    customProps: item,
    strokeWidth: 2,
    stroke: color,
  })
)

selectLine = (item, points) => {
  switch (item.type) {
    case 'line_with_arrow':
      return this.drawLineWithArrow(item, points, colors.BLACK);

    case 'wavy_line_with_arrow':
      return this.drawWavyLineWithArrow(item, points);
    // no default
  }
  return null;
}

let line;
let isDown;

fabricCanvas.on('mouse:down', (options) => {
  isDown = true;
  const pointer = fabricCanvas.getPointer(options.e);
  const points = [pointer.x, pointer.y, pointer.x, pointer.y];
  line = this.selectLine(item, points);
  fabricCanvas
    .add(line)
    .setActiveObject(line)
    .renderAll();
});

fabricCanvas.on('mouse:move', (options) => {
  if (!isDown) return;
  const pointer = fabricCanvas.getPointer(options.e);
  line.set({ x2: pointer.x, y2: pointer.y });
  fabricCanvas.renderAll();
});

fabricCanvas.on('mouse:up', () => {
  isDown = false;
  line.setCoords();
  fabricCanvas.setActiveObject(line).renderAll();
});

And the LineWithArrow file:

import { fabric } from 'fabric';

const LineWithArrow = fabric.util.createClass(fabric.Line, {
  type: 'line_with_arrow',

  initialize(element, options) {
    options || (options = {});
    this.callSuper('initialize', element, options);

    // Set default options
    this.set({
      hasBorders: false,
      hasControls: false,
    });
  },

  _render(ctx) {
    this.callSuper('_render', ctx);
    ctx.save();
    const xDiff = this.x2 - this.x1;
    const yDiff = this.y2 - this.y1;
    const angle = Math.atan2(yDiff, xDiff);
    ctx.translate((this.x2 - this.x1) / 2, (this.y2 - this.y1) / 2);
    ctx.rotate(angle);
    ctx.beginPath();
    // Move 5px in front of line to start the arrow so it does not have the square line end showing in front (0,0)
    ctx.moveTo(5, 0);
    ctx.lineTo(-5, 5);
    ctx.lineTo(-5, -5);
    ctx.closePath();
    ctx.fillStyle = this.stroke;
    ctx.fill();
    ctx.restore();
  },

  toObject() {
    return fabric.util.object.extend(this.callSuper('toObject'), {
      customProps: this.customProps,
    });
  },
});

export default LineWithArrow;

解决方案

As we are drawing from both corners for a line, you can draw wavy line in _render method of custom class. From the end I draw a line to mid , to show its connected with arrow.

DEMO

var line, isDown, evented;
var canvas = new fabric.Canvas('canvas', {
  perPixelTargetFind: true
});
draw();

function selection() {
  changeObjSelection(true);
  canvas.off('mouse:down');
  canvas.off('mouse:move');
  canvas.off('mouse:up');
  evented = false;
}

function draw() {
  changeObjSelection(false);
  if (!evented) {
    canvas.on('mouse:down', onMouseDown);
    canvas.on('mouse:move', onMouseMove);
    canvas.on('mouse:up', onMouseUp);
    evented = true;
  }
}

function clearCanvas() {
 canvas.clear();
}

function changeObjSelection(value) {
  canvas.selection = value;
  canvas.forEachObject(function(obj) {
    obj.selectable = value;
  })
  canvas.requestRenderAll();
}

function onMouseDown(options) {
  isDown = true;
  var pointer = canvas.getPointer(options.e);
  var points = [pointer.x, pointer.y, pointer.x, pointer.y];
  line = selectLine(points);
  canvas.add(line);
}

function onMouseMove(options) {
  if (!isDown) return;
  var pointer = canvas.getPointer(options.e);
  line.set({
    x2: pointer.x,
    y2: pointer.y
  });
  canvas.renderAll();

}

function onMouseUp(options) {
  isDown = false;
  line.setCoords();
  canvas.requestRenderAll();
}

function drawLineWithArrow(points, color) {
  return new fabric.LineWithArrow(points, {
    strokeWidth: 2,
    stroke: color,
    objectCaching: false,
    selectable: false
  })
}

function selectLine(points) {
  return drawLineWithArrow(points, 'black');
}

//Wavy line

(function(global) {
  'use strict';
  if (fabric.LineWithArrow) {
    fabric.warn('fabric.LineWithArrow is already defined.');
    return;
  }
  fabric.LineWithArrow = fabric.util.createClass(fabric.Line, {
    type: 'line_with_arrow',

    initialize: function(element, options) {
      options || (options = {});
      this.callSuper('initialize', element, options);

      // Set default options
      this.set({
        hasBorders: false,
        hasControls: false,
      });
    },

    _render: function(ctx) {
      // this.callSuper('_render', ctx);
      ctx.save();
      const xDiff = this.x2 - this.x1;
      const yDiff = this.y2 - this.y1;
      const angle = Math.atan2(yDiff, xDiff);
      ctx.translate(xDiff / 2, yDiff / 2);
      ctx.rotate(angle);
      ctx.beginPath();
      // Move 5px in front of line to start the arrow so it does not have the square line end showing in front (0,0)
      ctx.moveTo(5, 0);
      ctx.lineTo(-5, 5);
      ctx.lineTo(-5, -5);
      ctx.closePath();
      ctx.fillStyle = this.stroke;
      ctx.fill();
      ctx.restore();
      var p = this.calcLinePoints();
      var point = this.pointOnLine(this.point(p.x2, p.y2), this.point(p.x1, p.y1), 10)
      this.wavy(this.point(p.x1, p.y1), point, this.point(p.x2, p.y2), ctx);
      ctx.stroke();
    },

    point: function(x, y) {
      return {
        x: x,
        y: y
      };
    },

    wavy: function(from, to, endPoint, ctx) {
      var cx = 0,
        cy = 0,
        fx = from.x,
        fy = from.y,
        tx = to.x,
        ty = to.y,
        i = 0,
        step = 4,
        waveOffsetLength = 0,

        ang = Math.atan2(ty - fy, tx - fx),
        distance = Math.sqrt((fx - tx) * (fx - tx) + (fy - ty) * (fy - ty)),
        amplitude = -10,
        f = Math.PI * distance / 30;

      for (i; i <= distance; i += step) {
        waveOffsetLength = Math.sin((i / distance) * f) * amplitude;
        cx = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI / 2) * waveOffsetLength;
        cy = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI / 2) * waveOffsetLength;
        i > 0 ? ctx.lineTo(cx, cy) : ctx.moveTo(cx, cy);
      }
      ctx.lineTo(to.x, to.y);
      ctx.lineTo(endPoint.x, endPoint.y);
    },

    pointOnLine: function(point1, point2, dist) {
      var len = Math.sqrt(((point2.x - point1.x) * (point2.x - point1.x)) + ((point2.y - point1.y) * (point2.y - point1.y)));
      var t = (dist) / len;
      var x3 = ((1 - t) * point1.x) + (t * point2.x),
        y3 = ((1 - t) * point1.y) + (t * point2.y);
      return new fabric.Point(x3, y3);
    },

    toObject: function() {
      return fabric.util.object.extend(this.callSuper('toObject'), {
        customProps: this.customProps,
      });
    },
  });
})(typeof exports !== 'undefined' ? exports : this);

canvas {
  border: 2px dotted black;
}

<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<button type="button" onclick="selection()">selection</button>
<button type="button" onclick="draw()">draw</button>
<button type="button" onclick="clearCanvas()">clear</button>
<canvas id="canvas" width="400" height="400"></canvas>

这篇关于在FabricJS中画出一条波浪线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆