魔术棒工具通过JavaScript

时间:2015-06-07 22:27:46

标签: javascript image-processing

我将开始研究一个新项目,该项目要求我根据多边形选择裁剪图像。挑战是允许前端用户能够快速选择图像中的区域然后裁剪它。为了让用户更轻松地完成这个过程,我想提供一些类似于photoshop中魔术棒工具的功能。

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

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

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

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

1 个答案:

答案 0 :(得分:4)

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 = [];
  };
  
});
&#13;
#display * {
  cursor: crosshair;
  position: absolute;
}
&#13;
<!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>
&#13;
&#13;
&#13;