Created
July 14, 2009 07:31
-
-
Save ngerakines/146774 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <!-- | |
| Copyright (C) 2009 Charles Ying. All Rights Reserved. | |
| This source code is available under Apache License 2.0. | |
| Performance Notes (courtesy of Apple): | |
| on leopard, if animating transforms with a transform list > 1 function, animation falls back to software | |
| shadows (and animated shadows) plus border animations can cause additional redraws | |
| (not fixed) css selectors are expensive | |
| --> | |
| <title>Snow Stack - WebKit 3D CSS Visual Effects</title> | |
| <meta name="Description" content="Snow Stack is a demo of WebKit CSS 3D visual effects with latest WebKit nightly on Mac OS X Snow Leopard" /> | |
| <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> | |
| <style type="text/css"> | |
| body | |
| { | |
| background-color: black; | |
| color: white; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| .view | |
| { | |
| position: absolute; | |
| display: block; | |
| -webkit-transform-style: preserve-3d; | |
| } | |
| .viewflat | |
| { | |
| position: absolute; | |
| display: block; | |
| -webkit-transform-style: preserve-3d; | |
| } | |
| .fader | |
| { | |
| -webkit-transition-property: opacity; | |
| -webkit-transition-duration: 550ms; | |
| -webkit-transition-timing-function: ease-in-out; | |
| } | |
| .page | |
| { | |
| -webkit-perspective: 600; | |
| width: 100%; | |
| height: 100%; | |
| margin: 0 auto; | |
| text-align: center; | |
| } | |
| div.origin | |
| { | |
| left: 50%; | |
| top: 50%; | |
| } | |
| div#camera | |
| { | |
| -webkit-transition-property: -webkit-transform; | |
| -webkit-transition-duration: 5s; | |
| -webkit-transition-timing-function: cubic-bezier(0.2, 0.6, 0.6, 0.9); | |
| -webkit-transform: translate3d(0, 0, 0); | |
| } | |
| div#dolly | |
| { | |
| -webkit-transition-property: -webkit-transform; | |
| -webkit-transition-duration: 550ms; | |
| -webkit-transition-timing-function: ease-out; | |
| -webkit-transform: translate3d(0, 0, 0); | |
| } | |
| div.cell.reflection img | |
| { | |
| -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, color-stop(0.25, transparent), color-stop(1.0, rgba(255, 255, 255, 0.5))); | |
| } | |
| div.cell | |
| { | |
| -webkit-transition-property: -webkit-transform opacity; | |
| -webkit-transition-duration: 550ms; | |
| -webkit-transform: translate3d(0, 0, 0); | |
| } | |
| div.cell img | |
| { | |
| display: block; | |
| position: absolute; | |
| -webkit-transition-property: -webkit-transform /* PERF -webkit-box-shadow */ /* border-color */; | |
| -webkit-transition-duration: 550ms; | |
| -webkit-transform: translate3d(0, 0, 0); | |
| } | |
| div.original img | |
| { | |
| border: 1px solid transparent; | |
| } | |
| div.original.selected img | |
| { | |
| /* PERF | |
| -webkit-box-shadow: 0px 0px 35px #000; | |
| */ | |
| border-color: #000; | |
| } | |
| .mover | |
| { | |
| -webkit-transition-property: -webkit-transform; | |
| -webkit-transition-duration: 550ms; | |
| } | |
| div.original.selected .mover | |
| { | |
| -webkit-transform: translate3d(0, 0, 40px); | |
| } | |
| div.original.selected.magnify .mover | |
| { | |
| -webkit-transform: translate3d(0, 0, 100px); | |
| } | |
| div.original.selected.magnify img | |
| { | |
| -webkit-transform: scale(2.0); | |
| } | |
| div.reflection.selected .mover | |
| { | |
| -webkit-transform: translate3d(0, 0, 39px); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="page view"> | |
| <div class="origin view"> | |
| <div id="camera" class="view"> | |
| <div id="dolly" class="view"> | |
| <div id="stack" class="view"> | |
| </div> | |
| <div id="mirror" class="view"> | |
| <div id="rstack" class="view"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| var CWIDTH; | |
| var CHEIGHT; | |
| var CGAP = 10; | |
| var CXSPACING; | |
| var CYSPACING; | |
| function translate3d(x, y, z) | |
| { | |
| return "translate3d(" + x + "px, " + y + "px, " + z + "px)"; | |
| } | |
| function cameraTransformForCell(n) | |
| { | |
| var x = Math.floor(n / 3); | |
| var y = n - x * 3; | |
| var cx = (x + 0.5) * CXSPACING; | |
| var cy = (y + 0.5) * CYSPACING; | |
| if (magnifyMode) | |
| { | |
| return translate3d(-cx, -cy, 180); | |
| } | |
| else | |
| { | |
| return translate3d(-cx, -cy, 0); | |
| } | |
| } | |
| var currentCell = -1; | |
| var cells = []; | |
| var currentTimer = null; | |
| var dolly = jQuery("#dolly")[0]; | |
| var camera = jQuery("#camera")[0]; | |
| var magnifyMode = false; | |
| var zoomTimer = null; | |
| function refreshImage(elem, cell) | |
| { | |
| if (cell.iszoomed) | |
| { | |
| return; | |
| } | |
| if (zoomTimer) | |
| { | |
| clearTimeout(zoomTimer); | |
| } | |
| var zoomImage = jQuery('<img class="zoom"></img>'); | |
| zoomTimer = setTimeout(function () | |
| { | |
| zoomImage.load(function () | |
| { | |
| layoutImageInCell(zoomImage[0], cell.div[0]); | |
| jQuery(elem).replaceWith(zoomImage); | |
| cell.iszoomed = true; | |
| }); | |
| zoomImage.attr("src", cell.info.zoom); | |
| zoomTimer = null; | |
| }, 2000); | |
| } | |
| function layoutImageInCell(image, cell) | |
| { | |
| var iwidth = image.width; | |
| var iheight = image.height; | |
| var cwidth = jQuery(cell).width(); | |
| var cheight = jQuery(cell).height(); | |
| var ratio = Math.min(cheight / iheight, cwidth / iwidth); | |
| iwidth *= ratio; | |
| iheight *= ratio; | |
| image.style.width = Math.round(iwidth) + "px"; | |
| image.style.height = Math.round(iheight) + "px"; | |
| image.style.left = Math.round((cwidth - iwidth) / 2) + "px"; | |
| image.style.top = Math.round((cheight - iheight) / 2) + "px"; | |
| } | |
| function updateStack(newIndex, newmagnifymode) | |
| { | |
| if (currentCell == newIndex && magnifyMode == newmagnifymode) | |
| { | |
| return; | |
| } | |
| var oldIndex = currentCell; | |
| newIndex = Math.min(Math.max(newIndex, 0), cells.length - 1); | |
| currentCell = newIndex; | |
| if (oldIndex != -1) | |
| { | |
| var oldCell = cells[oldIndex]; | |
| oldCell.div.attr("class", "cell fader view original"); | |
| if (oldCell.reflection) | |
| { | |
| oldCell.reflection.attr("class", "cell fader view reflection"); | |
| } | |
| } | |
| var cell = cells[newIndex]; | |
| cell.div.addClass("selected"); | |
| if (cell.reflection) | |
| { | |
| cell.reflection.addClass("selected"); | |
| } | |
| magnifyMode = newmagnifymode; | |
| if (magnifyMode) | |
| { | |
| cell.div.addClass("magnify"); | |
| refreshImage(cell.div.find("img")[0], cell); | |
| } | |
| dolly.style.webkitTransform = cameraTransformForCell(newIndex); | |
| var currentMatrix = new WebKitCSSMatrix(document.defaultView.getComputedStyle(dolly, null).webkitTransform); | |
| var targetMatrix = new WebKitCSSMatrix(dolly.style.webkitTransform); | |
| var dx = currentMatrix.e - targetMatrix.e; | |
| var angle = Math.min(Math.max(dx / (CXSPACING * 3.0), -1), 1) * 45; | |
| camera.style.webkitTransform = "rotateY(" + angle + "deg)"; | |
| camera.style.webkitTransitionDuration = "330ms"; | |
| if (currentTimer) | |
| { | |
| clearTimeout(currentTimer); | |
| } | |
| currentTimer = setTimeout(function () | |
| { | |
| camera.style.webkitTransform = "rotateY(0)"; | |
| camera.style.webkitTransitionDuration = "5s"; | |
| }, 330); | |
| } | |
| function snowstack_addimage(reln, info) | |
| { | |
| var cell = {}; | |
| var realn = cells.length; | |
| cells.push(cell); | |
| var x = Math.floor(realn / 3); | |
| var y = realn - x * 3; | |
| cell.info = info; | |
| cell.div = jQuery('<div class="cell fader view original" style="opacity: 0"></div>').width(CWIDTH).height(CHEIGHT); | |
| cell.div[0].style.webkitTransform = translate3d(x * CXSPACING, y * CYSPACING, 0); | |
| var img = document.createElement("img"); | |
| jQuery(img).load(function () | |
| { | |
| layoutImageInCell(img, cell.div[0]); | |
| cell.div.append(jQuery('<a class="mover viewflat" href="' + cell.info.link + '" target="_blank"></a>').append(img)); | |
| cell.div.css("opacity", 1); | |
| }); | |
| img.src = info.thumb; | |
| jQuery("#stack").append(cell.div); | |
| if (y == 2) | |
| { | |
| cell.reflection = jQuery('<div class="cell fader view reflection" style="opacity: 0"></div>').width(CWIDTH).height(CHEIGHT); | |
| cell.reflection[0].style.webkitTransform = translate3d(x * CXSPACING, y * CYSPACING, 0); | |
| var rimg = document.createElement("img"); | |
| jQuery(rimg).load(function () | |
| { | |
| layoutImageInCell(rimg, cell.reflection[0]); | |
| cell.reflection.append(jQuery('<div class="mover viewflat"></div>').append(rimg)); | |
| cell.reflection.css("opacity", 1); | |
| }); | |
| rimg.src = info.thumb; | |
| jQuery("#rstack").append(cell.reflection); | |
| } | |
| } | |
| function snowstack_init() | |
| { | |
| CHEIGHT = Math.round(window.innerHeight / 5); | |
| CWIDTH = Math.round(CHEIGHT * 300 / 180); | |
| CXSPACING = CWIDTH + CGAP; | |
| CYSPACING = CHEIGHT + CGAP; | |
| jQuery("#mirror")[0].style.webkitTransform = "scaleY(-1.0) " + translate3d(0, - CYSPACING * 6 - 1, 0); | |
| } | |
| jQuery(window).load(function () | |
| { | |
| var page = 1; | |
| var loading = true; | |
| snowstack_init(); | |
| flickr(function (images) | |
| { | |
| jQuery.each(images, snowstack_addimage); | |
| updateStack(1); | |
| loading = false; | |
| }, page); | |
| var keys = { left: false, right: false, up: false, down: false }; | |
| var keymap = { 37: "left", 38: "up", 39: "right", 40: "down" }; | |
| var keytimer = null; | |
| function updatekeys() | |
| { | |
| var newcell = currentCell; | |
| if (keys.left) | |
| { | |
| /* Left Arrow */ | |
| if (newcell >= 3) | |
| { | |
| newcell -= 3; | |
| } | |
| } | |
| if (keys.right) | |
| { | |
| /* Right Arrow */ | |
| if ((newcell + 3) < cells.length) | |
| { | |
| newcell += 3; | |
| } | |
| else if (!loading) | |
| { | |
| // /* We hit the right wall, add some more */ | |
| // page = page + 1; | |
| // loading = true; | |
| // flickr(function (images) | |
| // { | |
| // jQuery.each(images, snowstack_addimage); | |
| // loading = false; | |
| // }, page); | |
| } | |
| } | |
| if (keys.up) | |
| { | |
| /* Up Arrow */ | |
| newcell -= 1; | |
| } | |
| if (keys.down) | |
| { | |
| /* Down Arrow */ | |
| newcell += 1; | |
| } | |
| updateStack(newcell, magnifyMode); | |
| } | |
| var delay = 330; | |
| function keycheck() | |
| { | |
| if (keys.left || keys.right || keys.up || keys.down) | |
| { | |
| if (keytimer === null) | |
| { | |
| delay = 330; | |
| var doTimer = function () | |
| { | |
| updatekeys(); | |
| keytimer = setTimeout(doTimer, delay); | |
| delay = 60; | |
| }; | |
| doTimer(); | |
| } | |
| } | |
| else | |
| { | |
| clearTimeout(keytimer); | |
| keytimer = null; | |
| } | |
| } | |
| /* Limited keyboard support for now */ | |
| window.addEventListener('keydown', function (e) | |
| { | |
| if (e.keyCode == 32) | |
| { | |
| /* Magnify toggle with spacebar */ | |
| updateStack(currentCell, !magnifyMode); | |
| } | |
| else | |
| { | |
| keys[keymap[e.keyCode]] = true; | |
| } | |
| keycheck(); | |
| }); | |
| window.addEventListener('keyup', function (e) | |
| { | |
| keys[keymap[e.keyCode]] = false; | |
| keycheck(); | |
| }); | |
| }); | |
| function flickr(callback, page) | |
| { | |
| var data = []; | |
| // SELECT application_id, count(*) FROM sessions GROUP BY application_id; | |
| data[0] = { cht: 'p', chs: '280x200', chtt: 'Website vs Client Sessions', chco: '4D89D9,C6D9FD', chd: 't:60,40', chl: 'Website|Client'} | |
| // SELECT identity_provider_id, count(*) as cnt FROM identities GROUP BY identity_provider_id; | |
| data[1] = { chtt: 'Identities by Provider', cht: 'p', chs: '280x200', chd: 't:40,30,20,10', chl: 'WoW|XBL|PSN|EA', chco: '4D89D9,C6D9FD' } | |
| // Special process | |
| data[2] = { chtt: 'Queue Size (30 minutes)', cht: 'ls', chs: '280x200', chd: 't:15,21,13,10,403,201,41,13,20,20,25,27,15,21,13,10,403,201,41,13,20,20,25,27,15,21,13,10,403,201', chco: '000000' } | |
| // Special process | |
| data[3] = { chtt: 'Events per minute (30 minutes)', cht: 'ls', chs: '280x200', chd: 't:700,19,30,41,1,2,14,30,25,19,30,41,1,2,14,30,25,19,30,41,1,2,14,30,25,19,30,41,1,2', chco: '000000' } | |
| // Special process | |
| data[4] = { chtt: 'Cached Feeds', cht: 'bhs', chs: '200x200', chd: 't:10,50,60,80,40|50,60,100,40,20', chl: 'Hits|Total', chco: '4D89D9,C6D9FD', chbh: 'a' } | |
| // // Special process | |
| // data[5] = { name: 'API Resource Breakdown', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[6] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[7] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[8] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[9] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[10] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[11] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| // data[12] = { name: 'Users', type: 'lc', size: '200x200', data: 't:40,43,49,51,103,141,156,178,201,301' } | |
| var images = jQuery.map(data, function (item) { | |
| var url = 'http://chart.apis.google.com/chart?' | |
| for (var key in item ) { | |
| url += '&' + key + '=' + item[key] | |
| } | |
| // var url = 'http://chart.apis.google.com/chart?cht=' + item.type + | |
| // '&chd=' + item.data + '&chs=' + item.size + '&chtt=' + | |
| // item.name + '&chco=' + item.colors + '&chl=' + item.labels | |
| return { | |
| thumb: url, | |
| zoom: url, | |
| link: url | |
| }; | |
| }); | |
| callback(images); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment