2017-09-02 93 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>