2017-09-02 68 views
2

目标:2D无限循环元件的阵列

的想法是创建元素网格(对于为例图库),将无限循环在其自身上滚动在两个轴上。 应该没有洞,也没有太多的随机性(避免将相同的元素随机从本身中排除)。而且,无论有多少元素,首先(通过16(4 * 4)个元素的网格无限循环似乎很容易,并不超过17(17 * 1)(我的猜测是任何素数元素的数量是定义一个痛苦,使网格)

所以,我居然发现了一个奇妙的工作为例:。 http://www.benstockley.com/

它实际上是非常接近(可能更好)比我想象现在是使用画布,我试着看着JavaScript,它是一个30000缩小线长的脚本,所以我真的不能读取任何核心逻辑。



数学侧/解决问题:

这是问题,所涉及的数学和心态背后的逻辑和理论。 程序如何处理元素列表,以便我们没有空洞,无限网格,最佳的元素遍布所有的轴。

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



侧之实践/ UI/UX:

所涉及的技术,代码段任何意见。我猜它经典的DOM是不可能的,不管怎样,canvas或2D webgl都是强制性的。但我很想听到这方面的任何建议。

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



Exemples:

我会欢迎有点分享这个问题的一个方面,任何工作为例。

+0

你想象得到真正的元素列表大? – arbuthnott

+0

可能是的。在我的直接用例中,它不会超过40个元素左右。但想想如何处理大量元素(可能会达到一百或更多),可能会非常有趣,因为它可能有很多应用程序。当我们开始平移和缩放时,可能我们甚至不知道孔列表(客户端)。所以既小而有潜力无限(程序?)内容的组织对我来说很有意思。 –

+0

基于benstockley.com我想我管理的是确定一个公式,该公式可以找到哪个元素放在哪个坐标中。看看我是否往下走一排(Y + 1),往左走六次(X-6)我总是找到相同的图像。所以我猜测公式是:'AbsoluteIndex = Xn - (6Yn)'然后'AbsoluteIndex%(模)SumOfElements = ElementIndex' 虽然这很有趣,但我相信它不是理想的,就好像我们有300个元素一样, Y行之间的索引偏移应该高于6.是否应该根据总数或元素或可见元素的总数来确定? –

回答

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) { 
 
\t 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) { 
 
\t var listItem = getListItem(x,y); 
 
    var offsets = getOffset(x,y); 
 
    var element = $('<div></div>').addClass('element').css({ 
 
    \t 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()) { 
 
    \t drawElement(x,y); 
 
     y++; 
 
    } 
 
    y = 0; 
 
    x++; 
 
    } 
 
} 
 

 
drawElements();
.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;}
<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>


你可以看到我已经留下了一些简单的按钮来实现运动,但它们无法运行呢。如果你想按照我在这里完成的工作来继续实现,你可以将元素渲染到超出可见框架的特定范围,然后实现某种动画重新定位。这里的renderElements函数只呈现可见的东西,所以你可以使用类似的东西,而不是在呈现无限元素时被卡住,即使对于“滚动”的距离没有理论限制。

+0

这太神奇了。我昨天想到了这个逻辑,尽管使用Y平移的列表长度的圆角平方根也是一样!我只是在玩PixiJS,我有点挣扎着实现和思考“让我们看看昨天发布的这个stackoverflow”。就在那里。惊人。当然我会需要玩,如果我设法把这个出来会更新这个堆栈。非常感谢(我的+1还没有出现,因为我是一个新手,但它在那里) –

+0

这是一个很棒的问题!实现移动我可以看到两种方法:一种是将一些变量保留为“虚拟”左边和上边的卷轴,并更新要素的绝对位置。另一种可能是在框架和元素之间添加一个中间'div' ...将元素放置在中间,并且框架内的中间位置可以代表用户滚动。 – arbuthnott

+0

我其实都跟着哈哈!这是有用的,因为我不能够移动像素的原点像素,但更新网格“单元格”的单元格 –

1

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

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

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

这是想法,不确定的实现(我吮吸处理数组)。

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

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

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

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

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

 

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

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

 
function drawElement(x,y) { 
 
\t var sourceItem = getSourceItem(x,y); 
 
    var offsets = getOffset(x,y); 
 
    var element = $('<div></div>').addClass('element').css({ 
 
    \t 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) { 
 
    \t drawElement(x,y); 
 
     y++; 
 
    } 
 
    y = 0; 
 
    x++; 
 
    } 
 
} 
 

 
function updateElements() { 
 
\t 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) { 
 
    \t 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>

+0

此版本可以查看集团如何出现和消失。我不知道为什么有这种模式,但它不是很差 https://jsfiddle.net/bnv6mumd/65/ –

+0

我喜欢那个小提琴版本。只创建你需要的元素!我认为你可以适应,而不是太麻烦,听取拖动事件或按钮点击,而不是自动生成的动画。但看起来真不错! – arbuthnott

+0

是的,但我每帧重新生成所有元素。如果删除旧的文件并创建新文件不是更好吗?还不确定 –

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>