├── .gitignore ├── LICENSE ├── README.md ├── css └── style.css ├── favicon.ico ├── img ├── bg.jpg ├── bg2.jpg ├── bg3.jpg ├── bg4.jpg ├── bg5.jpg ├── bg6.jpg └── bg7.jpg ├── index.html ├── index2.html ├── index3.html ├── index4.html ├── index5.html ├── js └── app.js ├── package-lock.json ├── package.json ├── shaders_circle ├── fragment.glsl └── vertex.glsl └── videos ├── 1.mp4 ├── 2.mp4 ├── 3.mp4 ├── 4.mp4 ├── 5.mp4 ├── 6.mp4 ├── 7.mp4 └── averyimportantvideo.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | node_modules/ 3 | dist/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2009 - 2020 [Codrops](https://tympanus.net/codrops) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experimental Video Transitions with WebGL 2 | 3 | Some experimental video transitions using Curtain.js and shaders. 4 | 5 | ![image](https://tympanus.net/codrops/wp-content/uploads/2020/10/VideoTransitions_featured-2.jpg) 6 | 7 | [Article on Codrops](https://tympanus.net/codrops/?p=51333) 8 | 9 | [Demo](https://github.com/akella/videoTransitions/) 10 | 11 | 12 | ## Installation 13 | 14 | To run demo you will need [Parcel](https://parceljs.org/), either install it 15 | ``` 16 | npm install -g parcel-bundler 17 | parcel index.html 18 | ``` 19 | Or run without installation: 20 | ``` 21 | npx parcel index.html 22 | ``` 23 | After that the demo should be available on http://localhost:1234. 24 | 25 | 26 | ## Credits 27 | 28 | - Videos from [MixKit](https://mixkit.co/free-stock-video/) and [Coverr](https://coverr.co/) 29 | - Play icon by [Smashicons](https://www.flaticon.com/authors/smashicons) 30 | - [Curtains.js](https://www.curtainsjs.com/) by [Martin Laxenaire](https://twitter.com/webdesign_ml) 31 | 32 | ## Misc 33 | 34 | Follow Yuriy: [Twitter](http://twitter.com/akella), [GitHub](https://github.com/akella) 35 | 36 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/) 37 | 38 | ## License 39 | [MIT](LICENSE) 40 | 41 | Made with :blue_heart: by [Codrops](http://www.codrops.com) 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | font-size: 15px; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | --color-text: #fff; 14 | --color-bg: #111; 15 | --color-link: #fff; 16 | --color-link-hover: #fff; 17 | color: var(--color-text); 18 | background-color: var(--color-bg); 19 | font-family: navigo, sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | overflow: hidden; 23 | background-image: linear-gradient(rgba(120,120,120,0.2),rgba(120,120,120,0.2)), url(../img/bg.jpg); 24 | background-size: cover; 25 | } 26 | 27 | .demo-2 { 28 | background-image: linear-gradient(rgba(120,120,120,0.2),rgba(120,120,120,0.2)), url(../img/bg2.jpg); 29 | } 30 | 31 | .demo-3 { 32 | background-image: linear-gradient(rgba(0,0,0,0.3),rgba(0,0,0,0.3)), url(../img/bg3.jpg); 33 | } 34 | 35 | .demo-4 { 36 | background-image: linear-gradient(rgba(120,120,120,0.2),rgba(120,120,120,0.2)), url(../img/bg7.jpg); 37 | } 38 | 39 | .demo-5 { 40 | background-image: linear-gradient(rgba(120,120,120,0.2),rgba(120,120,120,0.2)), url(../img/bg5.jpg); 41 | } 42 | 43 | /* Page Loader */ 44 | .js .loading::before, 45 | .js .loading::after { 46 | content: ''; 47 | position: fixed; 48 | z-index: 1000; 49 | } 50 | 51 | .js .loading::before { 52 | top: 0; 53 | left: 0; 54 | width: 100%; 55 | height: 100%; 56 | background: var(--color-bg); 57 | } 58 | 59 | .js .loading::after { 60 | top: 50%; 61 | left: 50%; 62 | width: 60px; 63 | height: 60px; 64 | margin: -30px 0 0 -30px; 65 | border-radius: 50%; 66 | opacity: 0.4; 67 | background: var(--color-link); 68 | animation: loaderAnim 0.7s linear infinite alternate forwards; 69 | 70 | } 71 | 72 | @keyframes loaderAnim { 73 | to { 74 | opacity: 1; 75 | transform: scale3d(0.5,0.5,1); 76 | } 77 | } 78 | 79 | a { 80 | text-decoration: none; 81 | color: var(--color-link); 82 | outline: none; 83 | opacity: 0.7; 84 | } 85 | 86 | a:hover, 87 | a:focus { 88 | color: var(--color-link-hover); 89 | outline: none; 90 | text-decoration: underline; 91 | opacity: 1; 92 | } 93 | 94 | .frame { 95 | padding: 3rem 5vw; 96 | text-align: center; 97 | z-index: 1000; 98 | position: fixed; 99 | top: 0; 100 | left: 0; 101 | width: 100%; 102 | height: 100vh; 103 | } 104 | 105 | .frame__title { 106 | font-size: 1rem; 107 | margin: 0 0 1rem; 108 | font-weight: normal; 109 | } 110 | 111 | .frame__links { 112 | display: inline; 113 | } 114 | 115 | .frame__links a:not(:last-child), 116 | .frame__demos a:not(:last-child) { 117 | margin-right: 1rem; 118 | } 119 | 120 | .frame__demos { 121 | margin: 1rem 0; 122 | } 123 | 124 | .frame__demos-text { 125 | margin-right: 2rem; 126 | } 127 | 128 | .frame__demo--current, 129 | .frame__demo--current:hover { 130 | color: var(--color-text); 131 | opacity: 0.5; 132 | text-decoration: underline; 133 | } 134 | 135 | .frame__button { 136 | background: none; 137 | border: 0; 138 | margin: 0; 139 | padding: 0; 140 | -moz-appearance: none; 141 | -webkit-appearance: none; 142 | fill: #fff; 143 | width: 60px; 144 | height: 60px; 145 | cursor: pointer; 146 | -webkit-touch-callout: none; 147 | -webkit-user-select: none; 148 | -moz-user-select: none; 149 | -ms-user-select: none; 150 | user-select: none; 151 | } 152 | 153 | .frame__button:focus { 154 | outline: none; 155 | opacity: 0.9; 156 | } 157 | 158 | .frame__content { 159 | text-align: center; 160 | } 161 | 162 | .frame__content-title { 163 | margin: 0; 164 | padding-bottom: 1rem; 165 | line-height: 1; 166 | font-size: 8vw; 167 | font-weight: 300; 168 | font-family: ivypresto-headline, serif; 169 | color: #fff; 170 | position: relative; 171 | text-shadow: 0 1px 6px rgba(0,0,0,0.1); 172 | } 173 | 174 | .frame__content-title::after { 175 | content: ''; 176 | position: absolute; 177 | width: 20%; 178 | height: 1px; 179 | background: #fff; 180 | left: 40%; 181 | top: 100%; 182 | } 183 | 184 | .frame__content-text { 185 | max-width: 600px; 186 | font-size: 1rem; 187 | margin: 2rem auto 3rem; 188 | } 189 | 190 | .frame__switch-item { 191 | cursor: pointer; 192 | padding: 0.85rem 1.25rem; 193 | border: 1px solid #fff; 194 | font-family: ivypresto-headline, serif; 195 | font-weight: 300; 196 | font-size: 1.85rem; 197 | margin: 0 1rem; 198 | transition: opacity 0.3s; 199 | display: inline-block; 200 | margin-bottom: 1rem; 201 | } 202 | 203 | .frame__switch-item--current, 204 | .frame__switch-item:hover, 205 | .frame__switch-item:focus { 206 | text-decoration: none; 207 | } 208 | 209 | .frame__switch-item--current { 210 | pointer-events: none; 211 | opacity: 1; 212 | } 213 | 214 | .frame__content-text, 215 | .frame__switch { 216 | opacity: 0; 217 | transition: opacity 0.3s; 218 | } 219 | 220 | .video-started .frame__content-text, 221 | .video-started .frame__switch { 222 | opacity: 1; 223 | } 224 | 225 | .video { 226 | pointer-events: none; 227 | } 228 | 229 | #canvas { 230 | position: absolute; 231 | top: 0; 232 | right: 0; 233 | bottom: 0; 234 | left: 0; 235 | } 236 | 237 | .wrapper { 238 | width: 100%; 239 | height: 100vh; 240 | display: flex; 241 | pointer-events: none; 242 | } 243 | 244 | .plane { 245 | width: 100vw; 246 | height: 100vh; 247 | position: relative; 248 | z-index: 100; 249 | } 250 | 251 | .plane video { 252 | position: absolute; 253 | left: 0; 254 | top: 0; 255 | width: 100px; 256 | display: none; 257 | } 258 | 259 | @media screen and (min-width: 53em) { 260 | .frame { 261 | text-align: left; 262 | display: grid; 263 | padding: 3rem; 264 | pointer-events: none; 265 | grid-template-columns: 25% 50% 25%; 266 | grid-template-areas: 'title links links' 267 | '... play ...' 268 | 'content content content' 269 | '... demos demos'; 270 | } 271 | .frame__title { 272 | margin: 0; 273 | grid-area: title; 274 | } 275 | .frame__button { 276 | grid-area: play; 277 | align-self: end; 278 | justify-self: center; 279 | } 280 | .frame__content { 281 | grid-area: content; 282 | align-self: end; 283 | justify-self: center; 284 | } 285 | .frame__tagline { 286 | position: relative; 287 | margin: 0 0 0 1rem; 288 | padding: 0 0 0 1rem; 289 | opacity: 0.5; 290 | } 291 | .frame__demos { 292 | margin: 0; 293 | grid-area: demos; 294 | justify-self: end; 295 | align-self: end; 296 | } 297 | .frame__links { 298 | grid-area: links; 299 | padding: 0; 300 | justify-self: end; 301 | } 302 | .frame a, 303 | .frame button { 304 | pointer-events: auto; 305 | } 306 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/favicon.ico -------------------------------------------------------------------------------- /img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg.jpg -------------------------------------------------------------------------------- /img/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg2.jpg -------------------------------------------------------------------------------- /img/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg3.jpg -------------------------------------------------------------------------------- /img/bg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg4.jpg -------------------------------------------------------------------------------- /img/bg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg5.jpg -------------------------------------------------------------------------------- /img/bg6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg6.jpg -------------------------------------------------------------------------------- /img/bg7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/img/bg7.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebGL Video Transitions | Demo 1 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

WebGL Video Transitions with curtains.js

19 | 24 | 32 | 33 |
34 |

Ungovernable

35 |

Select another video here:

36 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 89 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebGL Video Transitions | Demo 2 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

WebGL Video Transitions with curtains.js

19 | 24 | 32 | 33 |
34 |

Unbreakable

35 |

Select another video here:

36 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 89 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /index3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebGL Video Transitions | Demo 3 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

WebGL Video Transitions with curtains.js

19 | 24 | 32 | 33 |
34 |

Insubordinate

35 |

Select another video here:

36 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 89 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /index4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebGL Video Transitions | Demo 4 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

WebGL Video Transitions with curtains.js

19 | 24 | 32 | 33 |
34 |

Unpretentious

35 |

Select another video here:

36 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 89 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /index5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebGL Video Transitions | Demo 5 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

WebGL Video Transitions with curtains.js

19 | 24 | 32 | 33 |
34 |

Thriving

35 |

Select another video here:

36 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 90 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | import { Curtains, Plane } from "curtainsjs"; 2 | import fragment from "../shaders_circle/fragment.glsl"; 3 | import vertex from "../shaders_circle/vertex.glsl"; 4 | import gsap from "gsap"; 5 | 6 | let activeTexture = 0; 7 | let currentTexture = 0; 8 | let transitionTimer = 0; 9 | let timer = 0; 10 | let isRunning = 0; 11 | 12 | window.addEventListener("load", () => { 13 | // set up our WebGL context and append the canvas to our wrapper 14 | const curtains = new Curtains({ 15 | container: "canvas", 16 | alpha: true, 17 | pixelRatio: Math.min(1.5, window.devicePixelRatio), // limit pixel ratio for performance 18 | }); 19 | 20 | // get our plane element 21 | const planeElements = [...document.getElementsByClassName("plane")]; 22 | const navElements = [...document.getElementsByClassName("js-nav")]; 23 | const duration = planeElements[0].getAttribute("data-duration") || 2; 24 | // set our initial parameters (basic uniforms) 25 | const params = { 26 | vertexShaderID: "vert", 27 | fragmentShaderID: "frag", 28 | uniforms: { 29 | transitionTimer: { 30 | name: "uTransitionTimer", 31 | type: "1f", 32 | value: 0, 33 | }, 34 | fadeIn: { 35 | name: "uFadeIn", 36 | type: "1f", 37 | value: 0, 38 | }, 39 | timer: { 40 | name: "uTimer", 41 | type: "1f", 42 | value: 0, 43 | }, 44 | to: { 45 | name: "uTo", 46 | type: "1f", 47 | value: 0, 48 | }, 49 | from: { 50 | name: "uFrom", 51 | type: "1f", 52 | value: 0, 53 | }, 54 | }, 55 | }; 56 | 57 | const multiTexturesPlane = new Plane(curtains, planeElements[0], params); 58 | 59 | // set up our basic methods 60 | multiTexturesPlane 61 | .onReady(() => { 62 | // display the button 63 | 64 | document.body.classList.add("curtains-ready"); 65 | let length = multiTexturesPlane.videos.length; 66 | 67 | // planeElements[0].addEventListener("click", () => { 68 | // gsap.to(multiTexturesPlane.uniforms.transitionTimer, { 69 | // duration: duration, 70 | // value: currentTexture + 1, 71 | // easing: 'power2.in', 72 | // onStart: () => { 73 | // multiTexturesPlane.videos[(currentTexture + 1) % length].play(); 74 | // currentTexture = currentTexture + 1; 75 | // }, 76 | // onComplete: () => { 77 | // multiTexturesPlane.videos[ 78 | // (currentTexture + length - 1) % length 79 | // ].pause(); 80 | // multiTexturesPlane.videos[ 81 | // (currentTexture + length + 1) % length 82 | // ].pause(); 83 | // }, 84 | // }); 85 | // }); 86 | 87 | navElements.forEach(nav=>{ 88 | nav.addEventListener('click',(event)=>{ 89 | let to = event.target.getAttribute('data-nav'); 90 | if(isRunning || to==currentTexture) return; 91 | var elems = document.querySelectorAll(".frame__switch-item"); 92 | [].forEach.call(elems, function(el) { 93 | el.classList.remove("frame__switch-item--current"); 94 | }); 95 | event.target.classList.add('frame__switch-item--current') 96 | isRunning = true 97 | 98 | multiTexturesPlane.uniforms.to.value = to; 99 | 100 | let fake = {progress:0} 101 | gsap.to(fake, { 102 | duration: duration, 103 | progress: 1, 104 | easing: 'power2.in', 105 | onStart: () => { 106 | multiTexturesPlane.videos[to].play(); 107 | currentTexture = to; 108 | }, 109 | onUpdate:()=>{ 110 | if(fake.progress===1){ 111 | multiTexturesPlane.uniforms.from.value = to; 112 | } 113 | multiTexturesPlane.uniforms.transitionTimer.value = fake.progress 114 | }, 115 | onComplete: () => { 116 | multiTexturesPlane.uniforms.from.value = to; 117 | multiTexturesPlane.videos[ 118 | (currentTexture + length - 1) % length 119 | ].pause(); 120 | multiTexturesPlane.videos[ 121 | (currentTexture + length + 1) % length 122 | ].pause(); 123 | isRunning = false; 124 | }, 125 | }); 126 | 127 | }) 128 | }) 129 | 130 | // click to play the videos 131 | document.getElementById("intro").addEventListener( 132 | "click", 133 | () => { 134 | // fade out animation 135 | gsap.to('#intro',{duration:0.1,autoAlpha:0.}) 136 | document.body.classList.add("video-started"); 137 | 138 | gsap.to(multiTexturesPlane.uniforms.fadeIn,{ 139 | duration: 1, 140 | value: 1 141 | }) 142 | 143 | // play all videos to force uploading the first frame of each texture 144 | multiTexturesPlane.playVideos(); 145 | 146 | // wait a tick and pause the rest of videos (the ones that are hidden) 147 | curtains.nextRender(() => { 148 | multiTexturesPlane.videos[1].pause(); 149 | multiTexturesPlane.videos[2].pause(); 150 | }); 151 | }, 152 | false 153 | ); 154 | }) 155 | .onRender(() => { 156 | timer += 0.001; 157 | multiTexturesPlane.uniforms.timer.value = timer; 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "@choojs/findup": { 6 | "version": "0.2.1", 7 | "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", 8 | "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", 9 | "dev": true, 10 | "requires": { 11 | "commander": "^2.15.1" 12 | } 13 | }, 14 | "commander": { 15 | "version": "2.20.3", 16 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 17 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 18 | "dev": true 19 | }, 20 | "core-util-is": { 21 | "version": "1.0.2", 22 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 23 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 24 | "dev": true 25 | }, 26 | "curtainsjs": { 27 | "version": "7.1.0", 28 | "resolved": "https://registry.npmjs.org/curtainsjs/-/curtainsjs-7.1.0.tgz", 29 | "integrity": "sha512-p8sy/apUOUVitdOd6BFOO9e2YDgfsNWcY73yhHp1NEdWT+a0XMQPR6T5MK3jlyg4NnLT73wqO6da93GuIBZnqg==" 30 | }, 31 | "events": { 32 | "version": "1.1.1", 33 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 34 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", 35 | "dev": true 36 | }, 37 | "glsl-inject-defines": { 38 | "version": "1.0.3", 39 | "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz", 40 | "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=", 41 | "dev": true, 42 | "requires": { 43 | "glsl-token-inject-block": "^1.0.0", 44 | "glsl-token-string": "^1.0.1", 45 | "glsl-tokenizer": "^2.0.2" 46 | } 47 | }, 48 | "glsl-resolve": { 49 | "version": "0.0.1", 50 | "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz", 51 | "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=", 52 | "dev": true, 53 | "requires": { 54 | "resolve": "^0.6.1", 55 | "xtend": "^2.1.2" 56 | }, 57 | "dependencies": { 58 | "resolve": { 59 | "version": "0.6.3", 60 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", 61 | "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=", 62 | "dev": true 63 | } 64 | } 65 | }, 66 | "glsl-token-assignments": { 67 | "version": "2.0.2", 68 | "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz", 69 | "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8=", 70 | "dev": true 71 | }, 72 | "glsl-token-defines": { 73 | "version": "1.0.0", 74 | "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz", 75 | "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=", 76 | "dev": true, 77 | "requires": { 78 | "glsl-tokenizer": "^2.0.0" 79 | } 80 | }, 81 | "glsl-token-depth": { 82 | "version": "1.1.2", 83 | "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz", 84 | "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ=", 85 | "dev": true 86 | }, 87 | "glsl-token-descope": { 88 | "version": "1.0.2", 89 | "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz", 90 | "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=", 91 | "dev": true, 92 | "requires": { 93 | "glsl-token-assignments": "^2.0.0", 94 | "glsl-token-depth": "^1.1.0", 95 | "glsl-token-properties": "^1.0.0", 96 | "glsl-token-scope": "^1.1.0" 97 | } 98 | }, 99 | "glsl-token-inject-block": { 100 | "version": "1.1.0", 101 | "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz", 102 | "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ=", 103 | "dev": true 104 | }, 105 | "glsl-token-properties": { 106 | "version": "1.0.1", 107 | "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz", 108 | "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4=", 109 | "dev": true 110 | }, 111 | "glsl-token-scope": { 112 | "version": "1.1.2", 113 | "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz", 114 | "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E=", 115 | "dev": true 116 | }, 117 | "glsl-token-string": { 118 | "version": "1.0.1", 119 | "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz", 120 | "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw=", 121 | "dev": true 122 | }, 123 | "glsl-token-whitespace-trim": { 124 | "version": "1.0.0", 125 | "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz", 126 | "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA=", 127 | "dev": true 128 | }, 129 | "glsl-tokenizer": { 130 | "version": "2.1.5", 131 | "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz", 132 | "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", 133 | "dev": true, 134 | "requires": { 135 | "through2": "^0.6.3" 136 | } 137 | }, 138 | "glslify-bundle": { 139 | "version": "5.1.1", 140 | "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz", 141 | "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==", 142 | "dev": true, 143 | "requires": { 144 | "glsl-inject-defines": "^1.0.1", 145 | "glsl-token-defines": "^1.0.0", 146 | "glsl-token-depth": "^1.1.1", 147 | "glsl-token-descope": "^1.0.2", 148 | "glsl-token-scope": "^1.1.1", 149 | "glsl-token-string": "^1.0.1", 150 | "glsl-token-whitespace-trim": "^1.0.0", 151 | "glsl-tokenizer": "^2.0.2", 152 | "murmurhash-js": "^1.0.0", 153 | "shallow-copy": "0.0.1" 154 | } 155 | }, 156 | "glslify-deps": { 157 | "version": "1.3.1", 158 | "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.1.tgz", 159 | "integrity": "sha512-Ogm179MCazwIRyEqs3g3EOY4Y3XIAa0yl8J5RE9rJC6QH1w8weVOp2RZu0mvnYy/2xIas1w166YR2eZdDkWQxg==", 160 | "dev": true, 161 | "requires": { 162 | "@choojs/findup": "^0.2.0", 163 | "events": "^1.0.2", 164 | "glsl-resolve": "0.0.1", 165 | "glsl-tokenizer": "^2.0.0", 166 | "graceful-fs": "^4.1.2", 167 | "inherits": "^2.0.1", 168 | "map-limit": "0.0.1", 169 | "resolve": "^1.0.0" 170 | } 171 | }, 172 | "graceful-fs": { 173 | "version": "4.2.4", 174 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 175 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 176 | "dev": true 177 | }, 178 | "gsap": { 179 | "version": "3.5.1", 180 | "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.5.1.tgz", 181 | "integrity": "sha512-EMV0RSUKZNeTUzLKAizGlwxVOUyif3/g8I3S1aA/hf3gbqwBvmQ02x1RdTBQNQMOpHCVBv9y/vaHwfctoAg8zw==" 182 | }, 183 | "inherits": { 184 | "version": "2.0.4", 185 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 186 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 187 | "dev": true 188 | }, 189 | "isarray": { 190 | "version": "0.0.1", 191 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 192 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 193 | "dev": true 194 | }, 195 | "map-limit": { 196 | "version": "0.0.1", 197 | "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", 198 | "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=", 199 | "dev": true, 200 | "requires": { 201 | "once": "~1.3.0" 202 | } 203 | }, 204 | "murmurhash-js": { 205 | "version": "1.0.0", 206 | "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", 207 | "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=", 208 | "dev": true 209 | }, 210 | "once": { 211 | "version": "1.3.3", 212 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", 213 | "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", 214 | "dev": true, 215 | "requires": { 216 | "wrappy": "1" 217 | } 218 | }, 219 | "path-parse": { 220 | "version": "1.0.6", 221 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 222 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 223 | "dev": true 224 | }, 225 | "readable-stream": { 226 | "version": "1.0.34", 227 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 228 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 229 | "dev": true, 230 | "requires": { 231 | "core-util-is": "~1.0.0", 232 | "inherits": "~2.0.1", 233 | "isarray": "0.0.1", 234 | "string_decoder": "~0.10.x" 235 | } 236 | }, 237 | "resolve": { 238 | "version": "1.17.0", 239 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 240 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 241 | "dev": true, 242 | "requires": { 243 | "path-parse": "^1.0.6" 244 | } 245 | }, 246 | "shallow-copy": { 247 | "version": "0.0.1", 248 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", 249 | "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=", 250 | "dev": true 251 | }, 252 | "string_decoder": { 253 | "version": "0.10.31", 254 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 255 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 256 | "dev": true 257 | }, 258 | "through2": { 259 | "version": "0.6.5", 260 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", 261 | "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", 262 | "dev": true, 263 | "requires": { 264 | "readable-stream": ">=1.0.33-1 <1.1.0-0", 265 | "xtend": ">=4.0.0 <4.1.0-0" 266 | }, 267 | "dependencies": { 268 | "xtend": { 269 | "version": "4.0.2", 270 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 271 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 272 | "dev": true 273 | } 274 | } 275 | }, 276 | "wrappy": { 277 | "version": "1.0.2", 278 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 279 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 280 | "dev": true 281 | }, 282 | "xtend": { 283 | "version": "2.2.0", 284 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", 285 | "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak=", 286 | "dev": true 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "curtainjs": "^1.0.2", 4 | "curtainsjs": "^7.1.0", 5 | "gsap": "^3.5.1", 6 | "jquery": "^3.5.1" 7 | }, 8 | "devDependencies": { 9 | "cssnano": "^4.1.10", 10 | "glslify-bundle": "^5.1.1", 11 | "glslify-deps": "^1.3.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /shaders_circle/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec3 vVertexPosition; 4 | varying vec2 vTextureCoord; 5 | varying vec2 vFirstTextureCoord; 6 | varying vec2 vSecondTextureCoord; 7 | 8 | // custom uniforms 9 | uniform float uTransitionTimer; 10 | uniform float uTimer; 11 | uniform float uTo; 12 | uniform float uFrom; 13 | 14 | // our textures samplers 15 | uniform sampler2D firstTexture; 16 | uniform sampler2D secondTexture; 17 | uniform sampler2D thirdTexture; 18 | uniform sampler2D displacement; 19 | 20 | vec4 getTextureByIndex(float index, vec2 vUv){ 21 | vec4 result; 22 | if(index==0.){ 23 | result = texture2D(firstTexture,vUv); 24 | } 25 | if(index==1.){ 26 | result = texture2D(secondTexture,vUv); 27 | } 28 | if(index==2.){ 29 | result = texture2D(thirdTexture,vUv); 30 | } 31 | return result; 32 | } 33 | float circle (in vec2 uv, in float radius, in float sharpness) { 34 | float dist = length(uv - vec2(0.5)); 35 | return 1. - smoothstep(radius-sharpness,radius,dist); 36 | } 37 | void main() { 38 | float progress = fract(uTransitionTimer); 39 | 40 | vec2 center = vec2(0.5); 41 | vec2 centerVector = vFirstTextureCoord - center; 42 | vec2 vUv = vFirstTextureCoord; 43 | 44 | float circleProgress = circle(vTextureCoord, progress*0.9, 0.2); 45 | float ease = progress*(2. - progress); 46 | vec2 nextUV = vUv + centerVector * (circleProgress - 1.0) + centerVector * ( 1. - ease) * 0.; 47 | vec2 currentUV = vUv - centerVector * circleProgress*0.5 - centerVector * progress * 0.2; 48 | 49 | float currentTexture = mod(uFrom,3.); 50 | float nextTexture = mod(uTo, 3.); 51 | 52 | vec4 current = getTextureByIndex(currentTexture, currentUV); 53 | vec4 next = getTextureByIndex(nextTexture, nextUV); 54 | 55 | gl_FragColor = mix(current,next,circleProgress); 56 | } -------------------------------------------------------------------------------- /shaders_circle/vertex.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // default mandatory variables 4 | attribute vec3 aVertexPosition; 5 | attribute vec2 aTextureCoord; 6 | 7 | uniform mat4 uMVMatrix; 8 | uniform mat4 uPMatrix; 9 | 10 | // our texture matrices 11 | // displacement texture does not need to use them 12 | uniform mat4 firstTextureMatrix; 13 | uniform mat4 secondTextureMatrix; 14 | 15 | // custom variables 16 | varying vec3 vVertexPosition; 17 | varying vec2 vTextureCoord; 18 | varying vec2 vFirstTextureCoord; 19 | varying vec2 vSecondTextureCoord; 20 | 21 | // custom uniforms 22 | uniform float uTransitionTimer; 23 | 24 | void main() { 25 | gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); 26 | 27 | // varyings 28 | // use original texture coords for our displacement 29 | vTextureCoord = aTextureCoord; 30 | // use texture matrices for our videos 31 | vFirstTextureCoord = (firstTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy; 32 | vSecondTextureCoord = (secondTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy; 33 | vVertexPosition = aVertexPosition; 34 | } -------------------------------------------------------------------------------- /videos/1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/1.mp4 -------------------------------------------------------------------------------- /videos/2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/2.mp4 -------------------------------------------------------------------------------- /videos/3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/3.mp4 -------------------------------------------------------------------------------- /videos/4.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/4.mp4 -------------------------------------------------------------------------------- /videos/5.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/5.mp4 -------------------------------------------------------------------------------- /videos/6.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/6.mp4 -------------------------------------------------------------------------------- /videos/7.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/7.mp4 -------------------------------------------------------------------------------- /videos/averyimportantvideo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/videoTransitions/9b214109e8b9e5764e0cb57fff9149d135c2b79a/videos/averyimportantvideo.mp4 --------------------------------------------------------------------------------