<!-- Licensed under a BSD license. See license.html for license --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>Three.js - Canvas Textured Cube</title> <style> html, body { height: 100%; margin: 0; } #c { width: 100%; height: 100%; display: block; } </style> </head> <body> <canvas id="c"></canvas> </body> <!-- Import maps polyfill --> <!-- Remove this when import maps will be widely supported --> <script async src="https://unpkg.com/es-module-shims@1.8.0/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "../../build/three.module.js" } } </script> <script type="module"> import * as THREE from 'three'; function main() { const canvas = document.querySelector( '#c' ); const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } ); const fov = 75; const aspect = 2; // the canvas default const near = 0.1; const far = 5; const camera = new THREE.PerspectiveCamera( fov, aspect, near, far ); camera.position.z = 2; const scene = new THREE.Scene(); const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth ); const cubes = []; // just an array we can use to rotate the cubes const ctx = document.createElement( 'canvas' ).getContext( '2d' ); ctx.canvas.width = 256; ctx.canvas.height = 256; ctx.fillStyle = '#FFF'; ctx.fillRect( 0, 0, ctx.canvas.width, ctx.canvas.height ); const texture = new THREE.CanvasTexture( ctx.canvas ); const material = new THREE.MeshBasicMaterial( { map: texture, } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); cubes.push( cube ); // add to our list of cubes to rotate function resizeRendererToDisplaySize( renderer ) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; const needResize = canvas.width !== width || canvas.height !== height; if ( needResize ) { renderer.setSize( width, height, false ); } return needResize; } function rand( min, max ) { if ( max === undefined ) { max = min; min = 0; } return Math.random() * ( max - min ) + min; } function randVelocity() { return rand( 2, 4 ) * ( rand( 2 ) < 1 ? - 1 : 1 ); } const maxLines = 60; const points = [ { position: [ rand( 256 ), rand( 256 ) ], direction: [ randVelocity(), randVelocity() ] }, { position: [ rand( 256 ), rand( 256 ) ], direction: [ randVelocity(), randVelocity() ] }, ]; const lineHistory = []; let lineCursor = 0; function drawCurrentLine() { const line = lineHistory[ lineCursor ]; ctx.beginPath(); ctx.moveTo( ...line[ 0 ] ); ctx.lineTo( ...line[ 1 ] ); ctx.stroke(); } function drawLines( time ) { points.forEach( ( point ) => { point.position.forEach( ( position, ndx ) => { const newPosition = position + point.direction[ ndx ]; if ( newPosition > 255 ) { point.direction[ ndx ] = rand( - 2, - 4 ); } else if ( newPosition < 0 ) { point.direction[ ndx ] = rand( 2, 4 ); } point.position[ ndx ] = newPosition; } ); } ); if ( lineHistory.length === maxLines ) { ctx.lineWidth = 3; ctx.strokeStyle = '#FFF'; drawCurrentLine(); } lineHistory[ lineCursor ] = points.map( point => point.position.slice() ); ctx.lineWidth = 1; ctx.strokeStyle = hsl( time, 1, .5 ); drawCurrentLine(); lineCursor = ( lineCursor + 1 ) % maxLines; } function hsl( h, s, l ) { return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`; } function render( time ) { time *= 0.001; if ( resizeRendererToDisplaySize( renderer ) ) { const canvas = renderer.domElement; camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix(); } drawLines( time * 0.1 ); texture.needsUpdate = true; cubes.forEach( ( cube, ndx ) => { const speed = .2 + ndx * .1; const rot = time * speed; cube.rotation.x = rot; cube.rotation.y = rot; } ); renderer.render( scene, camera ); requestAnimationFrame( render ); } requestAnimationFrame( render ); } main(); </script> </html>