2D无限循环元素数组

时间:2017-09-02 10:43:59

标签: javascript math canvas logic

目标:

这个想法是创建一个元素网格(例如图像库),它将在两个轴上滚动无限循环。 应该没有洞也没有太多的随机性(避免让相同的元素随机掉落)。而且无论一开始有多少元素(通过16(4 * 4)个元素的网格看起来很容易无限循环,而不是超过17(17 * 1)那么多。(我的猜测是任何素数)根据定义,元素的数量是制作网格的痛苦。

所以我实际上找到了一个很好的工作例子: http://www.benstockley.com/

它实际上非常接近(可能更好),而不是我想象的那样。现在它使用画布,我尝试查看javascript,它是一个30000缩小行长脚本,所以我真的无法读取它背后的任何核心逻辑。

数学方面/解决问题:

这是问题背后的逻辑和理论,涉及的数学和心态。 程序应该如何处理元素列表,这样我们就没有孔,无限网格,所有轴上元素的最佳重现。

我的猜测是,某种程度上必须是程序性的。我不确定是否应该在每个轴上创建网格或循环列表(有点像数独?我不知道);

实用方/ UI / UX:

对所涉及的技术,代码片段的任何建议。我猜它经典的DOM是不合适的,并且某种方式画布或2D webgl将是强制性的。但我很乐意听到这方面的任何建议。

除了所有元素网格处理。在DOM或渲染器中探索2D无限或大型布局所涉及的UI和UX在某种程度上并不经典。欢迎提供最好的技术或建议。

例子:

我欢迎任何有点与此问题有关的工作例子。

3 个答案:

答案 0 :(得分:1)

我已经设置fiddle来安排您的2D网格。

它通过使用水平和垂直"步长"起作用。因此,在网格中向右移动一步可以提高列表中的水平步长。向下移动一步可以提升列表中的垂直步长(并累积)。

我们允许列表中的进展在到达结束时循环回零。

使用水平步长1可能是有意义的(因此网格的一行将保持您的列表顺序)。对于垂直步长,您需要一个不与列表长度共享公约数的整数。虽然不能保证,但我使用列表长度的(圆形)平方根作为可以在很多情况下工作的东西。

我会在这里重现小提琴:



var list = ['red','green','blue','cyan','orange','yellow','pink'];

var hstep = 1;
var vstep = Math.ceil(Math.sqrt(list.length));

function getListItem(x,y) {
	var index = x * hstep + y * vstep;
  return list[index % list.length];
}

var elementSize = 30;
var gutterSize = 10;

function getOffset(x,y) {
  return [10 + (elementSize + gutterSize) * x, 10 + (elementSize + gutterSize) * y];
}

var frame = $('.frame');

function drawElement(x,y) {
	var listItem = getListItem(x,y);
  var offsets = getOffset(x,y);
  var element = $('<div></div>').addClass('element').css({
  	left: offsets[0] + 'px',
    top: offsets[1] + 'px',
    'background-color': listItem
  });
  frame.append(element);
}

function drawElements() {
  var x = 0, y = 0;
  while (10 + (elementSize + gutterSize) * x < frame.width()) {
    while (10 + (elementSize + gutterSize) * y < frame.height()) {
    	drawElement(x,y);
      y++;
    }
    y = 0;
    x++;
  }
}

drawElements();
&#13;
.frame {
  border: 2px solid black;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  overflow: hidden;
}

.frame .element {
  position: absolute;
  width: 30px;
  height: 30px;
}

.buttons {
  position: absolute;
  top: 0px;
  width: 100%;
}

.buttons button {
  position: absolute;
  width: 30px;
  height: 30px;
  padding: 5px;
}

button.up {top: 0px; left: 46%;}
button.down {top: 355px; left: 46%;}
button.left {top: 160px; left: 15px;}
button.right {top: 160px; right: 15px;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="frame">

</div>
<div class="buttons">
  <button class="up">&uarr;</button>
  <button class="down">&darr;</button>
  <button class="left">&larr;</button>
  <button class="right">&rarr;</button>
</div>
&#13;
&#13;
&#13;

你可以看到我留下了一些简单的按钮来实现动作,但它们还没有发挥作用。如果你想按照我在这里完成的工作继续实现,你可以将元素渲染到可见帧之外的某个范围,然后实现某种动画重新定位。这里的renderElements函数只渲染可见的东西,所以你可以使用类似的东西而不会陷入渲染无限元素的过程中,即使你可以在多大程度上没有理论限制&#34;滚动& #34;

答案 1 :(得分:1)

@arbuthnott我编辑了你的代码,通过递减relativeX和relativeY变量来实现探索。我还插入了一个“原点”div(1x1 px,溢出可见)。此DOM元素将表示X和Y原点。我不确定它是否必不可少但是非常方便。

现在我的函数当前删除所有元素并重新插入每个更新的所有元素(现在每500毫秒)。

理想是找到一种方法来比较我需要的元素与已存在的元素。 可能将现有元素存储到数组中,并将数组与“查询”数组进行比较。只看到缺少的元素。

这是理想的,不确定实现(我很擅长处理数组)。

https://jsfiddle.net/bnv6mumd/64/

var sources = ['red','green','blue','cyan','orange','yellow','pink','purple'];

var frame = $('.frame'),
		origin = $('.origin');

var fWidth = 600,
		fHeight = 300,
   	srcTotal = sources.length,
    srcSquare = Math.ceil(Math.sqrt(srcTotal)),
    rX = 0,
    rY = 0;

var gridSize = 30,
		gutterSize = 5,
		elementSize = gridSize - gutterSize;


function getSourceItem(x,y) {
	var index = x + y * srcSquare;
  return sources[Math.abs(index) % srcTotal];
}

function getOffset(x,y) {
  return [gridSize * x,gridSize * y];
}

function drawElement(x,y) {
	var sourceItem = getSourceItem(x,y);
  var offsets = getOffset(x,y);
  var element = $('<div></div>').addClass('element').css({
  	left: offsets[0] + 'px',
    top: offsets[1] + 'px',
    'background-color': sourceItem,
  });
  origin.append(element);
}

function init() {
  var x = 0, y = 0;
  while ( gridSize * x < fWidth) {
    while ( gridSize * y < fHeight) {
    	drawElement(x,y);
      y++;
    }
    y = 0;
    x++;
  }
}

function updateElements() {
	origin.empty();
  var x = -Math.trunc(rX / gridSize) -1, y = - Math.trunc(rY / gridSize) -1;
  while ( gridSize * x + rX < fWidth) {
    while ( gridSize * y + rY < fHeight) {
    	drawElement(x,y);
      y++;
    }
    y = -Math.ceil(rY / gridSize);
    x++;
  }
}

function animate() {
  rX -= 5;
  rY -= 5;
  origin.css({left: rX, top: rY})
  updateElements();
  console.log("relative X : " + rX + " | relative Y : " + rY);
}

setInterval(animate, 500)
init();
.frame {
  border: 2px solid black;
  margin: 40px auto;
  height: 300px;
  width: 600px;
  position: relative;
  overflow: hidden;
}

.origin {
  height: 1px;
  width: 1px;
  position: absolute;
  overflow: visible;
}

.frame .element {
  position: absolute;
  width: 25px;
  height: 25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
<div class="origin" style="top:0;left:0;"></div>
</div>

答案 2 :(得分:0)

这是我的最终片段版本(我现在将开始针对我的案例进行实际实现)。

我认为我以一种体面的方式优化了DOM操作,代码结构等(尽管我很乐意接受建议)。

我现在只更新需要更新的元素(点击框架附近显示溢出)

https://jsfiddle.net/bnv6mumd/81/

var sources = ['red', 'green', 'blue', 'cyan', 'orange', 'yellow', 'pink', 'purple'];

var frame = $('.frame'),
    origin = $('.origin');

var srcTotal = sources.length,
    srcSquare = Math.round(Math.sqrt(srcTotal)),
    fWidth = 200,
    fHeight = 200,
    cellSize = 50,
    gutterSize = 20,
    gridSize = [Math.floor(fWidth / cellSize) + 1, Math.floor(fHeight / cellSize) + 1],
    aX = 0, // Absolute/Applied Coordinates
    aY = 0,
    rX = 0, // Relative/frame Coordinates
    rY = 0;

function getSrcItem(x, y) {
    var index = x + y * srcSquare;
    return sources[Math.abs(index) % srcTotal];
}

function getOffset(x, y) {
    return [cellSize * x, cellSize * y];
}

function getY() {
    return Math.floor(-rY / cellSize);
}

function getX() {
    return Math.floor(-rX / cellSize);
}

function drawElement(x, y) {
    var srcItem = getSrcItem(x, y),
        offsets = getOffset(x, y),
        element = $('<div></div>').addClass('element').css({
            left: offsets[0] + 'px',
            top: offsets[1] + 'px',
            'background-color': srcItem,
        }).attr({
            "X": x,
            "Y": y
        });
    origin.append(element);
}

function drawCol(x, y) {
    var maxY = y + gridSize[1];
    while (y <= maxY + 1) {
        drawElement(x - 1, y - 1);
        y++;
    }
}

function drawLign(x, y) {
    var maxX = x + gridSize[0];
    while (x <= maxX + 1) {
        drawElement(x - 1, y - 1);
        x++;
    }
}

function drawGrid() {
    origin.empty();
    var x = getX(),
        y = getY(),
        maxX = x + gridSize[0],
        maxY = y + gridSize[1];
    while (y <= maxY + 1) {
        drawLign(x, y);
        x = getX();
        y++;
    }
}

function updateX(x, y, diffX, diffY) {
    if (Math.sign(diffX) == -1) {
        drawCol(aX - 1, y);
        $('[x=' + (aX + gridSize[0]) + ']').remove();
        aX--;
    } else if (Math.sign(diffY) == 1) {
        drawCol(aX + gridSize[0] + 2, y);
        $('[x=' + (aX - 1) + ']').remove();
        aX++;
    }
}

function updateY(x, y, diffX, diffY) {
    if (Math.sign(diffY) == -1) {
        drawLign(x, aY - 1);
        $('[y=' + (aY + gridSize[0]) + ']').remove();
        aY--;
    } else if (Math.sign(diffY) == 1) {
        drawLign(x, aY + gridSize[0] + 2);
        $('[y=' + (aY - 1) + ']').remove();
        aY++;
    }
}

function animate() {
    rX += 1;
    rY += 1;
    origin.css({
        left: rX,
        top: rY
    });
    var x = getX(),
        y = getY(),
        diffX = x - aX,
        diffY = y - aY;
    if (diffX) {
        updateX(x, y, diffX, diffY)
    };
    if (diffY) {
        updateY(x, y, diffX, diffY)
    };
    requestAnimationFrame(animate);
}

$('body').click(function() {
$(frame).toggleClass("overflow");
})
drawGrid();
animate();
.frame {
    border: 2px solid black;
    margin: 100px auto;
    height: 200px;
    width: 200px;
    position: relative;
}

.overflow{
    overflow:hidden;
}

.origin {
    height: 1px;
    width: 1px;
    position: absolute;
    overflow: visible;
}

.frame .element {
    position: absolute;
    width: 30px;
    height: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame overflow">
    <div class="origin" style="top:0;left:0;"></div>
</div>