【问题标题】:Magic wand tool through javascript魔术魔杖工具通过JavaScript
【发布时间】:2015-08-22 07:50:38
【问题描述】:

我将开始一个新项目,该项目需要我根据多边形选择裁剪图像。挑战在于让前端用户能够快速选择图像中的一个区域,然后对其进行裁剪。为了使用户的过程更容易,我正在考虑提供一些类似于 Photoshop 中的魔术棒工具的功能。

我找到了这个库 - https://github.com/Tamersoul/magic-wand-js

这很好用,但只让我选择一次区域。

我的问题是,是否可以添加多个选择的功能并从已选择的区域中删除一个选择。

查看这个演示小提琴,看看我指的是什么 - jsfiddle(dot)net/Tamersoul/dr7Dw/

【问题讨论】:

  • 从已经编写好的图像编辑器开始可能会更容易。请参阅 MDN Demo Studio 上名为 MiniPaint 的示例。包括图像裁剪和魔术棒工具。
  • “幸福不是来自于做轻松的工作,而是来自于完成一项要求我们尽力而为的艰巨任务后所产生的满足感。”西奥多·艾萨克·鲁宾

标签: javascript image-processing


【解决方案1】:

github.com Magic-Wand-js

jsbin.com Demo

$(function() {
  var colorThreshold = 15,
      blurRadius = 5,
      simplifyTolerant = 0,
      simplifyCount = 30,
      hatchLength = 4,
      hatchOffset = 0,
      imageInfo = null,
      cacheInd = null,
      cacheInds = [],      
      downPoint = null,
      mask = null,
      masks = [],
      allowDraw = false,
      currentThreshold = colorThreshold;

  $('#upload').on('change', function () {
    var inp = this;
    if (inp.files && inp.files[0]) {
      var reader = new FileReader();
      
      reader.onload = function (e) {
        var img = $('#test');
        img.attr('src', e.target.result);
        
        img.on('load', function() {
          resetCache();
          var canvas = $('#canvas')[0];
          imageInfo = {
            width: img.width(),
            height: img.height(),
            context: canvas.getContext("2d")
          };          
          canvas.width = imageInfo.width;
          canvas.height = imageInfo.height;
          var tempCanvas = document.createElement('canvas'),
              tempCtx = tempCanvas.getContext("2d");
          tempCanvas.width = imageInfo.width;
          tempCanvas.height = imageInfo.height;
          tempCtx.drawImage(img[0], 0, 0);
          imageInfo.data = tempCtx.getImageData(0, 0, imageInfo.width, imageInfo.height).data;
        });
      };
      reader.readAsDataURL(inp.files[0]);
    }
  });

  $('#blur').on('change keyup', function () {
    blurRadius = Number($(this).val()) || 0;
    magic();
  });

  $('#threshold').on('change keyup', function () {
    currentThreshold = Number($(this).val()) || 0;
    magic();
  });

  $('#canvas').on('click', function (e) {
    var p = $(e.target).offset(),
        x = Math.round((e.clientX || e.pageX) - p.left),
        y = Math.round((e.clientY || e.pageY) - p.top);    
    downPoint = { x: x, y: y };    
    magic();
  });

  var magic = function () {
    if (imageInfo) {
      var image = {
        data: imageInfo.data,
        width: imageInfo.width,
        height: imageInfo.height,
        bytes: 4
      };
      mask = MagicWand.floodFill(image, downPoint.x, downPoint.y, currentThreshold);
      mask = MagicWand.gaussBlurOnlyBorder(mask, blurRadius);
      masks.push(mask);
      cacheInds.push(MagicWand.getBorderIndices(mask));
      drawBorder(true);
    }
  };
  
  var drawBorder = function () {
    if (masks.length) {

      var x, y, k, i, j, m,
          w = imageInfo.width,
          h = imageInfo.height,
          ctx = imageInfo.context,
          imgData = ctx.createImageData(w, h),
          res = imgData.data;
      
      ctx.clearRect(0, 0, w, h);
      
      for (m = 0; m < masks.length; m++) {
        
        cacheInd = cacheInds[m];
        
        for (j = 0; j < cacheInd.length; j++) {
          i = cacheInd[j];
          x = i % w; // calc x by index
          y = (i - x) / w; // calc y by index
          k = (y * w + x) * 4; 
          if ((x + y + hatchOffset) % (hatchLength * 2) < hatchLength) { 
            // detect hatch color 
            res[k + 3] = 255; // black, change only alpha
          } else {
            res[k] = 255; // white
            res[k + 1] = 255;
            res[k + 2] = 255;
            res[k + 3] = 255;
          }
        }
      }
      ctx.putImageData(imgData, 0, 0);
    }
  };

  setInterval(function () {
    hatchOffset = (hatchOffset + 1) % (hatchLength * 2);
    drawBorder();
  }, 100);
  
  $('#trace').on('click', function () {
    var ctx = imageInfo.context;
    ctx.clearRect(0, 0, imageInfo.width, imageInfo.height);
    for (var m = 0; m < masks.length; m++) {
      // draw contours
      var i, j, ps, cs = MagicWand.traceContours(masks[m]);
      cs = MagicWand.simplifyContours(cs, simplifyTolerant, simplifyCount);
      //inner
      ctx.beginPath();
      for (i = 0; i < cs.length; i++) {
        if (cs[i].inner) {
          ps = cs[i].points;
          ctx.moveTo(ps[0].x, ps[0].y);
          for (j = 1; j < ps.length; j++) {
            ctx.lineTo(ps[j].x, ps[j].y);
          }
        }
      }
      ctx.strokeStyle = 'red';
      ctx.stroke();
      //outer
      ctx.beginPath();
      for (i = 0; i < cs.length; i++) {
        if (!cs[i].inner) {
          ps = cs[i].points;
          ctx.moveTo(ps[0].x, ps[0].y);
          for (j = 1; j < ps.length; j++) {
            ctx.lineTo(ps[j].x, ps[j].y);
          }
        }
      }
      ctx.strokeStyle = 'blue';
      ctx.stroke(); 
    }
    resetCache();
  });
    
  var resetCache = function () {
    mask = null;
    masks = [];
    cacheInds = [];
  };
  
});
#display * {
  cursor: crosshair;
  position: absolute;
}
<!DOCTYPE html>
<html>

  <head>
    <script src="https://rawgit.com/Tamersoul/magic-wand-js/master/js/magic-wand-min.js"></script>
    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <meta charset="utf-8">
    <title>JS Bin</title>
  </head>

  <body>
    <div id="controls">
      <input id="upload" type="file" accept="image/*"/>
      <p>Blur radius
        <input value="5" id="blur" type="number"/>
      </p>
      <p>Threshold
        <input value="15" id="threshold" type="number"/>
      </p>
      <button id="trace">Trace</button>
    </div>
    <div id="display">      
      <img id="test"/>
      <canvas id="canvas"></canvas>
    </div>  
  </body>

</html>

【讨论】:

  • 这很有帮助。你能告诉我们如果我们选择了多个区域然后想要撤消最后一个选择,那怎么实现?
猜你喜欢
  • 1970-01-01
  • 2017-12-24
  • 2015-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 2016-02-01
  • 1970-01-01
相关资源
最近更新 更多