Vue-curtains - An attempt at converting curtains.js WebGL classes into reusable Vue components
vue-curtains
vue-curtains is an attempt at converting curtains.js WebGL classes into reusable Vue components.
Getting started
Installation
⚠️ vue-curtains requires Vue 3
Of course you’ll need to create a Vue app first. Then, just add vue-curtains into your project by installing the npm package:
npm install vue-curtains
Components
vue-curtains introduces a bunch of components based on curtains.js classes:
Hooks
The library relies on the Vue 3 Composition API.
Inside your <Curtains></Curtains>
component, you’ll have access to a couple useful custom hooks:
useCurtains
useCurtains(callback);
This hook returns the curtains instance injected by the <Curtains></Curtains>
component, or null
if the instance is undefined (for example if you try to use it outside your <Curtains></Curtains>
component).
import { useCurtains } from "vue-curtains";
export default {
name: "MyComponent",
setup() {
useCurtains((curtains) => {
// get curtains bounding box for example...
const curtainsBBox = curtains.getBoundingRect();
});
}
}
useCurtainsEvent
useCurtainsEvent(event, callback);
This hook lets you subscribe to any of your curtains instance events, so you can use those events from any <Curtains></Curtains>
child component in your app.
import { useCurtainsEvent } from "vue-curtains";
export default {
name: "MyComponent",
setup() {
useCurtainsEvent("onScroll", (curtains) => {
// get the scroll values...
const scrollValues = curtains.getScrollValues();
});
}
}
Examples
Explore
Here are codesandboxes ports of some of the official documentation examples:
- Basic plane
- Vertex coordinates helper
- Simple plane
- Simple video plane
- Slideshow using GSAP
- Multiple planes
- Multiple planes with post processing
- Selective render targets
- Flowmap
Basic example
This is the port of curtains.js documentation basic example:
App.vue
import { Curtains, Plane } from "vue-curtains";
const basicVs = `
precision mediump float;
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uTextureMatrix0;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
void main() {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
// varyings
vVertexPosition = aVertexPosition;
vTextureCoord = (uTextureMatrix0 * vec4(aTextureCoord, 0.0, 1.0)).xy;
}
`;
const basicFs = `
precision mediump float;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D uSampler0;
uniform float uTime;
void main() {
vec2 textureCoord = vTextureCoord;
// displace our pixels along the X axis based on our time uniform
// textures coords are ranging from 0.0 to 1.0 on both axis
textureCoord.x += sin(textureCoord.y * 25.0) * cos(textureCoord.x * 25.0) * (cos(uTime / 50.0)) / 25.0;
gl_FragColor = texture2D(uSampler0, textureCoord);
}
`;
export default {
name: "App",
components: {
Curtains,
Plane,
},
setup() {
const planeParams = {
vertexShader: basicVs,
fragmentShader: basicFs,
uniforms: {
time: {
name: "uTime",
value: 0,
},
},
};
const onRender = (plane) => {
plane.uniforms.time.value++;
};
return {
planeProps,
onRender
};
};
</script>
<template>
<!-- should be put outside the router -->
<Curtains id="CurtainsCanvas">
<Plane id="BasicPlane" :params="planeParams" @render="onRender">
<img src="/path/to/my-image.jpg" alt="" />
</Plane>
</Curtains>
</template>
<style scoped>
#CurtainsCanvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
}
#BasicPlane {
width: 80%;
height: 80vh;
margin: 10vh auto;
}
#BasicPlane img {
display: none;
}
</style>
main.js
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");