I had a chance recently to tackle a project that required several rotating cubes that showed updated text. Initially, I was excited to work with some simple 3D in HTML5 using div elements, but that excitement was lost when I struggled to reconcile my years of 3D development with this type of web development. I’m used to a “world” with a “camera” type of situation, and so I set out to try and control the HTML elements in a similar fashion.
The requirements were that the “cubes” had to have their own camera view, so to speak. They couldn’t all use the same one since this would cause a perspective issue when you move them to the outer edges and areas of the view.
/** * Created by NeoRiley on 5/2/16. */ function run(){ createBillboard({x:20, y:20, width: 350, height: 350}); createBillboard({x:400, y:20, width: 350, height: 350}); createBillboard({x:200, y:350, width: 350, height: 350}); } function createBillboard(p_bounds){ var world = create3DWorld(p_bounds); var billboard = document.createElement("div"); billboard.id = "billboard"; billboard.style.webkitTransformStyle = "preserve-3d"; billboard.style.width = p_bounds.width + "px"; billboard.style.height = p_bounds.height + "px"; billboard.style.position = "absolute"; billboard.style.top = "0px"; billboard.style.left = "0px"; billboard.style.overflow = "visible"; billboard.className = "rotating"; var str = "l0l"; var panel_front = $createPanel(str, p_bounds); panel_front.style.color = "#FFFFFF"; panel_front.style.background = "rgba(0, 255, 0, 0.5"; var panel_back = $createPanel(str, p_bounds); panel_back.style.color = "#FFFFFF"; panel_back.style.background = "rgba(0, 0, 255, 0.5"; var panel_left = $createPanel(str, p_bounds); panel_left.style.color = "#FFFFFF"; panel_left.style.background = "rgba(255, 0, 0, 0.5"; var panel_right = $createPanel(str, p_bounds); panel_right.style.color = "#FFFFFF"; panel_right.style.background = "rgba(255, 156, 0, 0.5"; world.appendChild(billboard); billboard.appendChild(panel_front); billboard.appendChild(panel_back); billboard.appendChild(panel_left); billboard.appendChild(panel_right); panel_front.style.webkitTransform = "translateZ(10px)"; panel_back.style.webkitTransform = "rotateY(180deg) translateZ(10px)"; panel_left.style.webkitTransform = "rotateY(90deg) translateZ(10px)"; panel_right.style.webkitTransform = "rotateY(270deg) translateZ(10px)"; } function create3DWorld(p_bounds){ var camera = document.createElement("div"); camera.id = "camera"; camera.style.webkitPerspective = "350px"; camera.style.webkitTransformStyle = "flat"; camera.style.width = p_bounds.width + "px"; camera.style.height = p_bounds.height + "px"; camera.style.position = "absolute"; camera.style.top = p_bounds.y + "px"; camera.style.left = p_bounds.x + "px"; camera.style.overflow = "hidden"; var world = document.createElement("div"); world.id = "world"; world.style.webkitTransformStyle = "preserve-3d"; world.style.width = p_bounds.width + "px"; world.style.height = p_bounds.height + "px"; world.style.position = "absolute"; world.style.top = "0px"; world.style.left = "0px"; world.style.overflow = "visible"; document.body.appendChild(camera); camera.appendChild(world); return world; } ////////////////////////////////////////////////////////////////////////////////// function $createPanel(p_id, p_bounds){ var panel = document.createElement("div"); panel.id = "panel_" + p_id; panel.style.width = p_bounds.width + "px"; panel.style.height = p_bounds.height + "px"; panel.style.position = "absolute"; panel.style.top = "0px"; panel.style.left = "0px"; //panel.style.webkitBackfaceVisibility = "hidden"; panel.style.overflow = "hidden"; var text = document.createElement('span'); text.textContent = p_id; text.style.fontSize = "200px"; var bounds = getBounds(p_id, {fontSize: "200px"}); text.style.position = "absolute"; text.style.top = ((p_bounds.width * 0.5) - (bounds.height * 0.5)) + "px"; text.style.left = ((p_bounds.width * 0.5) - (bounds.width * 0.5)) + "px"; panel.appendChild(text); return panel; } ////////////////////////////////////////////////////////////////////////////////// function getBounds(p_text, p_options, p_debug) { var element = document.createElement('div'), bounds = {width: 0, height: 0}; element.appendChild(document.createTextNode(p_text)); p_options.fontFamily = p_options.fontFamily || "arial"; p_options.fontSize = p_options.fontSize || "12px"; p_options.fontWeight = p_options.fontWeight || "normal"; element.style.fontFamily = p_options.fontFamily; element.style.fontWeight = p_options.fontWeight; element.style.fontSize = p_options.fontSize; element.style.position = 'absolute'; element.style.visibility = p_debug ? 'visible' : 'hidden'; element.style.left = p_debug ? "0px" : "-1000px"; element.style.top = p_debug ? "0px" : "-1000px"; element.style.width = 'auto'; element.style.height = 'auto'; if( p_debug ){ element.style.background = "#FFFF00"; element.style.color = "#000000"; } document.body.appendChild(element); bounds.width = element.offsetWidth; bounds.height = element.offsetHeight; if( !p_debug ) document.body.removeChild(element); return bounds; } run();