JSFiddle - React, Tailwind, and code Playground
HTML
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Amita:wght@400;700&family=Danfo&family=Gloria+Hallelujah&family=Jacquard+12&family=Micro+5&family=Monoton&family=Orbitron:wght@400..900&family=Rock+Salt&family=Rubik+Moonrocks&family=Shadows+Into+Light&family=Silkscreen:wght@400;700&family=Yarndings+20&display=swap"
rel="stylesheet"
/>
<div>
Type Something!
</div>
CSS
body {
margin: 0;
overflow: hidden;
}
div {
position: absolute;
left: 0;
bottom: 1em;
text-align: center;
width: 100%;
color: #fff;
font: 500 2em sans-serif;
}
JavaScript
//@ts-check
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// init
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xff6f61);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animation);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
// Logic -------
const spritesPool = [];
const animations = new Map();
window.addEventListener("keyup", (e) => {
const sprite =
spritesPool.pop() ??
new THREE.Sprite(
new THREE.SpriteMaterial({
map: new THREE.CanvasTexture(createCanvas(500)),
})
);
scene.add(sprite);
const canvasTexture = sprite.material.map;
drawLetter(canvasTexture, e.key);
initAnimatedSprite(sprite);
});
const SPEED = 5;
const GRAVITY_FORCE = new THREE.Vector3(0, -9.8, 0);
const velocity = new THREE.Vector3(0, 0, 0);
const origin = new THREE.Vector3(0, 0, 0);
function initAnimatedSprite(sprite) {
sprite.visible = true;
sprite.scale.setScalar(0.3);
const distance = camera.position.distanceTo(origin) * 1.1;
sprite.position.set(
THREE.MathUtils.randFloat(-0.5, 0.5) * distance,
THREE.MathUtils.randFloat(-0.5, 0.5) * distance,
THREE.MathUtils.randFloat(-0.5, 0.5) * distance
);
animations.set(sprite, spriteAnimationCallback);
function spriteAnimationCallback(delta) {
sprite.position.add(
velocity.copy(GRAVITY_FORCE).multiplyScalar(delta * delta * SPEED)
);
if (sprite.position.y < -distance * 0.5) {
...