| <!DOCTYPE html> |
| <html> |
| <head> |
| <style> |
| body { |
| background-color: rgb(255, 255, 255); |
| } |
| |
| #container { |
| position: absolute; |
| left: 510px; |
| top: 90px; |
| } |
| |
| #hud { |
| position: absolute; |
| left: 60px; |
| top: 40px; |
| font-size: 30px; |
| } |
| |
| .image-square { |
| position: absolute; |
| left: 0; |
| top: 0; |
| background-color: rgba(0, 0, 0, 0); |
| transition: 1s; |
| } |
| </style> |
| </head> |
| <body> |
| |
| <div id="hud"> |
| <!-- Populated by JavaScript --> |
| </div> |
| <div id="container"></div> |
| <!-- Populated by JavaScript --> |
| </div> |
| <script> |
| var NUMBER_OF_ROWS = 9; |
| var NUMBER_OF_COLUMNS = 9; |
| var TOTAL_GRID_WIDTH = 900; |
| var TOTAL_GRID_HEIGHT = 900; |
| |
| var PIECE_WIDTH = TOTAL_GRID_WIDTH / NUMBER_OF_COLUMNS; |
| var PIECE_HEIGHT = TOTAL_GRID_HEIGHT / NUMBER_OF_ROWS; |
| |
| var NUMBER_OF_PIECES = 70; |
| |
| var IMAGES = [ |
| { |
| 'palette': [ |
| 'rgb(0, 0, 0)', |
| ], |
| 'image': [ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 1, 1, 1, 0, 0, 1, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 1, 0, 1, 0, 0, 1, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| ] |
| }, |
| |
| { |
| 'palette': [ |
| 'rgb(100, 255, 100)', |
| 'rgb(200, 200, 50)', |
| ], |
| 'image': [ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 1, 1, 1, 0, 0, 0, |
| 0, 0, 1, 1, 1, 1, 1, 0, 0, |
| 0, 0, 1, 1, 1, 1, 1, 0, 0, |
| 0, 0, 0, 1, 1, 1, 0, 0, 0, |
| 0, 0, 0, 0, 2, 0, 0, 0, 0, |
| 0, 0, 0, 0, 2, 0, 0, 0, 0, |
| 0, 0, 0, 2, 2, 2, 0, 0, 0, |
| 0, 0, 2, 2, 2, 2, 2, 0, 0, |
| ] |
| }, |
| { |
| 'palette': [ |
| 'rgb(0, 0, 0)', |
| ], |
| 'image': [ |
| 0, 0, 0, 0, 1, 0, 0, 0, 0, |
| 0, 0, 0, 1, 0, 1, 0, 0, 0, |
| 0, 1, 0, 0, 1, 0, 0, 1, 0, |
| 0, 0, 1, 0, 1, 0, 1, 0, 0, |
| 0, 0, 0, 1, 1, 1, 0, 0, 0, |
| 0, 0, 0, 0, 1, 0, 0, 0, 0, |
| 0, 0, 0, 0, 1, 0, 0, 0, 0, |
| 0, 0, 0, 1, 0, 1, 0, 0, 0, |
| 0, 0, 1, 0, 0, 0, 1, 0, 0, |
| ] |
| }, |
| { |
| 'palette': [ |
| 'rgb(50, 50, 50)', |
| 'rgb(100, 100, 255)', |
| 'rgb(200, 200, 50)', |
| ], |
| 'image': [ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 0, 0, 0, 0, 0, 0, 0, 1, |
| 1, 0, 2, 2, 0, 2, 2, 0, 1, |
| 1, 0, 2, 2, 0, 2, 2, 0, 1, |
| 1, 0, 0, 0, 0, 0, 0, 0, 1, |
| 1, 0, 3, 3, 0, 2, 2, 0, 1, |
| 1, 0, 3, 3, 0, 2, 2, 0, 1, |
| 1, 0, 3, 3, 0, 0, 0, 0, 1, |
| 1, 0, 3, 3, 0, 0, 0, 0, 1, |
| ] |
| }, |
| { |
| 'palette': [ |
| 'rgb(255, 0, 0)', |
| ], |
| 'image': [ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 0, 0, 0, 0, 0, 1, 1, |
| 1, 0, 1, 0, 0, 0, 1, 0, 1, |
| 1, 0, 0, 1, 0, 1, 0, 0, 1, |
| 1, 0, 0, 0, 1, 0, 0, 0, 1, |
| 1, 0, 0, 0, 0, 0, 0, 0, 1, |
| 1, 0, 0, 0, 0, 0, 0, 0, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| ] |
| }, |
| { |
| 'palette': [ |
| 'rgb(200, 200, 200)', |
| 'rgb(170, 170, 170)', |
| 'rgb(140, 140, 140)', |
| 'rgb(110, 110, 110)', |
| 'rgb(80, 80, 80)', |
| ], |
| 'image': [ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 0, 0, 0, 0, 0, 0, 0, 0, 1, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 2, 0, 0, 0, 0, 0, 0, 0, 2, |
| 3, 3, 3, 3, 3, 3, 3, 3, 3, |
| 0, 0, 0, 0, 0, 0, 0, 0, 3, |
| 4, 4, 4, 4, 4, 4, 4, 4, 4, |
| 4, 0, 0, 0, 0, 0, 0, 0, 0, |
| 5, 5, 5, 5, 5, 5, 5, 5, 5, |
| ] |
| }, |
| { |
| 'palette': [ |
| 'rgb(0, 0, 0)', |
| 'rgb(255, 255, 0)', |
| ], |
| 'image': [ |
| 0, 0, 0, 1, 1, 1, 0, 0, 0, |
| 0, 0, 1, 2, 2, 2, 1, 0, 0, |
| 0, 1, 2, 2, 2, 2, 2, 1, 0, |
| 1, 2, 2, 1, 2, 1, 2, 2, 1, |
| 1, 2, 2, 2, 1, 2, 2, 2, 1, |
| 1, 2, 1, 2, 2, 2, 1, 2, 1, |
| 0, 1, 2, 1, 1, 1, 2, 1, 0, |
| 0, 0, 1, 2, 2, 2, 1, 0, 0, |
| 0, 0, 0, 1, 1, 1, 0, 0, 0, |
| ] |
| }, |
| ]; |
| |
| var TRANSITION_MODES = [ |
| { |
| 'name': 'Easing', |
| 'styleParameters': function() { |
| return {'transition': '1s'} |
| } |
| }, |
| { |
| 'name': 'Linear', |
| 'styleParameters': function() { |
| return {'transition': '1s linear'} |
| } |
| }, |
| { |
| 'name': 'Stepped', |
| 'styleParameters': function() { |
| return {'transition': '1s steps(6)'} |
| } |
| }, |
| { |
| 'name': 'Springy', |
| 'styleParameters': function() { |
| return {'transition': |
| 'cubic-bezier(0.67, 2.26, 0.47, 0.59) transform 1s, ' + |
| 'background-color 1s'} |
| } |
| }, |
| { |
| 'name': 'Random-delay easing', |
| 'styleParameters': function() { |
| return {'transition': '1s ' + Math.random() * 0.5 + 's'} |
| } |
| }, |
| { |
| 'name': 'Random-duration easing', |
| 'styleParameters': function() { |
| return {'transition': (0.6 + Math.random() * 1.0) + 's'} |
| } |
| }, |
| ] |
| |
| var pieces = []; |
| var transition_mode_index = 0; |
| |
| // This function sets up initial style for each piece. |
| function setPieceInitialStyle(piece) { |
| piece.style.width = PIECE_WIDTH + 'px'; |
| piece.style.height = PIECE_HEIGHT + 'px'; |
| } |
| |
| // This function returns style data for the transform and background-color |
| // of a piece in its resting position, which depends on its piece index. |
| function getRestingStyle(pieceIndex, number_of_pieces) { |
| var kGridCenter = [TOTAL_GRID_WIDTH / 2, TOTAL_GRID_HEIGHT / 2]; |
| var kGridRadius = Math.sqrt(TOTAL_GRID_WIDTH * TOTAL_GRID_WIDTH / 4 + |
| TOTAL_GRID_HEIGHT * TOTAL_GRID_HEIGHT / 4); |
| |
| var pieceTheta = 2 * Math.PI * pieceIndex / number_of_pieces; |
| |
| return {'x': kGridCenter[0] + Math.cos(pieceTheta) * kGridRadius, |
| 'y': kGridCenter[1] + Math.sin(pieceTheta) * kGridRadius, |
| 'rotation': 0, |
| 'scale': 0, |
| 'color': 'rgba(0, 0, 0, 0)', |
| 'transition': '1.0s'}; |
| } |
| |
| // Functions like getRestingStyle() and getStyleForGridPosition() return |
| // dictionaries that are not actually CSS styles. This function converts |
| // them to CSS styles. |
| function setStyleFromStyleDict(piece, pieceIndex, styleDict) { |
| piece.style.transform = |
| 'translateX(' + styleDict.x + 'px) translateY(' + |
| styleDict.y + 'px) rotate(' + styleDict.rotation + |
| 'rad) scale(' + styleDict.scale + ')'; |
| piece.style.transition = styleDict.transition; |
| piece.style.backgroundColor = styleDict.color; |
| } |
| |
| function getStyleForGridPosition(x, y, color) { |
| var targetStyle = {'x': x * PIECE_WIDTH, |
| 'y': y * PIECE_HEIGHT, |
| 'rotation': 2 * Math.PI * Math.floor(Math.random() * 2), |
| 'scale': 1, |
| 'color': color}; |
| var transition_mode = TRANSITION_MODES[transition_mode_index]; |
| transitionStyleParameters = transition_mode.styleParameters(); |
| for (var attributeName in transitionStyleParameters) { |
| targetStyle[attributeName] = transitionStyleParameters[attributeName]; |
| } |
| return targetStyle; |
| } |
| |
| function shuffle(array) { |
| for (var i = 0; i < array.length; ++i) { |
| var swapIndex = i + Math.floor(Math.random() * array.length - i); |
| var temp = array[i]; |
| array[i] = array[swapIndex]; |
| array[swapIndex] = temp; |
| } |
| } |
| |
| function refreshImage(image) { |
| // Prepare an array of piece indices we can use to create our image. |
| var pieceIndices = new Array(pieces.length); |
| for (var i = 0; i < pieces.length; ++i) pieceIndices[i] = i; |
| shuffle(pieceIndices); |
| |
| var currentPiece = 0; |
| |
| // Traverse the grid, setting up pieces as we need them. |
| for (var col = 0; col < NUMBER_OF_COLUMNS; ++col) { |
| for (var row = 0; row < NUMBER_OF_ROWS; ++row) { |
| var gridIndex = row * NUMBER_OF_COLUMNS + col; |
| var gridValue = image.image[gridIndex]; |
| |
| if (gridValue != 0 && currentPiece < pieces.length) { |
| // Setup a piece for this grid location. |
| var pieceIndex = pieceIndices[currentPiece++]; |
| var piece = pieces[pieceIndex]; |
| setStyleFromStyleDict( |
| piece, pieceIndex, |
| getStyleForGridPosition(col, row, image.palette[gridValue - 1])); |
| } |
| } |
| } |
| |
| // Set the remaining pieces to their resting style. |
| for (var i = currentPiece; i < pieces.length; ++i) { |
| var pieceIndex = pieceIndices[i]; |
| setStyleFromStyleDict( |
| pieces[pieceIndex], pieceIndex, |
| getRestingStyle(pieceIndex, pieces.length)); |
| } |
| } |
| |
| function onTransitionModeUpdated() { |
| var transition_mode = TRANSITION_MODES[transition_mode_index]; |
| var hudDiv = document.getElementById('hud'); |
| hudDiv.innerHTML = 'Transition mode: ' + transition_mode.name; |
| } |
| |
| window.addEventListener('load', function () { |
| var containerDiv = document.getElementById('container'); |
| for (var i = 0; i < NUMBER_OF_PIECES; ++i) { |
| var newDiv = document.createElement('div'); |
| newDiv.className = 'image-square'; |
| |
| pieces.push(newDiv); |
| setPieceInitialStyle(pieces[i]); |
| setStyleFromStyleDict(pieces[i], i, getRestingStyle(i, pieces.length)); |
| |
| containerDiv.appendChild(newDiv); |
| } |
| pieces = document.getElementsByClassName('image-square'); |
| for (var i = 0; i < pieces.length; ++i) { |
| setPieceInitialStyle(pieces[i]); |
| setStyleFromStyleDict(pieces[i], i, getRestingStyle(i, pieces.length)); |
| } |
| |
| onTransitionModeUpdated(); |
| }); |
| |
| var image_index = -1; |
| document.addEventListener('keydown', function (e) { |
| if (e.keyCode == 37) { |
| // Left arrow key was pressed. |
| image_index -= 1; |
| if (image_index < 0) { |
| image_index = IMAGES.length - 1; |
| } |
| refreshImage(IMAGES[image_index]); |
| } else if (e.keyCode == 39) { |
| // Right arrow key was pressed. |
| image_index += 1; |
| if (image_index >= IMAGES.length) { |
| image_index = 0; |
| } |
| refreshImage(IMAGES[image_index]); |
| } else if (e.keyCode == 38) { |
| // Up arrow key was pressed. |
| transition_mode_index += 1; |
| if (transition_mode_index >= TRANSITION_MODES.length) { |
| transition_mode_index = 0; |
| } |
| |
| onTransitionModeUpdated(); |
| } else if (e.keyCode == 40) { |
| // Down arrow key was pressed. |
| transition_mode_index -= 1; |
| if (transition_mode_index < 0) { |
| transition_mode_index = TRANSITION_MODES.length - 1; |
| } |
| |
| onTransitionModeUpdated(); |
| } |
| }); |
| </script> |
| |
| </body> |
| </html> |
| |