Giter Site home page Giter Site logo

martinlaxenaire / curtainsjs Goto Github PK

View Code? Open in Web Editor NEW
1.6K 20.0 110.0 44.82 MB

curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes.

Home Page: https://www.curtainsjs.com/

License: MIT License

JavaScript 100.00%
javascript webgl 3d html5 canvas responsive shaders glsl texture curtains curtainsjs webgl2 shader dom

curtainsjs's Introduction

What is it ?

Shaders are the new front-end web developpment big thing, with the ability to create very powerful 3D interactions and animations. A lot of very good javascript libraries already handle WebGL but with most of them it's kind of a headache to position your meshes relative to the DOM elements of your web page.

curtains.js was created with just that issue in mind. It is a small vanilla WebGL javascript library that converts HTML elements containing images and videos into 3D WebGL textured planes, allowing you to animate them via shaders.
You can define each plane size and position via CSS, which makes it super easy to add WebGL responsive planes all over your pages.

curtains.js demo gif

Knowledge and technical requirements

It is easy to use but you will of course have to possess good basics of HTML, CSS and javascript.

If you've never heard about shaders, you may want to learn a bit more about them on The Book of Shaders for example. You will have to understand what are the vertex and fragment shaders, the use of uniforms as well as the GLSL syntax basics.

Installation and usage

You can directly download the files and start using the ES6 modules:
import {Curtains, Plane} from 'path/to/src/index.mjs';

const curtains = new Curtains({
    container: "canvas"
});

const plane = new Plane(curtains, document.querySelector("#plane"));
Or you can use npm:
npm i curtainsjs
Load ES6 modules:
import {Curtains, Plane} from 'curtainsjs';
In a browser, you can use the UMD files located in the `dist` directory:
<script src="dist/curtains.umd.min.js"></script>
const curtains = new Curtains({
    container: "canvas"
});

const plane = new Plane(curtains, document.querySelector("#plane"));

// etc

Usage with React

Note that if you are using React, you might want to try react-curtains, curtains.js official React package.

Documentation

The library is split into classes modules. Most of them are used internally by the library but there are however a few classes meant to be used directly, exported in the src/index.mjs file.

Core

  • Curtains: appends a canvas to your container and instanciates the WebGL context. Also handles a few helpers like scroll and resize events, request animation frame loop, etc.
  • Plane: creates a new Plane object bound to a HTML element.
  • Textures: creates a new Texture object.

Frame Buffer Objects

  • RenderTarget: creates a frame buffer object.
  • ShaderPass: creates a post processing pass using a RenderTarget object.

Loader

  • TextureLoader: loads HTML media elements such as images, videos or canvases and creates Texture objects using those sources.

Math

  • Vec2: creates a new Vector 2.
  • Vec3: creates a new Vector 3.
  • Mat4: creates a new Matrix 4.
  • Quat: creates a new Quaternion.

Extras

  • PingPongPlane: creates a plane that uses FBOs ping pong to read/write a texture.
  • FXAAPass: creates an antialiasing FXAA pass using a ShaderPass object.

Full documentation

Getting started
API docs
Examples

Basic example

HTML

<body>
    <!-- div that will hold our WebGL canvas -->
    <div id="canvas"></div>
    
    <!-- div used to create our plane -->
    <div class="plane">
    
        <!-- image that will be used as texture by our plane -->
        <img src="path/to/my-image.jpg" crossorigin="" />
    </div>
    
</body>

CSS

body {
    /* make the body fits our viewport */
    position: relative;
    width: 100%;
    height: 100vh;
    margin: 0;
    overflow: hidden;
}

#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: 80%;
    height: 80vh;
    margin: 10vh auto;
}

.plane img {
    /* hide the img element */
    display: none;
}

Javascript

import {Curtains, Plane} from 'curtainsjs';

window.addEventListener("load", () => {
    // set up our WebGL context and append the canvas to our wrapper
    const curtains = new Curtains({
        container: "canvas"
    });
    
    // get our plane element
    const planeElement = document.getElementsByClassName("plane")[0];
    
    // set our initial parameters (basic uniforms)
    const params = {
        vertexShaderID: "plane-vs", // our vertex shader ID
        fragmentShaderID: "plane-fs", // our fragment 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,
            },
        },
    };
    
    // create our plane using our curtains object, the bound HTML element and the parameters
    const plane = new Plane(curtains, planeElement, params);
    
    plane.onRender(() => {
        // use the onRender method of our plane fired at each requestAnimationFrame call
        plane.uniforms.time.value++; // update our time uniform value
    });
    
});

Shaders

Vertex shader

<script id="plane-vs" type="x-shader/x-vertex">
    #ifdef GL_ES
    precision mediump float;
    #endif
    
    // 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;
    
    // our texture matrix that will handle image cover
    uniform mat4 uTextureMatrix0;
    
    // pass your vertex and texture coords to the fragment shader
    varying vec3 vVertexPosition;
    varying vec2 vTextureCoord;
    
    void main() {       
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        
        // set the varyings
        // here we use our texture matrix to calculate the accurate texture coords
        vTextureCoord = (uTextureMatrix0 * vec4(aTextureCoord, 0.0, 1.0)).xy;
        vVertexPosition = aVertexPosition;
    }
</script> 

Fragment shader

<script id="plane-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision mediump float;
    #endif
    
    // get our varyings
    varying vec3 vVertexPosition;
    varying vec2 vTextureCoord;
    
    // the uniform we declared inside our javascript
    uniform float uTime;
    
    // our texture sampler (default name, to use a different name please refer to the documentation)
    uniform sampler2D uSampler0;
    
    void main() {
        // get our texture coords from our varying
        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;
        
        // map our texture with the texture matrix coords
        gl_FragColor = texture2D(uSampler0, textureCoord);
    }
</script> 

Changelog

Complete changelog starting from version 7.1.0

curtainsjs's People

Contributors

ajayns avatar andrewbranch avatar dejong avatar dependabot[bot] avatar martinlaxenaire avatar risq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

curtainsjs's Issues

Logo for curtainsjs

Hi, I'm Interested with your lightweight vanilla WebGL library for animating images and videos. Can I Contribute a Logo For This Project ?

Curtains.js in React components using React Hooks

Hey there, I'm trying to use Curtains.js in React with React Hooks (and with Gatsby), does anybody have any experience setting this up correctly to avoid any memory leaks or lost references to canvas elements?

I've already looked into this older discussion on the topic (#9), although I notice that it only sets up one curtains instance in the root node (and uses an id in a React component), which seems different from my goal of setting up distinct Curtains instances in different sub components and handling their creation, re-rendering, and destruction separately - and handling dom elements via refs and not id directly in React.

I'm using the useRef() hook to keep reference to my canvas container dom element. Also, I am setting the Curtains and Plane instances in my state with the useState() hook, and calling curtains.dispose() in the deconstructor return of the useEffect() React hook. I'm trying to use textures in my shader via img elements with data-sampler attributes, although I'm not sure if this attribute and how its handled as a uniform in Curtains will play well with React re-renders.

The problem I'm facing is that it seems to only work sometimes to apply dynamic image src changes to the data-sampler elements - almost as if the uniforms for these textures are not being correctly updated when the React component updates. Either that, or perhaps I am accidentally re-creating or duplicating canvas elements and losing reference to the original

If anyone has experience with getting this working properly with React hooks, it would be greatly appreciated - thanks!

Curtains is not defined

Hi, I'm getting the error "Curtains is not defined", just trying to implement the basic example.

My code:

import 'curtainsjs/build/curtains'

function backgroundScene() {
	const curtain = new Curtains('intro-canvas')
	const planeEl = document.querySelector('.js-intro__plane')

	const params = {
        vertexShaderID: "plane-vs", // our vertex shader ID
        fragmentShaderID: "plane-fs", // 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,
            },
        }			
	}

	const plane = curtain.addPlane(planeEl, params)

	plane.onRender(function() {
		plane.uniforms.time.value++
	})
}

backgroundScene()

Also if i try import Curtains from 'curtainsjs' I get an error saying can't find module curtainsjs

Am I doing something wrong?

Possible to resize texture inside of plane?

Hi,

Thanks for the great work on this library! So I'm trying to use intrinsic scale on the image textures inside each plane to have them fill and center inside the parent plane container because each image isn't a uniform size...

I can calculate the bounding box math using the _getSizes() method to scrape (planeWidth, planeHeight, sourceWidth, sourceHeight). I see texture has a setScale() method but not sure exactly how to apply the new width, height, x, y values to the texture directly.

Any ideas?

Implementing a morphing page transition

Hey Martin!
Enjoying working with the library so far, great work. This is a query for approach rather than an issue.

I'm attempting to implement a morphing page transition: much like the ajax navigation example but on both the pages, the same image is present and the page transition (instead of fade from your example) morphs between the sizes and positions of the image in different pages.

For the process of morphing, I move the image to position: fixed and modify values of width, height, top, left. So curtains seem to work fine when I remove planes right before the transition and add planes right after it is over but if I want to keep the plane during the course of the transition, it doesn't seem to work.
Since the position changes, I figured during the course of the transition I could call plane.updatePosition on render but that only seems to cause the plane itself to disappear during the transition. Is there any condition that position tracking only works for elements position relative or static?

Thanks,
Ajay

How to stop curtains?

Hi,
First of all, I love curtainsjs. I'm working on a project and I want to stop the canvas animation when I click a button. But I can't find the way and I can't find anything related on the docs.

[Performance] Hide elements not in view

Not sure if this is something already implemented (sorry, I should sift through the code), but it would be beneficial for pages with tons of content, or pages with infinite (lazy loaded or otherwise) content and so forth, to hide elements that are not in view. Would there be any benefit to adding that to the library, or perhaps a plugin API with subsequent plugins for curtains.

React App / Gatsby

Hey There!

I love how this works, however I haven't got a clue how to make this work inside of a React app / Gatsby site. have you any advice?

I've followed the tutorial, but can never get the curtain to render the simple plane.

displacement image aspect ratio

Hey guys.
Fantastic library!

https://www.curtainsjs.com/examples/multiple-textures/index.html

I was playing around with this demo but I can't figure out how to make the displacement image to respect its aspect ratio while covering the whole texture (sort of like "backgound-size:cover or object-fill:cover;)

I put a black text inside the displacement image in order to have a cool effect, but when I resize the screen, the text stretches... any ideas?

Thank you for your help!

Curtains glitches when using a JS Router

I've set up a basic code sandbox to illustrate the issue: https://codesandbox.io/s/fervent-dust-43spf

I'm using a very simple router setup which adds page content and removes the previous page content. So for curtains, I'm removing planes and adding new ones on route change. But this seems to be causing a couple of glitches once the transition is done - the images aren't loaded in position/right manner.

Additional note: Async textures or so isn't the solution I'm looking for cause the planes in different pages will have different positions/layouts, this example is just to illustrate the bug in the simplest manner

Problems when packaging curtain.js

Hi,
I'm working with parceljs and packaging curtainjs v2.0 sends me this message to the console. For while I will use v1.8 until I find a solution.

                <div id="canvas"></div>

                <!-- div used to create our plane -->
                <div class="plane" data-vs-id="plane-vs" data-fs-id="plane-fs">

                    <!-- images that will be used as textures by our plane -->
                    <img data-sampler="dispImage" src="./src/images/cooking.jpg" crossorigin="anonymous"/>
                    <img data-sampler="map" src="./src/images/map.jpg" crossorigin="anonymous"/>
                    <img data-sampler="blurImage" src="./src/images/cooking-blur.jpg" crossorigin="anonymous"/>
                </div>
const {
   vs,
   fs
 } = shaders
 const mouse = {
   x: 0,
   y: 0
 };
 // set up our WebGL context and append the canvas to our wrapper
 const webGLCurtain = new Curtains("canvas");

 // get our plane element
 const planeElement = document.querySelector(".plane");

 // set our initial parameters (basic uniforms)
 const params = {
   vertexShader: vs, // our vertex shader ID
   fragmentShader: fs, // our framgent shader ID
   widthSegments: 40,
   heightSegments: 40, // we now have 40*40*6 = 9600 vertices !
   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: [mouse.x, mouse.y]
     },
     resolution: {
       name: "uReso",
       type: "2f",
       value: [innerWidth, innerHeight]
     }
   }
 };

 console.log(planeElement)
 // create our plane mesh
 const plane = webGLCurtain.addPlane(planeElement, params);
 
 // use the onRender method of our plane fired at each requestAnimationFrame call

 
 plane.onRender(() => {
   plane.uniforms.time.value++; // update our time uniform value

   plane.uniforms.mousepos.value[0] = maths.lerp(mouse.x, plane.uniforms.mousepos.value[0], .5)
   plane.uniforms.mousepos.value[1] = maths.lerp(mouse.y, plane.uniforms.mousepos.value[1], .5)
 });

 window.addEventListener('resize', () => {
   plane.uniforms.resolution.value = [innerWidth, innerHeight]
 })

 window.addEventListener("mousemove", ({
   clientX,
   clientY
 }) => {
   mouse.x = -clientX / innerWidth;
   mouse.y = clientY / innerHeight;
 });
const shaders = {
    vs: `    
    #ifdef GL_ES
    precision mediump float;
    #endif
    
    // 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 mapMatrix;

    // if you want to pass your vertex and texture coords to the fragment shader
    varying vec3 vVertexPosition;
    varying vec2 vTextureCoord;

    void main() {
        vec3 vertexPosition = aVertexPosition;
        gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);

        // set the varyings
        vTextureCoord = (mapMatrix * vec4(aTextureCoord, 0., 1.)).xy;
        vVertexPosition = vertexPosition;
    }`,

    fs: `
    #ifdef GL_ES
    precision mediump float;
    #endif
    
    #define PI2 6.28318530718
    #define PI 3.14159265359
    #define S(a,b,n) smoothstep(a,b,n)
    
    // get our varyings
    varying vec3 vVertexPosition;
    varying vec2 vTextureCoord;

    // the uniform we declared inside our javascript
    uniform float uTime;
    uniform vec2 uReso;
    uniform vec2 uMouse;

    // our texture sampler (default name, to use a different name please refer to the documentation)
    uniform sampler2D dispImage;
    uniform sampler2D map;
    uniform sampler2D blurImage;
  
    vec2 distortion(vec2 p, float freq, float amp, float t, float map, float speed){
          float phase = speed * freq;
      
          float x = 50. * p.y - freq * t * .009 + phase; 
          float y = 5. * p.x - freq * t * .005 + phase;
          
          float distX = cos(x+y) * amp * cos(y);
          float distY = sin(x-y) * amp * cos(y);
      
          vec2 distortion = vec2(distX * map, distY * map);  
      
          return distortion;
    }
  
    void main() {
          vec2 st = (gl_FragCoord.xy - .5 * uReso) / min(uReso.x, uReso.y);     
          vec2 uv = vTextureCoord;
     
          float t = uTime * .1;
          vec2 m = uMouse * .02;

          float depth = texture2D(map, uv).r - .5;
          float map = texture2D(map, uv).g;
               
          vec2 dist = distortion(uv, 100., .003, t, map, 10.);
          
          vec2 turbulance = dist + depth * m;

          vec4 color = texture2D(dispImage, uv + turbulance);
          vec4 blur = texture2D(blurImage, uv + turbulance);
          
          gl_FragColor = mix(color, blur, length(dist) / .003 );
          gl_FragColor = vec4(1., 1., 0., 1.);
    }    
    `
};

image

Possible to use canvas as texture?

First of all, thanks so much for this great library!

At the moment, this library allows us to use an image or video as a texture, but I am wondering if there is any way I can use a canvas as a texture?

Many thanks in advance!

Plane inside plane or make ShaderPass work like a plane?

The animation I'm trying to make requires 3d position work that affects all the planes inside as a whole. Shader passes would've served this purpose but as I read it does not have projection and model view matrices.
Getting everything inside a bigger plane is also not possible yet?

What's the workaround? Awesome library btw.

Image position

Good day! Thank you for the awesome plugin. Can you please tell me is there a way to render the image with 'padding'. The issue is that when shader applied the image gets cut off from sides. It looks like an image trying to fill a plane fully, like cover in CSS. But I need like contain.
Tried: drawCheckMargins but nothing changed, played with image size by CSS in plane but its not working too.
Thanks in advance

How to get image to render correctly

Hi, I'm using your node package with react.
I am trying to set up the basic plane example, but for some reason, my image is not rendering on the canvas.
It is only rendering a black canvas that makes waves in response to my mouse movements.
How can I get my image to render?

import React, { useRef, useEffect } from 'react';
import curtain from '../../assets/images/curtain.jpg'
import './curtain.scss';
import { Curtains } from 'curtainsjs';
import vertexShader from './vert.js';
import fragmentShader from './frag.js';
import { XYDelta, linearInterpolate } from '../../utils/physics';

function damp(a, b, dampFactor) {
  return (a - b) * dampFactor
}

export default function Curtain() {
  const mouseDamp = 10;
  const warpDepth = 1;
  const decayCoefficient = 0.3;
  const rippleWidth = 0.02;
  const curtainElement = useRef(null);
  const curtainContainer = useRef(null);
  const plane = useRef(null);
  const lerpedPosition = useRef({
    x: 0,
    y: 0,
  })
  const prevLerpedPosition = useRef({
    x: 0,
    y: 0,
  })
  const deltas = useRef({
    max: 0,
    applied: 0,
  })

  useEffect(() => {
    // set up our WebGL context and append the canvas to our wrapper
    const webGLCurtain = new Curtains({
        container: "canvas",
        watchScroll: false
    });

    webGLCurtain
    .onError(
      function(e) {
        console.error(e);
      }
    )
    .onContextLost(
      function() {
        webGLCurtain.restoreContext();
      }
    )

    const { clientWidth, clientHeight } = curtainElement.current;
    const { x: mouseX, y: mouseY } = lerpedPosition.current;
    const params = {
        vertexShader,
        fragmentShader,
        widthSegments: 20,
        heightSegments: 20,
        uniforms: {
            resolution: { // resolution of our plane
                name: "uResolution",
                type: "2f",
                value: [clientWidth, clientHeight],
            },
            time: { // time uniform that will be updated at each draw call
                name: "uTime",
                type: "1f",
                value: 0,
            },
            mousePosition: { // our mouse position
                name: "uMousePosition",
                type: "2f", // again an array of floats
                value: [mouseX, mouseY],
            },
            mouseMoveStrength: { // the mouse move strength
                name: "uMouseMoveStrength",
                type: "1f",
                value: 0,
            }
        }
    };
    const _plane = webGLCurtain.addPlane(curtainElement.current, params);
    _plane
    .onReady(
      function() {
          // set a fov of 35 to exagerate perspective
          _plane.setPerspective(35);

          // apply a little effect once everything is ready
          deltas.current.max = 2;
          plane.current = _plane;
      }
    )
    .onRender(
      function() {
          const { uniforms: { time, mouseMoveStrength } } = _plane;
          // increment our time uniform
          time.value++;

          // decrease both deltas by damping : if the user doesn't move the mouse, effect will fade away
          deltas.current.applied -= damp(deltas.current.applied, deltas.current.max, rippleWidth);
          deltas.current.max -= damp(deltas.current.max, 0, rippleWidth);

          // send the new mouse move strength value
          mouseMoveStrength.value = deltas.current.applied;
      }
    )
    .onAfterResize(
      function() {
          const { uniforms: { resolution } } = _plane;
          const { width, height } = _plane.getBoundingRect();
          resolution.value = [width, height];
      }
    )

    return () => {
      plane.current = null;
      webGLCurtain.dispose();
    }
  })

  function handleMovement(clientX, clientY) {
    if (plane.current) {
      const { x: prevX, y: prevY } = prevLerpedPosition.current = lerpedPosition.current
      const { x: lerpX, y: lerpY } = lerpedPosition.current = {
        x: linearInterpolate(prevX, clientX, decayCoefficient),
        y: linearInterpolate(prevY, clientY, decayCoefficient)
      }
      const { x, y } = plane.current.mouseToPlaneCoords(lerpX, lerpY);
      plane.current.uniforms.mousePosition.value = [x,y];

      if (prevX && prevY) {
        const delta = XYDelta(lerpedPosition.current, prevLerpedPosition.current, mouseDamp, warpDepth)
        if (delta >= deltas.current.max) {
          deltas.current.max = delta;
        }
      }
    }
  }

  function onMouseMove({ clientX, clientY }) {
    handleMovement(clientX, clientY);
  }

  function onTouchMove({ targetTouches: [{ clientX, clientY }] }) {
    handleMovement(clientX, clientY);
  }

  // function onMouseMove() {}
  // function onTouchMove() {}
  return <div
           ref={curtainContainer}
           id="page-wrap"
           onMouseMove={onMouseMove}
           onTouchMove={onTouchMove}
          >
      <div id="canvas"/>
      <div ref={curtainElement} className="curtain">
        <img src={curtain} alt='banner' />
      </div>
    </div>
}
#page-wrap {
    width: 100%;
    height: 100%;
    position: relative;
}

/*** canvas ***/

#canvas {
    height: 100%;
    width: 100%;
}


.curtain {
    position: absolute;
    top: 0;
    width: 100%;
    height: 100%;
}

.curtain img {
    width: 100%;
    height: 100%;
    display: none;
}

How to dispose of Curtains

Hi, ive curtains working beautifully, however it's causing a huge memory leak as when i unmount the component, it is still running.

I read the documentation on the dispose function. How do you use it?

This is my current code:
`

export default class About extends React.Component {

addAnimation = () => {
    const $trigger = document.querySelector('.header-about');
    const $target = document.querySelector('.home-about');

    $trigger.addEventListener('mouseover', function () {
        $target.classList.add('open');
    });

    $trigger.addEventListener('mouseout', function () {
        $target.classList.remove('open');
    });
}

removeAnimation = () => {
    const $trigger = document.querySelector('.header-about');
    const $target = document.querySelector('.home-about');

    $trigger.removeEventListener('mouseover', function () {
        $target.classList.add('open');
    });

    $trigger.removeEventListener('mouseout', function () {
        $target.classList.remove('open');
    });
}

curtains = () => {
    setTimeout(function () {
        // our canvas container
        var canvasContainer = document.getElementById("canvas");

        // track the mouse positions to send it to the shaders
        var mousePosition = {
            x: 0,
            y: 0,
        };
        // we will keep track of the last position in order to calculate the movement strength/delta
        var mouseLastPosition = {
            x: 0,
            y: 0,
        };
        var mouseDelta = 0;

        // set up our WebGL context and append the canvas to our wrapper
        var webGLCurtain = new Curtains("canvas");

        // get our plane element
        var planeElements = document.getElementsByClassName("plane");


        // could be useful to get pixel ratio
        var pixelRatio = window.devicePixelRatio ? window.devicePixelRatio : 1.0;


        // some basic parameters
        // we don't need to specifiate vertexShaderID and fragmentShaderID because we already passed it via the data attributes of the plane HTML element
        var params = {
            widthSegments: 20,
            heightSegments: 20,
            imageCover: true,
            vertexShader: $vertexShader, // our vertex shader ID
            fragmentShader: $fragmentShader, // our framgent shader ID
            uniforms: {
                resolution: { // resolution of our plane
                    name: "uResolution",
                    type: "2f", // notice this is an length 2 array of floats
                    value: [pixelRatio * planeElements[0].clientWidth, pixelRatio * planeElements[0].clientHeight],
                },
                time: { // time uniform that will be updated at each draw call
                    name: "uTime",
                    type: "1f",
                    value: 0,
                },
                mousePosition: { // our mouse position
                    name: "uMousePosition",
                    type: "2f", // again an array of floats
                    value: [mousePosition.x, mousePosition.y],
                },
                mouseMoveStrength: { // the mouse move strength
                    name: "uMouseMoveStrength",
                    type: "1f",
                    value: 0,
                }
            }
        }

        // create our plane
        var simplePlane = webGLCurtain.addPlane(planeElements[0], params);

        simplePlane.onReady(function () {
            // set a fov of 35 to exagerate perspective
            simplePlane.setPerspective(35);

            // now that our plane is ready we can listen to mouse move event
            var wrapper = document.querySelector(".home");

            wrapper.addEventListener("mousemove", function (e) {
                handleMovement(e, simplePlane);
            });

            wrapper.addEventListener("touchmove", function (e) {
                handleMovement(e, simplePlane);
            });

            // on resize, update the resolution uniform
            window.onresize = function () {
                simplePlane.uniforms.resolution.value = [pixelRatio * planeElements[0].clientWidth, pixelRatio * planeElements[0].clientHeight];
            }

        }).onRender(function () {
            // increment our time uniform
            simplePlane.uniforms.time.value++;


            // send the new mouse move strength value
            simplePlane.uniforms.mouseMoveStrength.value = mouseDelta;
            // decrease the mouse move strenght a bit : if the user doesn't move the mouse, effect will fade away
            mouseDelta = Math.max(0, mouseDelta * 0.995);
        });

        // handle the mouse move event
        function handleMovement(e, plane) {

            if (mousePosition.x !== -100000 && mousePosition.y !== -100000) {
                // if mouse position is defined, set mouse last position
                mouseLastPosition.x = mousePosition.x;
                mouseLastPosition.y = mousePosition.y;
            }

            // touch event
            if (e.targetTouches) {

                mousePosition.x = e.targetTouches[0].clientX;
                mousePosition.y = e.targetTouches[0].clientY;
            }
            // mouse event
            else {
                mousePosition.x = e.clientX;
                mousePosition.y = e.clientY;
            }

            // convert our mouse/touch position to coordinates relative to the vertices of the plane
            var mouseCoords = plane.mouseToPlaneCoords(mousePosition.x, mousePosition.y);
            // update our mouse position uniform
            plane.uniforms.mousePosition.value = [mouseCoords.x, mouseCoords.y];

            // calculate the mouse move strength
            if (mouseLastPosition.x && mouseLastPosition.y) {
                var delta = Math.sqrt(Math.pow(mousePosition.x - mouseLastPosition.x, 2) + Math.pow(mousePosition.y - mouseLastPosition.y, 2)) / 30;
                delta = Math.min(4, delta);
                // update mouseDelta only if it increased
                if (delta >= mouseDelta) {
                    mouseDelta = delta;
                    // reset our time uniform
                    plane.uniforms.time.value = 0;
                }
            }
        }
    }, 50);
}

componentDidMount() {
    this.addAnimation();
    this.curtains();
}

componentWillUnmount() {
    this.removeAnimation();
    this.curtains.webGLCurtain.dispose(); // THIS DOESNT WORK

}

render() {

    return (
        <div className="home-about ">
            <div className="home-about__image plane">
                <img src={$image} alt="About 12Studio - JoeBenTaylor" />
            </div>

            <div id="canvas"></div>
        </div>
    );
}

}
`

Gray border when creating effects that change the edge of the image

When creating effects that change the edge of an image (for example, waves), a gray border appears along the perimeter of the image in browsers running under Windows 10. When working in Windows 7, there is no such border. This effect https://prnt.sc/sx1jt1, https://prnt.sc/sx2nw3 is visible even on your demos from the page https://www.curtainsjs.com/
Can something be done about it?
P.S. The border does not appear on effects that do not affect the edge of the image (tint, replace image, etc.).

TextureMatrix not functioning correctly

I've tried reproducing the issue in a simpler environment but was unfortunately unable to do so in the first attempt, so I'm hoping you have so insight just by reading the details of my problem itself.
This is how the layout looks like (it has no error but almost similar thing shows up an error in my local env): https://codepen.io/ajayns/pen/XWmQLNL

The plane's image unlike how it's expected to be (like object-fit: cover) it's showing up in stretched dimensions or zoom in and other glitches. Any input on how I can correct the uTextureMatrix0 uniform?

I have a feeling it was something to do with the layout but then I recreated that in the codepen in which it seems to work fine, now I suspect it's got something to do with the shader and that's why I'm looking for ideas.

Video url not working with curtains

Hi, I'm trying to to create a video displacement shader using the example available here: https://css-tricks.com/animate-images-and-videos-with-curtains-js/

I want to create the exact same effect. This is working fine when my video sources are in the directory, or if I use the links available in your example. However when I change the video source to a url the videos do not play. I have double checked that the urls are correct, and they work fine when in a video html tag, just not when I am using curtains. I'm trying to figure out the problem because a url can clearly work as it works when I use the links in the example. Let me know if you have any suggestions! Thanks.

iOS Safari bug

The positions of the planes get out of sync on iOS Safari. Chrome looks good though.

API to access camera position

The curtains class allows you to specify or access fov using params I believe. I'd like to access the Camera position the same way.

The problem I'm facing is while attempting to design an image grid to fullscreen animation using WebGL and shaders. So I'd like to calculate the dimensions of the viewport using camera fov and position.z.

WebGL version of image doesn't seem to align

I created a basic, responsive, draggable slider using GSAP. I'm now attempting to render it with Curtains so that I can apply some WebGL effects. But when I attempt to render the images they don't seem to match up - the Curtains version has extra padding/white space on the sides. Why is this? And how can I remove the white space? Ignore the color inversion, it's part of the end effect I'll be making.

I'm using this article as a reference of learning about Curtains in case that helps understand where I'm coming from.

Specifying z-position or stacking of planes

I'd like to know if there is any manual way of specifying the z-position of planes. The use case here would be a scenario where I have overlapping images in a certain order (equivalent to specifying z-position of a plane in WebGL).

Digging through the documentation and also experimenting: I see that the order of planes depends on creation: earlier the higher up. Docs also give information about depthTest but nothing in particular about placing planes in an order other than moveToFront which isn't exactly flexible for many use cases.

Feature: Detect if plane is currently in viewport or not

From the documentation, I assumed plane.visible will do the trick but it doesn't: it always shows up as true by default. It seems like _shouldDraw is what I'm looking for, but I feel like that could be exposed in the API itself.

The common use case where that will come handy is animations triggered once by visibility in the viewport: like an on appear animation. So it'll be nice to know what's already in the viewport and rest of them can be animated using plane.onReEnterView.

Rendering issue with large display when embedded in page

I got my slider to where I want it to be in terms of the effect. Now when I try embedding it into an actual page it seems to do great on small window sizes but breaks in a way that I don't understand on large window sizes. The background color should never change and the images disappear when I try to do the drag effect when the window size is large. I made a video recording of the issue. I see the issue in all browsers (I'm on Mac Catalina).

The code is the same as this pen. Any ideas what could cause this sort of issue?

Use with more than 2 textures

Hi !
First of all, thank you for this library and keep up the good work !

I am new to Webgl, and I followed this example : https://www.martin-laxenaire.fr/libs/curtainsjs/examples/multiple-textures/index.html

I can't get it to work with more than 2 textures...

Let's suppose I have 4 images. And I want the effect to be the same when going from the first texture to the second, and from the second to the third for example. And backwards from the third texture to the second texture, and from the second texture to the first.

Is there any way to have a generic code that would render the correct effect based on the texture we are currently showing and the one we are going to display ?

TextureLoader class

Hello, my congrats with a big update, but methods loadImages() loadCanvases() and loadVideos() are missing in a TextureLoader

Use more than two images

Is there a way to use more than two images? I'd love to make a slideshow using different images and displacement maps if possible, but I don't know if it's possible to do this.

Difficulty with fixed positioning

Hey there, I'm having difficulty with fixed positioning of my canvas and its container. It seems the container is behaving correctly with fixed position, but I can't get the canvas element to stay put, even when I apply fixed positioning to the canvas element directly. My goal is to have a full width, full height shader which can be a fixed background of a scrolling page.

I've read that that if any "transform" styling is set on a parent element then fixed won't work. However I don't see any transforms in DevTools on any of the containing elements.

Is there a setting I can use in the library or known adjustments I can make to my code to keep my canvas and container fixed?

Thank you!

Performant approach for using multiple effects on a plane

Say I've got a plane that does multiple effects using its fragment shader. The animations including things like a scroll-linked appear, then a mouseover and finally, an exit animation when transitioning out of the page.

What would be the recommended approach to go about this using Curtains that is efficient and performant? The ideas include including all these effects in a single fragment shader but attempting to optimize as to reduce unnecessary calculations when unused. Alternatively, I'm thinking of switching frag shaders for different states: initial appear, in page interaction, exit - this could be by reinitializing or loading new planes.

Edit: Sorry about the whole mixup, I closed the wrong issue by mistake.

Locomotive scroll example doesn't work on WebKit browsers

Hello,

It looks like that Locomotive scroll example doesn't work correctly on Mac WebKit desktop browsers (Chrome and Safari).

Even fixing the console error
curtains.js:5319 Uncaught ReferenceError: module is not defined at curtains.js:5319
by using the minified js, the planes are not showed on WebKit browsers.

image

Can you check?

Great library, thank you.
Marco

Missing NPM package

Hi, I've recently tried curtainsjs and really liked it, nice project!
Although I can't find an NPM module of this library to install with node or yarn.
Could you create one, please? It would be very useful, thanks a lot! πŸ˜„

How to assign values ​​to an element of a vector?

I was observing that in new versions of the library I assign values ​​to the items but it doesn't detect.
Another problem is that now i can't use gsap to change vector values

this is a example not works

this.bufferTrail.uniforms.mousepos.value[0] = this.mouse.x
this.bufferTrail.uniforms.mousepos.value[1] = this.mouse.y

this works

this.bufferTrail.uniforms.mousepos.value = [this.mouse.x, this.mouse.y]

Uncaught SyntaxError: Unexpected token export

Hi @martinlaxenaire

I've been trying to implement the demo code for tests in my script.

The following works :
When I include CurtainJS like this <script src="curtains.min.js"></script> before my script.

The following doesn't work :
Adding this directly at the beginning of my script : import { Curtains } from 'curtainsjs';

I get the error : Uncaught SyntaxError: Unexpected token export

I'm using webpack and babel.

Thanks for your help

Missing some uniform types

Hi, I've tried to pass a mat4 uniform to my shader, but I haven't found a uniform type for it. So I digged down into the source code and I saw it was actually missing along with mat2 and mat3 types. I thing it could be useful in some cases, it would be a problem to add this types?

Curtain.js + Squarespace?

Hello!
I am following your tutorial on CSS Tricks, I am trying to get your first example in the tutorial working on Squarespace. Not sure if you are familiar with Squarespace. But I am using a Code Block for the HTML and injecting the JS into the header of the page. It's turning out to be black, would you have an insight on why that is?

The warnings I am getting make me think it's limits Squarespace has put on the template, but I wondered if you have seen them before? It looks like curtain.js is loading fine but the image is being blocked.

Specifically this warning...
Access to image at 'https://static1.squarespace.com/static/5dc0741018cb921d7dead1d7/t/5e6d6368e095af7f34d6a373/1584227183147/DSC06867-c-barbari-cbd-shop-cbd-flower-joints-cbd-prerolls-herbal-cigarettes-hemp.png' from origin 'https://www.naomichan.info' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*,*', but only one is allowed.

This is where I'm testing this, please take a look and if you have any insight, would be extremely helpful!

Typescript declaration file missing

I can no longer render anything as no declaration file can be fouund, i get this error when i use the es6 import method:

module "/Users/joetaylor/Documents/www/airpay-2019-website/node_modules/curtainsjs/libs/curtains.min" Could not find a declaration file for module 'curtainsjs'. '/Users/joetaylor/Documents/www/airpay-2019-website/node_modules/curtainsjs/libs/curtains.min.js' implicitly has an 'any' type. Try npm install @types/curtainsjsif it exists or add a new declaration (.d.ts) file containingdeclare module 'curtainsjs';ts(7016) Could not find a declaration file for module 'curtainsjs'. '/Users/joetaylor/Documents/www/airpay-2019-website/node_modules/curtainsjs/libs/curtains.min.js' implicitly has an 'any' type. Try npm install @types/curtainsjsif it exists or add a new declaration (.d.ts) file containingdeclare module 'curtainsjs';ts(7016)

Applying effect on multiple images

Hi everyone, and thank you Martin for developing such an cool library. I would like to apply this effect on several images on a website I'm building with Wordpress. Starting from that example I've tried adding another image but unfortunately I wasn't able to get the same effect. It just show one image and, I'm not sure why, it appears black.

In this codepen there's my attempt. I have to say I'm quite new to WebGl and Shaders but I've been trying to keep up

https://codepen.io/archemede/pen/mdPjRve

Can you help understand what I'm doing wrong?

Thank you so much!

GL_REPEAT as default on plane texture instead of GL_CLAMP

Hi, I'm trying to do some simple image postprocessing and would like the default sampler to be repeated instead of clamped ( as shown here).

Is there a way I can do this without creating a texture object and adding it to a plane? I can see in the docs that you can set the wrapS/wrapT as a parameter in the texture object but wondered if there's a way to set it without doing that. I don't really understand how to use addParent as the texture doesn't seem to show up.

Apologies if this is somewhere in an example, I don't seem to be able to find it.

Also, not sure how to add a label to the issue, I'm seeking-help

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.