I've been looking at using three.js for a fun experiment on a site. I would like to use a current experiment (for which I already have the code for) and use it as a background for my site.
Anybody know how to do this?
I saw it done here: http://janjorissen.be/
Three JS API: https://github.com/mrdoob/three.js/wiki/API-Reference
I'm going to add yet another answer. I'd use
canvas {
width: 100vw;
height: 100vh;
display: block;
position: fixed;
top: 0;
left: 0;
z-index: -9999;
}
Here's why:
Many people use canvas { width: 100%; height: 100% } but that arguably doesn't make a lot of sense. You don't want the canvas to be 100% of the body. You want it to 100% of the screen/window. That's what canvas { width: 100vw; height: 100vh; } does. It's 100% of the viewport width and viewport height.
This means you don't need to set the body to height: 100% which also would not make sense, especially if the page is taller than the window/screen
display: block; fixes some issues with scrollbars on certain browsers. Some pages use html, body { overflow: none; } but again that doesn't make sense if your page ends up needing to be taller than the screen/window.
position: fixed; makes the canvas position relative to the top of window so it won't scroll with the page. If you use position: absolute then the canvas will scroll off the top if the page is taller than the screen/window. For example this page.
top: 0; left 0; puts it at the top left. Without that it would default to it's default position which is inside the body's margins. Often this is solved by setting body { margin: 0; } but generally that means you end up needing some other container to add a margin back in otherwise your normal content gets positioned at the edge of the window.
z-index: -9999; is there to try to force it further back than anything else just in case the page itself is using some negative values for z-index
Here's an example as a snippet
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
var canvas = document.querySelector("canvas");
var renderer = new THREE.WebGLRenderer({canvas: canvas});
renderer.setClearColor(0xF0F0F0);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true,
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 1;
function resize() {
var width = canvas.clientWidth;
var height = canvas.clientHeight;
if (width != canvas.width || height != canvas.height) {
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001;
resize();
cube.rotation.x = time;
cube.rotation.y = time * 0.31;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
canvas {
width: 100vw;
height: 100vh;
display: block;
position: fixed;
top: 0;
left: 0;
z-index: -9999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<canvas></canvas>
<div>
some content that is in front of the canvas
</div>
And here's an example outside SO so you can view it easier full size.
iframes work as well
Note that there's the issue that if your canvas animation is interactive the elements in front of the canvas will eat the mouse/touch events. There's no easy solution I know of for that. You can mark everything but that canvas/iframe as pointer-events: none and mark the canvas/iframe as pointer-events: auto but then you run into the issue that no text on your page can be selected and no links can be clicked. You could then say set <a> tags to have pointer-events: auto so links work but I'm sure there will be issues here and there depending on what info is on your page (trying to copy an email address, or a location address, etc...)
One note: most three.js examples are structured different (less flexible) by referencing window.innerWidth and window.innerHeight and putting the canvas inside a div with an id="canvas" for some reason.
Here's a snippet using that structure. There's several more lines of code, redundant calls to renderer.setSize and setting the camera aspect in 2 places (not very D.R.Y.) but as far as this Q&A is concerned the only difference is #canvas instead of canvas as the CSS to size the div instead of the canvas.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
document.getElementById("canvas").appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xF0F0F0);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true,
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 1;
function onResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', onResize);
function render(time) {
time *= 0.001;
cube.rotation.x = time;
cube.rotation.y = time * 0.31;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
#canvas {
width: 100vw;
height: 100vh;
display: block;
position: fixed;
top: 0;
left: 0;
z-index: -9999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<div id="canvas"></div>
<div>
some content that is in front of the canvas
</div>
usually i use iframe for that. Thus you dont have conflict with the base page.
<style>
iframe {
z-index : -9999;
position: absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
margin : 0;
padding : 0;
}
</style>
<iframe src="http://example.com/"></iframe>
an example of it
https://github.com/jeromeetienne/www.jetienne.com/blob/master/index-webgl.html#L128 for the source
http://jetienne.com/index-webgl.html for the living code
This is not an actual background, but a 100% width/height element that is displaying the animation, with the rest of the content "elevated" using z-index or similar above that fake background.
Following the very basic example on threejs.org (here), I only had to change the canvas style section to:
canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -9999;
}
That moved the canvas to the background.
Related
Intro:
I have a sticky header and a body which has linear gradient.
Goal:
I'd like to set the header's background as the same of a specific area, that is to say where it initially sits on the top.
Solution tried:
Using the dev tool color picker to find the first value on the top and the second value where the header ends.
Result:
this works.
In this way the header looks like is integrated and part of the body's linear gradient.
It maintains its background as I scroll down the page.
Issue:
If the page's height changes the body's linear gradient will be recalculated indeed.
So now the header's background need a recalculation as well.
As I am new to programming I would appreciate any suggestion that can help me to solve this dynamically.
Guess it's gonna be Javascript helping out.
Here I found a user with the same issue.
Thanks a lot for your time guys!
Instead of using a CSS background gradient, you can create a canvas with z-index -1 and the same size of your page. In the canvas you can render your gradient. This has the advantage, that you can query the canvas for the color at a specific position, which is not possible with the CSS background gradient. By this you can update the background color of your header, whenever a resize or scroll event occurs.
var canvas = document.getElementById ('background');
var ctx = canvas.getContext ('2d');
var header = document.getElementById ('header');
function scroll()
{
var y = window.scrollY + header.getClientRects()[0].height;
var rgba = ctx.getImageData (0, y, 1, 1).data;
header.style.backgroundColor = 'rgba(' + rgba.join(',') + ')';
}
function draw()
{
var colors = ['red', 'orange', 'yellow', 'green',
'blue', 'indigo', 'violet'];
var gradient = ctx.createLinearGradient (0, 0, 0, canvas.height);
for (var i=0; i < colors.length; i++) {
gradient.addColorStop (i/(colors.length-1), colors[i]);
}
ctx.fillStyle = gradient;
ctx.fillRect (0, 0, canvas.width, canvas.height);
scroll();
}
function resize()
{
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
draw();
}
window.addEventListener('resize', resize, false);
window.addEventListener('scroll', scroll, false);
resize();
body {
height: 100rem;
overflow: scroll;
margin: 0;
}
canvas {
display: block;
height: 100%;
width: 100%;
z-index: -1;
margin: 0;
}
#header {
position: fixed;
top: 0;
left: 50%;
right: 0;
height: 50%;
border-bottom: 1pt solid white;
}
<body>
<canvas id="background"></canvas>
<div id="header">
Header
</div>
<script src="gradient.js"></script>
</body>
I'm quite a noob with Pixi.js so here comes the noob question.
I'm trying to display a square in the middle of the screen. When I resize the window, the square should stay in the middle.
When drawing the square I'm using
const square = new PIXI.Graphics();
square.beginFill(0xff0000, 1);
square.drawRect(app.renderer.width /2, app.renderer.height / 2, 50, 50);
square.endFill();
app.stage.addChild(square);
Then, I understand that the x and y properties of square are relative to the initial drawing position. So square.x = 0 will keep the square in the middle.
But then, how do I reset the center of the window (and so the position of the square) when I resize it?
This can be your solution. This is diametric resize response.
I use something like :
getWidthByPer : function (per) {
return window.innerWidth / 100 * per
}
console.log = function(){};
var app = new PIXI.Application({
autoResize: true,
resolution: 1.77
});
document.querySelector("#MYAPP").appendChild(app.view);
const rect = new PIXI.Graphics()
.beginFill(0x55faaf)
.drawRect(-50, -50, 100, 100);
app.stage.addChild(rect);
window.addEventListener('resize', resize);
function resize() {
const parent = app.view.parentNode;
app.renderer.resize(parent.clientWidth, parent.clientHeight);
rect.position.set(
app.screen.width / 2 ,
app.screen.height / 2
);
}
window.onload = function(){
resize();
};
canvas {
display:block;
}
#MYAPP {
position: absolute;
overflow: hidden;
top:0%;
left:0%;
width: 100%;
height: 100%;
}
<script src="https://pixijs.download/v4.7.0/pixi.min.js"></script>
<div id="MYAPP"></div>
I made adaptive-scale to reuse common resizing needs for multiple projects
I'm trying to add an overlay using canvas. It needs to disable clicks so all elements above the overlay should be unclickable, except the element that I send to openOverlay function.
In addition, there is a button that I want to make it clickable. This button is sent to openOverlay function.
How can I do it?
This is my code:
https://codepen.io/anon/pen/QQqQae
The button needs to be clickable but not the div
I tried something like: ctx.clearRect in order to cut the piece that is found above the button:
function openOverlay(elem) {
var loc = elem.getBoundingClientRect();
var canvas = document.createElement("canvas");
canvas.className = "highlight";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(loc.left - padding, loc.top - padding, loc.width + padding * 2, loc.height + padding * 2);
document.body.appendChild(canvas);
window.overlayCanvas = canvas;
}
Set z-index for "canvas" to -1 :
canvas{
z-index: -1;
}
But, the above method will make all the elements in the canvas clickable.
So, you can set the following style on the button to make it clickable :
button{
position: absolute; //or, relative
left: 0px;
top: 0px;
z-index: 2;
}
All the other elements in the canvas won't be clickable unless they have position absolute (or, relative) and higher z-index.
First, ctx.clearRect doesn't really have any effect on the mouse click event.
In the future, you may be able to use canvas hit regions, but they have limited support for now. See MDN AddHitRegion
But for now, you can put any button that is supposed to be clickable at a higher z-index than the overlay and give it either relative or absolute positioning.
function openOverlay(elem) {
var canvas = document.createElement("canvas");
canvas.className = "highlight";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.zIndex = 100;
var ctx = canvas.getContext("2d");
ctx.fillRect(4, 4, 300, 150);
elem.classList.add("clickable");
document.body.appendChild(canvas);
window.overlayCanvas = canvas;
}
var element= document.getElementById("button1")
openOverlay(element)
body{
background-color: grey;
}
.clickable{
position: relative;
z-index:200;
}
canvas{
z-index: 100;
position: absolute;
display: block;
top: 0;
left: 0;
background-color:blue;
opacity: 0.6;
}
<button id="button1" onclick="window.alert('Clicked button1')">Click Me</button>
<button id="button2" onclick="window.alert('Clicked button 2')">Don't Click Me</button>
i found an example of three.js and I am trying to implement it on a <canvas></canvas> element.
I get a reference to the element but I dont get a visual. I have my canvas element with the Id of "mycanvas".
<canvas id="mycanvas" style="border: 5px solid white" width="600" height="500"></canvas>
an I use an onload function in the body called WebGLStart() which calls the script below.
<script>
function WebGLStart()
{
//Get the canvas and the context
var canvas = document.getElementById('mycanvas');
var canvas_context = canvas.getContext("webgl");
console.log("Canvas: "+canvas.width);
console.log("Canvas: "+canvas.height);
var RENDER_DIST = 1000,
FOV = 75;
var WIDTH = canvas.width,
HEIGHT= canvas.height;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, 0.1, RENDER_DIST);
camera.position.z = 100;
scene.add(camera);
renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH,HEIGHT);
console.log("R info: "+renderer.info);
canvas.appendChild(renderer.domElement);
init();
loopRun();
function init()
{
var geometry = new THREE.SphereGeometry(50);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
}
function loopRun()
{
requestAnimationFrame(loopRun);
renderer.render(scene, camera);
}
}
</script>
Is there any reason why this would not display?
I get outputs on chromes prompt(canvas width and height) but no display. any ideas would be appreciated
Child elements of the canvas element are not visible.
To solve this attach the renderer DOM element to some other DOM element, e.g. document body.
if you change this line:
canvas.appendChild(renderer.domElement);
to this:
document.body.appendChild(renderer.domElement);
You'll get the desired result.
Tade0 was right in the above answer! This is for people who like me had a <canvas></canvas> tags and now have to remove it.
The way I did it was first:
Implement a style tag and call your class something unique:
<style>
canvas_for_three {
width: 600px;
height: 500px;
border: 1px solid white;
position: absolute;
left: 0px;
top: 0px;
z-index: -1;
}
</style>
Next remove your <canvas></canvas> tags and replace them with:
<div id="ctx" class="canvas_for_three">
</div>
From here then on in your onload function use the
var canvas = document.getElementById('ctx');
So when your attaching the render to a DOM element it is now:
canvas.appendChild(renderer.domElement);
Where canvas is your newly created canvas.
This will replace the canvas tags.
Now you should have replaced your canvas tags with divs and the image viewport should be in the same position as where you had your <canvas></canvas> tags
I'm having trouble resizing this canvas that is displaying a video. After resizing, it continually jerks all around into different sizes between the "before" and "after" window sizes.
I tried this posts' idea, and that seemed to calm down Chrome a little, but had no affect on Firefox.
This other post gave me some ideas, but still didn't fix it. It seems like I'm either calling resize multiple times in a loop (which I don't see), or the canvas's context doesn't know how to settle on the final size. Any ideas?
<!DOCTYPE html>
<html>
<head>
<title>overflow</title>
<style>
#c {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#hold {
position: fixed;
}
#v {
position: absolute;
height: auto;
width: 100%;
z-index: 0;
}
#see {
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
}
</style>
</head>
<body>
<canvas id=c></canvas>
<div id=hold>
<video id=v>
</video>
</div>
<canvas id=see></canvas>
<script>
window.onload = start;
function start() {
var v = document.getElementById('v');
var house = document.getElementById('hold');
var base = document.getElementById('c');
var canvas = base.getContext('2d');
var cover = document.getElementById('see');
var canvastwo = cover.getContext('2d');
v.src=("keyed.ogv")
v.load();
v.play();
resize();
function resize() {
var wth = (window.innerWidth * 0.65);
house.width = wth;
house.height = (wth * 9/16);
house.style.marginTop=((window.innerHeight/2) - (house.height/2) + "px");
house.style.marginLeft=((window.innerWidth/2) - (house.width/2) + "px");
cover.width = (wth/2);
cover.height = (house.height/2);
cover.style.marginTop=((window.innerHeight/2) - (cover.height/2) + "px");
cover.style.marginLeft=((window.innerWidth/2) - (cover.width/2) + "px");
var rw = cover.width;
var rh = cover.height;
canvastwo.clearRect(0, 0, rw, rh);
draw(v, canvastwo, rw, rh);
}
window.onresize = resize;
function draw(o,j,w,h) {
if(v.paused || v.ended) return false;
j.drawImage(o,0,0,w,h);
setTimeout(draw,20,o,j,w,h);
}
}
</script>
</body>
</html>
You seem to lock in the old values you use for the setTimeout function the way you are using it here, as the context changes. So when you re-size the loop still uses the old values which no longer corresponds with the new sizes and results in the video toggle between these sizes.
Try to more "globalize" the values so that the loop call is clean when it comes to arguments. This way you are sure the variables contains the correct values for each round.
Also change setTimeout with requestAnimationFrame to make the loop more low-level (efficient) and fluid as this syncs to the monitor's vblank gap. This is particular important with video as you otherwise will get skipped frames as setTimeout is not able to sync with monitor.
Here is the essential code you need to change:
/// put these into you start block to keep them "global"
/// for the functions within it.
var w, h;
Change this part in the resize function:
/// ...
w = cover.width;
h = cover.height;
canvastwo.clearRect(0, 0, w, h);
/// argument free call to draw:
draw();
And finally the loop:
function draw() {
if(v.paused || v.ended) return false;
canvastwo.drawImage(v,0,0,w,h);
requestAnimationFrame(draw);
}
This will remove the jerking video and also make the update in sync to the monitor like the video element itself does.
ONLINE DEMO