Files
preact-text3D/src/text.tsx
2022-03-22 18:42:56 -05:00

169 lines
4.3 KiB
TypeScript

import * as THREE from 'three';
import { Font as FontLoader } from 'three/examples/jsm/loaders/FontLoader';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
import { ComponentProps } from 'preact';
import helvetiker_regular from './fonts/helvetiker_regular.typeface.json'; //taken from threejs's website
function getTextGeometry(text: string): TextGeometry {
//get font
const Font = new FontLoader(helvetiker_regular);
const geometry = new TextGeometry(text, {
font: Font,
size: 20,
height: 1,
curveSegments: 4,
bevelEnabled: true,
bevelThickness: 2,
bevelSize: 1,
bevelOffset: 0,
bevelSegments: 2
});
geometry.center();
return geometry;
}
interface runThreeArgs {
canvas: HTMLCanvasElement;
text: string;
zoom?: number;
scale?: number;
width?: number;
height?: number;
}
function runThree({ canvas, text, zoom = 1, scale = 1, width, height }: runThreeArgs) {
if (!width && !height) {
return;
}
//create scene
const scene = new THREE.Scene();
//create text
const geometry = getTextGeometry(text);
//create mesh from text
const material = new THREE.MeshNormalMaterial();
// const material = new THREE.ShaderMaterial({
// uniforms: {
// 'tDiffuse': {
// value: null
// },
// 'resolution': {
// value: null
// },
// 'pixelSize': {
// value: 100
// }
// },
// vertexShader:
// /* glsl */
// `
// varying highp vec2 vUv;
// void main() {
// vUv = uv;
// gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
// }`,
// fragmentShader:
// /* glsl */
// `
// uniform sampler2D tDiffuse;
// uniform float pixelSize;
// uniform vec2 resolution;
// varying highp vec2 vUv;
// void main(){
// vec2 dxy = pixelSize / resolution;
// vec2 coord = dxy * floor( vUv / dxy );
// gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
// }`
// });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
//set canvas size
{
//get size of text
geometry.computeBoundingBox();
let textW = Math.abs(geometry.boundingBox!.min.x) + Math.abs(geometry.boundingBox!.max.x),
textH = Math.abs(geometry.boundingBox!.min.y) + Math.abs(geometry.boundingBox!.max.y),
aspect = textW / textH;
//calculate width and height
if (width && !height) {
height = width / aspect;
} else if (!width && height) {
width = height * aspect;
}
console.log(width! * scale, height! * scale)
canvas.width = width! * scale;
canvas.height = height! * scale;
canvas.style.width = `${width!}px`;
canvas.style.height = `${height!}px`;
}
//create camera
const Bounds = canvas.getBoundingClientRect();
const camera = new THREE.PerspectiveCamera(70, Bounds.width / Bounds.height, 0.01, 1000);
camera.position.set(0, 0, 100 / zoom);
//set render settings
const renderer = new THREE.WebGLRenderer({
antialias: false,
precision: 'lowp',
canvas: canvas,
alpha: true
});
renderer.render(scene, camera);
//animate
renderer.setAnimationLoop(animation);
function animation(time: number) {
mesh.rotation.x = time / 500;
renderer.render(scene, camera);
}
}
interface Text3DArgs extends ComponentProps<'canvas'> {
children: string;
/**
* zoom < 100
*/
zoom?: number;
scale?: number;
width?: number;
height?: number;
}
export default function Text3D({ children: text, zoom, scale, width, height, ...props }: Text3DArgs) {
if (!width && !height) {
return (null);
}
function runRef(canvas: HTMLCanvasElement | null) {
if (!canvas)
return;
runThree({
canvas, text, zoom, scale, width, height
});
}
return (
<canvas width={width} height={height} {...props} ref={runRef} />
)
}