Skip to content

Instantly share code, notes, and snippets.

@bozzin
Created August 28, 2020 16:52
Show Gist options
  • Save bozzin/0a452540969e4de92747beaad3c7c274 to your computer and use it in GitHub Desktop.
Save bozzin/0a452540969e4de92747beaad3c7c274 to your computer and use it in GitHub Desktop.
Text Curtains.js
<div id="page-wrap">
<!-- div that will hold our WebGL canvas -->
<!--<nav class="main-nav">
<div class="logo"><img src="" alt="" class="img-logo">AB</div>
<ul class="main-menu">
<li><a href="">HOME</a></li>
<li><a href="">ABOUT</a></li>
<li><a href="">WORKS</a></li>
<li><a href="">CONTACT</a></li>
</ul>
</nav>--->
<div id="canvas"></div>
<!-- div used to create our plane -->
<div class="plane">
<!-- images that will be used as textures by our plane -->
<div class="title-hero">
<h1 class="text">Alain Barrios</h1>
</div>
</div>
</div>
<script id="vs" type="f">
#ifdef GL_ES
precision mediump float;
#endif
#define TAU 6.28318530718
uniform float uTime;
// those are the mandatory attributes that the lib sets
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
// those are mandatory uniforms that the lib sets and that contain our model view and projection matrix
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 texture0Matrix;
// if you want to pass your vertex and texture coords to the fragment shader
varying vec3 vVertexPosition;
varying vec2 vTextureCoord0;
varying float vTime;
void main() {
vec3 vertexPosition = aVertexPosition;
vTime = uTime;
vTextureCoord0 = aTextureCoord;
vVertexPosition = vertexPosition;
gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);
}
</script>
<script id="fs" type="f">
#ifdef GL_ES
precision mediump float;
#endif
#define TAU 6.28318530718
#define PI 3.14159265359
#define S(a,b,n) smoothstep(a,b,n)
#define NUM_OCTAVES 5
varying float vTime;
uniform float uProgress;
uniform vec2 uReso;
uniform vec2 uMouse;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord0;
uniform sampler2D planeTexture;
void main(){
float radius = 0.4;
vec2 uv0 = vTextureCoord0;
vec2 st = (gl_FragCoord.xy - 0.5 * uReso) / min(uReso.x, uReso.y);
vec2 mouse = (uMouse - 0.5 * uReso) / min(uReso.x, uReso.y);
float dist = length(st - mouse);
float mag = pow(clamp(radius - dist / radius, 0.0, 1.0), 0.7);
vec2 len = (mouse - st) * mag;
float x = uv0.y * TAU * 15.0 + vTime * 5.0;
float y = uv0.x * TAU * 15.0 + vTime * 5.0;
float distx = cos(x+y) / 1000.0 * cos(y);
float disty = sin(x-y) / 1000.0 * cos(y);
vec2 distortion = vec2(distx, disty) + len;
vec4 color0 = texture2D(planeTexture, uv0 + distortion);
gl_FragColor = color0;
}
</script>
class WEBGL {
constructor(set) {
this.curtains = new Curtains({ container: "canvas" });
this.planeElement = set.planeElement;
this.mouse = {
x: 0,
y: 0
};
this.font = 'a'
this.params = {
vertexShader: document.getElementById("vs").textContent, // our vertex shader ID
fragmentShader: document.getElementById("fs").textContent, // our framgent shader ID
uniforms: {
time: {
name: "uTime", // uniform name that will be passed to our shaders
type: "1f", // this means our uniform is a float
value: 0
},
mousepos: {
name: "uMouse",
type: "2f",
value: [0, 0]
},
resolution: {
name: "uReso",
type: "2f",
value: [0, 0]
},
progress: {
name: "uProgress",
type: "1f",
value: 0
}
}
};
}
loadFont(name, urlFont) {
const font = new FontFace(`${name}`, `url(${urlFont})`);
return new Promise(resolve => {
font.load().then(font_face => {
document.fonts.add(font_face);
document.body.style.fontFamily = font.family;
resolve(font_face);
});
});
}
async createCanvas(plane, canvas) {
this.font = this.font && await this.loadFont(
"futura",
"https://cdn.statically.io/gh/AlainBarrios/Fonts/12a918f9/futura%20medium%20bt.ttf?raw=true"
);
const boundingPlane = plane.getBoundingRect();
const planeElement = plane.htmlElement;
// get styles of the h1 title
const textStyles = window.getComputedStyle(planeElement.children[0]);
const relWidth = boundingPlane.width / this.curtains.pixelRatio;
const relHeight = boundingPlane.height / this.curtains.pixelRatio;
const ctx2d = canvas.getContext("2d");
canvas.width = ctx2d.width = relWidth;
canvas.height = ctx2d.height = relHeight;
// set styles at text
ctx2d.fillStyle = textStyles.color;
ctx2d.font = `${textStyles.fontWeight} ${textStyles.fontSize} ${
this.font.family
}`;
// text baseline and text align
ctx2d.textBaseline = "middle";
ctx2d.textAlign = "center";
// draw the text in the midlle or our plane
ctx2d.fillText(planeElement.textContent, relWidth / 2, relHeight / 2);
// update our canvas texture once on next draw call
if (plane.textures.length > 0) {
plane.textures[0].needUpdate();
}
}
initPlane() {
// create our plane mesh
this.plane = this.curtains.addPlane(this.planeElement, this.params);
// use the onRender method of our plane fired at each requestAnimationFrame call
if (this.plane) {
const text = this.plane.htmlElement.textContent;
const canvas = document.createElement("canvas");
this.createCanvas(this.plane, canvas);
canvas.setAttribute("data-sampler", "planeTexture");
this.plane.loadCanvas(canvas);
this.plane.setScale(1, 1);
this.plane.onReady(() => {
this.plane.uniforms.resolution.value = [
this.plane.getBoundingRect().width,
this.plane.getBoundingRect().height
];
this.plane.textures[0].shouldUpdate = false;
this.update();
this.initEvents(canvas);
});
}
}
update() {
this.plane.onRender(() => {
this.plane.updatePosition();
this.plane.uniforms.time.value += 0.01; // update our time uniform value
});
}
initEvents(canvas) {
window.addEventListener("mousemove", e => {
const x = e.clientX;
const y = innerHeight - e.clientY;
TweenMax.to(this.plane.uniforms.mousepos.value, 0.7, {
0: x,
1: y
});
});
window.addEventListener("resize", () => {
this.plane.uniforms.resolution.value = [
this.plane.getBoundingRect().width,
this.plane.getBoundingRect().height
];
this.createCanvas(this.plane, this.plane.textures[0].source);
});
}
}
const webgl = new WEBGL({
planeElement: document.querySelector(".title-hero")
});
webgl.initPlane();
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://rawcdn.githack.com/martinlaxenaire/curtainsjs/95957f855ad9fd51850e97312cd389af76e9704e/build/curtains.min.js"></script>
/*@font-face {
font-family: "futura";
src: url(https://cdn.statically.io/gh/AlainBarrios/Fonts/12a918f9/futura%20medium%20bt.ttf?raw=true);
}*/
:root {
/*--futura: "futura"*/;
--color1: rgba(255, 255, 255, 1);
--color2: rgba(0, 0, 0, 1);
--fontSize1: 3rem;
}
*,
*::before,
*::after {
box-sizing: border-box;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-kerning: auto;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: var(--color2);
}
canvas {
display: block;
max-width: 100%;
}
.container-fluid {
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
#page-wrap {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
}
.main-nav {
position: relative;
display: flex;
align-items: center;
padding: 15px;
}
.logo {
font-size: 2rem;
background-color: var(--color2);
color: var(--color1);
padding-left: 10px;
padding-right: 10px;
letter-spacing: 5px;
text-indent: 2.5px;
padding-top: 15px;
padding-bottom: 15px;
}
.main-menu {
padding-left: 30px;
display: flex;
justify-content: flex-end;
font-weight: bold;
}
.main-menu li {
margin-left: 30px;
margin-right: 30px;
}
#canvas {
/* make the canvas wrapper fits the document */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.plane {
/* define the size of your plane*/
width: 100%;
height: 100vh;
z-index: 10;
cursor: crosshair;
}
.plane img {
/* hide the img element */
display: none;
}
.title-hero {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: calc(9/16 * 10vw);
}
.title-hero .text {
margin: 0;
opacity: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment