在鼠标上移之前在下部画布上绘制笔触

问题描述 投票:1回答:1

我试图在鼠标移开事件之前立即在较低的画布上渲染图形。

我发现了一些描述此问题的主题,但是我不明白如何在项目中使用它。

Emulate free drawing with fabricjs

Fabrics - How to render free drawing content before mouse up

How to programmatically free draw using Fabric js?

我的代码笔example

var canvas = this.__canvas = new fabric.Canvas('c', {
  isDrawingMode: true
});
  
canvas {
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"></script>   
<canvas id="c" width="300" height="300"></canvas>
javascript html5-canvas fabricjs
1个回答
0
投票

我认为您已经陷入XY问题。注释中解释了您所需要的,所要问的是如何去做自己认为是解决该问题的方法的事情。

您设置了一个特定的应用程序,该应用程序在每次after:render事件时以静态表示形式将画布克隆到另一个画布中。

[现在,自由绘制画笔不会触发after:render事件,即使那样,它也不会在mouse:up事件之前出现在画布上。

因此,为了解决您的问题,您希望将画笔放在下部画布上,以便您现有的代码能够找到它。

这很难做到,因为这意味着要弄乱库的内部逻辑。为什么每次添加画笔的新片段时都只能复制上层画布?这显然会让您达到目标。

我的答案和摘要并不完美,它没有考虑会多次复制的透明画笔。

另一种解决方案是在第一个静态画布上放置第二个静态画布,以处理这些中间阶段。

var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");

var ctx1 = c1.getContext("2d");
var ctx2 = c2.getContext("2d");

var canvas = this.__canvas = new fabric.Canvas(c1, {
  isDrawingMode: true,
  renderOnAddRemove: false 
});
  
canvas.add(new fabric.Rect({ top: 100, left: 100, width: 50, height: 50, fill: '#f55' }),
            new fabric.Circle({ top: 140, left: 130, radius: 75, fill: 'green' }),
            new fabric.Triangle({ top: 100, left: 110, width: 100, height: 100, fill: 'blue' }))


fabric.Image.fromURL(
  "https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
  img => {
    img.scaleToWidth(canvas.width);
    canvas.setBackgroundImage(img);
    canvas.requestRenderAll();
  },
  {
    crossOrigin: "Annoymous"
  }
);


canvas.on('mouse:wheel', function(opt) {
  var delta = opt.e.deltaY;
  var pointer = canvas.getPointer(opt.e);
  var zoom = canvas.getZoom();
  zoom += delta / 500;
  if (zoom > 10) zoom = 10;
  if (zoom < 0.5) zoom = 0.5;
  canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
  this.requestRenderAll();
  opt.e.preventDefault();
  opt.e.stopPropagation();
});


fabric.StaticCanvas.prototype.drawCopyOnCanvas = function(canvasEl) {
  // save values
  var scaledWidth = this.width,
      scaledHeight = this.height,
      vp = this.viewportTransform,
      originalInteractive = this.interactive,
      newVp = [1, 0, 0, 1, 0, 0],
      originalRetina = this.enableRetinaScaling,
      originalContextTop = this.contextTop;
  // reset
  this.contextTop = null;
  this.enableRetinaScaling = false;
  this.interactive = false;
  this.viewportTransform = newVp;
  this.calcViewportBoundaries();
  // draw on copy
  this.renderCanvas(canvasEl.getContext('2d'), this._objects);
  // restore values
  this.viewportTransform = vp;
  this.calcViewportBoundaries();
  this.interactive = originalInteractive;
  this.enableRetinaScaling = originalRetina;
  this.contextTop = originalContextTop;
}

function afterRender() {
  // remove 'after:render' listener as canvas.toCanvasElement()
  // calls renderCanvas(), which results in an infinite recursion
  canvas.off('after:render', afterRender);
  // draw c1 contents on c2
  canvas.drawCopyOnCanvas(c2);
  
  setTimeout(() => {
    // re-attach the listener in the next event loop
    canvas.on('after:render', afterRender);
  });
}

function copyThePencil() {
  if (this._isCurrentlyDrawing) {
    ctx2.save();
    var m = fabric.util.invertTransform(this.viewportTransform);
    ctx2.transform.apply(ctx2, m);
    ctx2.drawImage(this.upperCanvasEl, 0, 0, this.upperCanvasEl.width / this.getRetinaScaling(), this.upperCanvasEl.height / this.getRetinaScaling())
    ctx2.restore();
  }
}

canvas.on('after:render', afterRender);

canvas.on('mouse:move', copyThePencil);





















var $ = function(id){return document.getElementById(id)};

  fabric.Object.prototype.transparentCorners = false;

  var drawingModeEl = $('drawing-mode'),
      drawingOptionsEl = $('drawing-mode-options'),
      drawingColorEl = $('drawing-color'),
      drawingShadowColorEl = $('drawing-shadow-color'),
      drawingLineWidthEl = $('drawing-line-width'),
      drawingShadowWidth = $('drawing-shadow-width'),
      drawingShadowOffset = $('drawing-shadow-offset'),
      clearEl = $('clear-canvas');

  clearEl.onclick = function() { canvas.clear() };

  drawingModeEl.onclick = function() {
    canvas.isDrawingMode = !canvas.isDrawingMode;
    if (canvas.isDrawingMode) {
      drawingModeEl.innerHTML = 'Cancel drawing mode';
      drawingOptionsEl.style.display = '';
    }
    else {
      drawingModeEl.innerHTML = 'Enter drawing mode';
      drawingOptionsEl.style.display = 'none';
    }
  };

  if (fabric.PatternBrush) {
    var vLinePatternBrush = new fabric.PatternBrush(canvas);
    vLinePatternBrush.getPatternSrc = function() {

      var patternCanvas = fabric.document.createElement('canvas');
      patternCanvas.width = patternCanvas.height = 10;
      var ctx = patternCanvas.getContext('2d');

      ctx.strokeStyle = this.color;
      ctx.lineWidth = 5;
      ctx.beginPath();
      ctx.moveTo(0, 5);
      ctx.lineTo(10, 5);
      ctx.closePath();
      ctx.stroke();

      return patternCanvas;
    };

    var hLinePatternBrush = new fabric.PatternBrush(canvas);
    hLinePatternBrush.getPatternSrc = function() {

      var patternCanvas = fabric.document.createElement('canvas');
      patternCanvas.width = patternCanvas.height = 10;
      var ctx = patternCanvas.getContext('2d');

      ctx.strokeStyle = this.color;
      ctx.lineWidth = 5;
      ctx.beginPath();
      ctx.moveTo(5, 0);
      ctx.lineTo(5, 10);
      ctx.closePath();
      ctx.stroke();

      return patternCanvas;
    };

    var squarePatternBrush = new fabric.PatternBrush(canvas);
    squarePatternBrush.getPatternSrc = function() {

      var squareWidth = 10, squareDistance = 2;

      var patternCanvas = fabric.document.createElement('canvas');
      patternCanvas.width = patternCanvas.height = squareWidth + squareDistance;
      var ctx = patternCanvas.getContext('2d');

      ctx.fillStyle = this.color;
      ctx.fillRect(0, 0, squareWidth, squareWidth);

      return patternCanvas;
    };

    var diamondPatternBrush = new fabric.PatternBrush(canvas);
    diamondPatternBrush.getPatternSrc = function() {

      var squareWidth = 10, squareDistance = 5;
      var patternCanvas = fabric.document.createElement('canvas');
      var rect = new fabric.Rect({
        width: squareWidth,
        height: squareWidth,
        angle: 45,
        fill: this.color
      });

      var canvasWidth = rect.getBoundingRect().width;

      patternCanvas.width = patternCanvas.height = canvasWidth + squareDistance;
      rect.set({ left: canvasWidth / 2, top: canvasWidth / 2 });

      var ctx = patternCanvas.getContext('2d');
      rect.render(ctx);

      return patternCanvas;
    };

    var img = new Image();
    img.src = 'https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg';

    var texturePatternBrush = new fabric.PatternBrush(canvas);
    texturePatternBrush.source = img;
  }

  $('drawing-mode-selector').onchange = function() {

    if (this.value === 'hline') {
      canvas.freeDrawingBrush = vLinePatternBrush;
    }
    else if (this.value === 'vline') {
      canvas.freeDrawingBrush = hLinePatternBrush;
    }
    else if (this.value === 'square') {
      canvas.freeDrawingBrush = squarePatternBrush;
    }
    else if (this.value === 'diamond') {
      canvas.freeDrawingBrush = diamondPatternBrush;
    }
    else if (this.value === 'texture') {
      canvas.freeDrawingBrush = texturePatternBrush;
    }
    else {
      canvas.freeDrawingBrush = new fabric[this.value + 'Brush'](canvas);
    }

    if (canvas.freeDrawingBrush) {
      canvas.freeDrawingBrush.color = drawingColorEl.value;
      canvas.freeDrawingBrush.width = parseInt(drawingLineWidthEl.value, 10) || 1;
      canvas.freeDrawingBrush.shadow = new fabric.Shadow({
        blur: parseInt(drawingShadowWidth.value, 10) || 0,
        offsetX: 0,
        offsetY: 0,
        affectStroke: true,
        color: drawingShadowColorEl.value,
      });
    }
  };

  drawingColorEl.onchange = function() {
    canvas.freeDrawingBrush.color = this.value;
  };
  drawingShadowColorEl.onchange = function() {
    canvas.freeDrawingBrush.shadow.color = this.value;
  };
  drawingLineWidthEl.onchange = function() {
    canvas.freeDrawingBrush.width = parseInt(this.value, 10) || 1;
    this.previousSibling.innerHTML = this.value;
  };
  drawingShadowWidth.onchange = function() {
    canvas.freeDrawingBrush.shadow.blur = parseInt(this.value, 10) || 0;
    this.previousSibling.innerHTML = this.value;
  };
  drawingShadowOffset.onchange = function() {
    canvas.freeDrawingBrush.shadow.offsetX = parseInt(this.value, 10) || 0;
    canvas.freeDrawingBrush.shadow.offsetY = parseInt(this.value, 10) || 0;
    this.previousSibling.innerHTML = this.value;
  };

  if (canvas.freeDrawingBrush) {
    canvas.freeDrawingBrush.color = drawingColorEl.value;
    canvas.freeDrawingBrush.width = parseInt(drawingLineWidthEl.value, 10) || 1;
    canvas.freeDrawingBrush.shadow = new fabric.Shadow({
      blur: parseInt(drawingShadowWidth.value, 10) || 0,
      offsetX: 0,
      offsetY: 0,
      affectStroke: true,
      color: drawingShadowColorEl.value,
    });
  }
canvas {
  border: 1px solid black;
}
#static {
  position: relative;
  top: -300px;
  left: 330px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.js"></script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static" width="300" height="300"></canvas>
<div style="display: inline-block; margin: 0px 0px 0px 210px">
  <button id="drawing-mode" class="btn btn-info">Cancel drawing mode</button><br>
  <button id="clear-canvas" class="btn btn-info">Clear</button><br>

  <div id="drawing-mode-options">
    <label for="drawing-mode-selector">Mode:</label>
    <select id="drawing-mode-selector">
      <option>Pencil</option>
      <option>Circle</option>
      <option>Spray</option>
      <option>Pattern</option>

      <option>hline</option>
      <option>vline</option>
      <option>square</option>
      <option>diamond</option>
      <option>texture</option>
    </select><br>

    <label for="drawing-line-width">Line width:</label>
    <span class="info">30</span><input type="range" value="30" min="0" max="150" id="drawing-line-width"><br>

    <label for="drawing-color">Line color:</label>
    <input type="color" value="#005E7A" id="drawing-color"><br>

    <label for="drawing-shadow-color">Shadow color:</label>
    <input type="color" value="#005E7A" id="drawing-shadow-color"><br>

    <label for="drawing-shadow-width">Shadow width:</label>
    <span class="info">0</span><input type="range" value="0" min="0" max="50" id="drawing-shadow-width"><br>

    <label for="drawing-shadow-offset">Shadow offset:</label>
    <span class="info">0</span><input type="range" value="0" min="0" max="50" id="drawing-shadow-offset"><br>
  </div>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.