【问题标题】:Assign random picked color with javascript使用 javascript 分配随机选择的颜色
【发布时间】:2014-03-17 23:58:47
【问题描述】:

我的页面中有一个网格,我想通过 javascript 填充 200 个元素。填充.grid 元素的实际代码如下:

$(function () {
  var $gd = $(".grid");

  var blocks="";
  for(i=0; i < 200; i++){
    blocks += '<div class="block"></div>';
  }
  $gd.append(blocks);
});

我现在要做的是为每个元素分配从列表中随机选择的颜色。可以说红色,蓝色,黄色,绿色(出乎意料的嗯?)。我希望这些值尽可能随机,并避免同样的颜色被再次选择两次(为了清楚起见,可以使用red-blue-red-green-blue 等等,NOTred-red-green-yellow)。

也许这可以帮助随机化过程,Fisher–Yates Shuffle,但我不知道如何实现上述非两次相邻规则(如果可以应用的话)。

实现此结果的最佳方法是什么?我还想知道是否可以对每个 .block 应用渐变而不是平面十六进制颜色;我想更好的方法是为每个在 CSS 中映射的元素分配一个随机类以用于渐变等等..

如果上述脚本可以在性能方面进行优化,我很感激任何建议!

附加信息:

  • 我正在使用 jQuery
  • 网格由每行 20 个元素组成,共 10 行
  • 颜色应该是 4,但如果有帮助,可以提高到 5-7 添加一些中性灰色调

这是一支可以试验的笔 http://codepen.io/Gruber/pen/lDxBw/

额外功能请求: 如上所述,我想避免重复的相邻颜色。是否也可以“上下”避免这种情况?我想完全避免这种情况即使不是不可能也非常困难,但是如果有人能找到解决方案,那就太棒了! 像这样,“nope”标记元素被阻止,而“yep”对角线标记被允许:

【问题讨论】:

  • 最后,所有答案都为我正在研究的其他东西带来了有趣的解决方案和想法,但确实@phil-h 解决方案是解决这个问题的最巧妙的方法。我发布的笔更新了他的代码以及响应性和渐变的一些增强。

标签: javascript jquery css performance math


【解决方案1】:
$(function () {
  var colors = ["red","blue","green","yellow"];
  var $gd = $(".grid");
  var previousColor;
  var blocks="";
  for(i=0; i < 200; i++){
    var color = "";
    while(color === previousColor) {
        color= colors [Math.floor(Math.random()*colors .length)];
    }
    blocks += '<div class="block" style="color:' + color + '"></div>';
    previousColor = color;
  }
  $gd.append(blocks);
});

【讨论】:

  • 这不是一个完美的解决方案,因为它不会阻止垂直重复颜色,但它可能是一个很好的起点。
  • while 声明确实是一个好点,尽管它会产生奇怪的结果,因为它会在每个彩色元素之后留下一个空白元素。我稍微编辑了您的代码以使用background 而不是color。不用担心垂直颜色,这是一个额外的功能请求,拥有它会很棒,但并不是真正的强制性!我将用小提琴更新我的问题以用于实验
【解决方案2】:

首先,我会使用颜色类:

CSS:

.red { background-color: red; }
.blue { background-color: blue; }
.green { background-color: green; }
.yellow { background-color: yellow; }

然后是javascript:

$(document).ready(function() {
  var colors = ["red","blue","green","yellow"];
  var $gd = $(".grid");
  var previousColor;
  var previousRow;
  var rowSize = 10;
  while(rowSize--) previousRow.push("none");
  var blocks = "";
  for(i=0; i < 200; i++){
    var color = colors [Math.floor(Math.random()*colors .length)];
    while((color == previousColor) || (color == previousRow[i%rowSize])) {
        color = colors [Math.floor(Math.random()*colors .length)];
    }
    blocks += '<div class="block ' + color + '"></div>';
    previousColor = color;
    previousRow[i%rowSize] = color;
  }
  $gd.append(blocks);
});

我从类似于 MikeB 的代码开始,但添加了一个行元素,以便我们知道您当前块上方的内容。

【讨论】:

    【解决方案3】:

    首先要介绍的是过滤索引功能。

    给定一个数组:

    var options = ['red', 'green', 'blue', 'purple', 'yellow']; // never less than 3!
    

    还有一个过滤器:

    function filterFunc(val) { 
       var taken = { 'red': 1,  'blue': 1 };
       return taken[val] ? 0 : 1;
    } 
    

    我们可以从过滤器允许的值 (==1) 中取出第 n 项(这不是一种快速的方法,但直到出现性能限制...):

    // filteredIndex returns nth (0-index) element satisfying filterFunc
    //   returns undefined if insufficient options
    function filteredIndex(options, filterFunc, n) {
       var i=-1, j=0;
       for(;j<options.length && i<n; ++j) {
         i += filterFunc(options[j]);
         if(i==n)
           break;
       }
       return options[j];
    }
    

    所以现在我们可以在过滤列表中选择索引为 2 的值。如果我们没有足够的选择这样做,我们应该得到undefined

    如果您从左上角填充颜色,则可以使用最少 3 种颜色,因为您只受上方和左侧单元格的限制。

    要随机选择,我们需要设置过滤器。我们可以从已知值列表中执行此操作:

    function genFilterFunc(takenValues) {
       var takenLookup = {};
       for(var i=0; i < takenValues.length; ++i) {
          takenLookup[takenValues[i]] = 1;
       }
       var filterFunc = function(val) {
         return takenLookup[val] ? 0 : 1;
       };
       return filterFunc;
    }
    

    然后,我们可以为grid[rows][cols] 中的单元格选择一种随机颜色:

    function randomColourNotUpOrLeft(grid, row, col, options, ignoreColour) {
      var takenlist = [];
      if(row > 0 && grid[row-1][col] != ignoreColour) {
        takenlist.push(grid[row-1][col]);
      }
      if(col > 0 && grid[row][col-1] != ignoreColour) {
        takenlist.push(grid[row][col-1]);
      }
      var filt = genFilterFunc(takenlist);
      var randomIndex = Math.floor(Math.random()*(options.length-takenlist.length));
      var randomColour = filteredIndex(options, filt, randomIndex);
      return randomColour;
    }
    

    这里注意,使用的随机索引取决于过滤掉了多少颜色;如果剩下 4 个,我们可以有 0-3,但如果只剩下 2 个,则必须是 0-1,依此类推。当相邻的单元格颜色相同和/或我们靠近边界时,关于哪个单元格的约束较少选择颜色。最后填一个格子:

    function fillGridSpeckled(grid, options, nullColour) {
      for(var row=0; row<grid.length; ++row) {
        for(var col=0; col<grid[row].length; ++col) {
          grid[row][col] = randomColourNotUpOrLeft(grid,row,col,options,nullColour);
        }
      }
    }
    

    我已将其全部放入 this jsbin,以及一些用于演示代码工作的代码。

    【讨论】:

    • 谢谢你的愿意,结果确实是我想要得到的!不过我有一个问题:你为什么使用canvas 来绘制元素?我知道这确实是最合适的方法,但我更喜欢在我的页面中注入一个旧的普通垃圾 divs 以获得更好的浏览器支持以及为每个人分配一个类以便我可以映射他们的可能性通过 CSS 样式。是否可以通过这种方式更改您的代码?
    • 另外你能简单解释一下for(;filteredIndex 函数中吗?特别是我不明白圆括号后面的分号是什么意思,对不起,我还是个 javascript 新手(“darn n00b”的委婉说法:P)
    • 慢慢掌握你的代码,我知道canvas 是(我猜)创建没有垂直重复的布局所必需的。在这种情况下,您建议用渐变而不是纯色填充正方形的可能性是什么?我有点担心用渐变和笔触绘制那些 200~ 方格的性能。或者这根本不是问题?
    • @Gruber:抱歉,刚刚看到这些 cmets。您不必使用画布;我不是很了解 jquery,所以只需更改绘制到画布的代码,而是在代码中通过 jquery 设置颜色,至少要对其进行测试。
    • @Gruber:for(; 是因为for 结构需要括号内的三个语句:init、test 和 increment。由于最后我需要循环外部的j 变量,因此我将var j=0 语句从循环初始化中取出并将其放在上一行。在 javascript 中不是绝对必要的,因为 var 在函数级范围内声明事物,但我习惯于 C++,其中声明是块级的。
    猜你喜欢
    • 2019-10-29
    • 2014-06-12
    • 2011-07-07
    • 2014-10-07
    • 2012-08-24
    • 2017-03-29
    • 1970-01-01
    • 2015-08-16
    • 2014-01-19
    相关资源
    最近更新 更多