【问题标题】:Transform complex svg shapes into a circle abstraction将复杂的 svg 形状转换为圆形抽象
【发布时间】:2016-01-14 12:34:50
【问题描述】:

SVG 很难看,请查看我的:

JSFIDDLE LINK

HTML:

<svg version="1.1" class="overlap-svg" id="alaska"></svg>
<svg version="1.1" class="overlap-svg" id="grid"></svg>

CSS:

.overlap-svg {
    position: absolute;
    left:0;
    top: 0;
}

问题:

如果我们将这 2 个 svg 重叠在一起,那么 JS 函数会如何仅突出显示其中包含阿拉斯加(红色)部分的 svg 圆圈?

查看下面的描述以获取更多信息


  1. 假设您有一个复杂的形状,例如阿拉斯加的轮廓。

  1. 假设您有另一个圆圈网格的 svg:


我该如何转换:

变成这样:

如果阿拉斯加的任何部分(红色)在圆圈区域内,则圆圈应填充为红色。

请再次查看我上面的 JSFiddle 链接。

【问题讨论】:

    标签: javascript html css svg


    【解决方案1】:

    fiddle

    您可以获取 svg 并将其加载到画布元素中。获取元素,因为它是一个画布元素,所以您可以获得一个像素数组。

    您的圆形抽象可以通过使用适当调整大小的画布的像素构建网格来构建。

    首先是一个助手:网格管理器。

    function GridManager(configIn) {
      var gm_ = {};
    
      gm_.config = {
        'gridWidth': 10,
        'gridHeight': 10,
        'gridCellWidth': 10,
        'gridCellHeight': 10,
        'gridHeight': 100,
        'dataSrc': []
      };
    
      // Load new config over defaults
      for (var property in configIn) {
        gm_.config[property] = configIn[property];
      }
    
      /** 
        * Creates an array using the module's config building a 2d data array 
        * from a flat array. Loops over GridManager.config.dataSrc
        * 
        * Render a checkerboard pattern:
        *   GridManager.config.dataSrc = ["#"," "]
        * 
        * Render you can load a image by passing in its full pixel array, 
        * provided image height and width match GridManager.config.gridHeight
        * and GridManager.config.gridWidth. 
        */
      gm_.createGridSrc = function() {
        var height = this.config.gridHeight;
        var width = this.config.gridWidth;
        var output = [];
    
        for (var i = 0; i < height; i++) {
          output[i] = [];
    
          for (var ii = 0; ii < width; ii++) {
            if (this.config.dataSrc !== undefined) {
              var dataSrc = this.config.dataSrc;
              output[i][ii] = dataSrc[i*width + ii % dataSrc.length];
            }
          }
        }
        return output;
      };
    
      /** 
        * Creates a SVG with a grid of circles based on
        * GridManager.config.dataSrc.
        * 
        * This is where you can customize GridManager output.
        */
      gm_.createSvgGrid = function() {
        var cellWidth = this.config.gridCellWidth;
        var cellHeight = this.config.gridCellHeight;
        var svgWidth = 1000;
        var svgHeight = 1000;
        var radius = 3
        var cellOffset = radius / 2;
    
        //create svg
        var xmlns = 'http://www.w3.org/2000/svg';
        var svgElem = document.createElementNS (xmlns, 'svg');
        svgElem.setAttributeNS (null, 'viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);
        svgElem.setAttributeNS (null, 'width', svgWidth);
        svgElem.setAttributeNS (null, 'height', svgHeight);
        svgElem.style.display = 'block';
    
        //create wrapper path
        var g = document.createElementNS (xmlns, 'g');
        svgElem.appendChild (g);
    
        //create grid
        var data = this.createGridSrc();
        var count = 0;
        for (var i = data.length - 1; i >= 0; i--) {
          for (var ii = data[i].length - 1; ii >= 0; ii--) {
            
            // This svgHeight and svgWidth subtraction here flips the image over
            // perhaps this should be accomplished elsewhere.
            var y = svgHeight - (cellHeight * i) - cellOffset;
            var x = svgWidth - (cellWidth * ii) - cellOffset;
    
            var cell = document.createElementNS (xmlns, 'circle');
            var template = data[i][ii];
            
            // Machine has averaged the amount of fill per pixel
            // from 0 - 255, so you can filter just the red pixels like this
            // over a certain strength.
            if (template[0] > 10 ) {
              cell.setAttributeNS (null, 'fill', '#ff0000');
              // Consider stashing refs to these in this.groups['red'] or something
              // similar
            } else {
              cell.setAttributeNS (null, 'fill', 'none');
            }
    
            cell.setAttributeNS (null, 'stroke', '#000000');
            cell.setAttributeNS (null, 'stroke-miterlimit', '#10');
            cell.setAttributeNS (null, 'cx', x);
            cell.setAttributeNS (null, 'cy', y);
            cell.setAttributeNS (null, 'r', radius);
    
            g.appendChild (cell);
          }
        }
        return svgElem;
      }
      return gm_;
    }
    

    然后在 main.js 中

    var wrapper = document.getElementById('wrapper');
    
    var mySVG = document.getElementById('alaska').outerHTML;
    
    mySVG = mySVG.slice(0, 4) + ' height="100" ' + mySVG.slice(4);
    
    // Create a Data URI based on the #alaska element.
    var mySrc = 'data:image/svg+xml;base64,' + window.btoa(mySVG);
    
    // Create a new image to do our resizing and capture our pixel data from.
    var source = new Image();
    source.onload = function() {
    
      var svgRasterStage = document.createElement('canvas');
      svgRasterStage.width = 1000;
      svgRasterStage.height = 1000;
    
      svgRasterStage.classList.add('hidden');
    
      // You may not need this at all, I didn't test it.
      wrapper.appendChild(svgRasterStage);
    
      // Get drawing context for the Canvas
      var svgRasterStageContext = svgRasterStage.getContext('2d');
    
      // Draw the SVG to the stage.
      svgRasterStageContext.drawImage(source, 0, 0);
      
      // We can now get array of rgba pixels all concatinated together:
      //    [ r, g, b, a, r, g, b, a,  (...)  r, g, b, a, r, g, b, a]
      var rgbaConcat = svgRasterStageContext.getImageData(0, 0, 100, 100).data;
    
      // Which sucks, so here's a way to convert them to pixels that we can 
      // use with GridManager.createSvgGrid.
      var pixels = [];
      var count = 0;
      
      // NOTE: this is a for with a weird step: i=i-4. i-4 is an infinte loop.
      // anything else just jumbles the pixels.
      for (var i = rgbaConcat.length - 1; i >= 0; i=i-4) {
        var r = rgbaConcat[i - 0];
        var g = rgbaConcat[i - 1];
        var b = rgbaConcat[i - 2];
        var a = rgbaConcat[i - 3];
        pixels.push([r, g, b, a]);
      }
    
      // We create our GridManager (finally).
      var gm = new GridManager({
        'gridWidth': 100,
        'gridHeight': 100,
        'dataSrc': pixels
      });
      
      // And let her rip!
      wrapper.appendChild(gm.createSvgGrid());
    }
    

    【讨论】:

      【解决方案2】:

      我尝试快速解决此问题,并进行了一些研究,但仍然尚未完成/完成(您可以在您的 fina 实现中完成)。

      你需要有一个函数来检查一个点是否在路径内。 我在 JS 中找到了 2 个库:RaphaelSnapSVG

      我分叉并编辑了您的 JSFiddle,并快速尝试来解决它。我的第一次尝试是使用SnapSVG's function,但它返回的结果比Raphael's function 要少。

      打开小提琴并检查:https://jsfiddle.net/edmundo096/7sjLb956/4/。 请注意,2 会减慢您的浏览器速度,虽然我用它来查看正确的结果但需要时间才能看到某些内容(移动浏览器可能会挂起)。

      var alaska = $('#alaska');
      var grid = $('#grid');
      var path =  alaska.find('path').first().attr('d');
      
      grid.children().each(function(){
          var circle = $(this);
          var scale = 2;
      
          // SnapSVG version: var isInside = Snap.path.isPointInside(path, 
          var isInside = Raphael.isPointInsidePath(path, 
                              circle.attr('cx') * scale, 
                              circle.attr('cy') * scale);
          if (isInside) {
              circle.attr('fill', 'blue');
          }
      });
      

      (我使用了 jQuery,以及 2 个外部资源:来自 Cloudflare CDN 的 Raphael 和 SnapSvg)

      如下图所示,它生成了一种点图,但仍然需要修正 Path 的映射、位置、比例等。

      拉斐尔第一次快速尝试结果:

      SnapSVG 第一个快速试用结果:

      你可以缓存你的结果;将生成的地图保存在 JSON 地图对象中,然后从这个复杂的路径单独加载它以节省计算时间

      希望对你有帮助。

      【讨论】:

        【解决方案3】:

        您可以获取圆心坐标并使用类似 Raphael 的 isPointInsidePath() 函数来测试它是否在地图路径内。

        http://raphaeljs.com/reference.html#Raphael.isPointInsidePath

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-03
          • 1970-01-01
          • 1970-01-01
          • 2022-11-26
          • 2018-10-06
          相关资源
          最近更新 更多