GIF effect
using plain image, video element and mix-blend-mode
by Konstantin Rouda
HTML
<!-- <section class="c-container">
<img src="https://farm1.staticflickr.com/798/40381373534_ae61b918db_o.jpg" width="1920" height="1280" alt="girl in winter jacket_1920">
<video class="video-elem" id='myVideo' autoplay loop muted>
<source src="//konrud.com/external/video/snow_particles_22s_1080p.mp4" type="video/mp4" >
</video>
</section> -->
<main class="c-main">
<!-- Snow/Rain -->
<section id="main-container" class="c-container">
<header class="c-container__header"><h3 class="c-container__title">Snow Effect</h3></header>
<article class="c-container__content">
<video class="c-video-elem has-snow-effect" id='snow-video-layer' loop muted playsinline>
<source src="//konrud.com/external/video/snowflakes_falling_1080p.mp4" type="video/mp4" >
</video>
<video class="c-video-elem has-rain-effect" id='rain-video-layer' loop muted playsinline>
<source src="//konrud.com/external/video/lightning_n_rain_11s_1080p.mp4" type="video/mp4" >
</video>
<img src="https://farm1.staticflickr.com/798/40381373534_ae61b918db_o.jpg" class="o-image-layer c-container__img" width="1920" height="1280" alt="girl in winter jacket_1920">
</article>
<footer class="c-container__footer">
<div id="effects-controllers-container" class="c-container__controls c-controls">
<label for="snow-controller" id="snow-controller-wrapper" class="c-controller-label c-controls__snow-controller-wrapper">
<input type="radio" class="c-controller o-radio js-snowController c-controls__snow-controller" id="snow-controller" name="controller" value="snow" />
<span class="c-controller-text">Let it snow</span>
</label>
<label for="rain-controller" id="rain-controller-wrapper" class="c-controller-label c-controls__rain-controller-wrapper">
<input type="radio" class="c-controller o-radio js-rainController...
CSS
HTML {
/*using system font-stack*/
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 115%; /*~18px*/
font-size: calc(16px + (30 - 16) * (100vw - 300px) / (1300 - 300) );
line-height: 1.5;
box-sizing: border-box;
}
BODY {
margin: 0;
color: #3a3d40;
background-color: rgb(252, 252, 252);
}
*, *::before, *::after {
box-sizing: inherit;
color: inherit;
}
IMG {
max-width: 100%;
height: 100%;
}
/*Actual Style*/
.c-main {
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
padding: 1rem;
/* background: red; */
}
.o-image-layer {
vertical-align: bottom;
}
.c-container__img {
}
/*
.o-radio {
-webkit-appearance: none;
border: 1px solid cornflowerblue;
width: 0.8rem; /!*~13px*!/
height: 0.8rem; /!*~13px*!/
border-radius: 50%;
vertical-align: -0.0625rem; /!*~-1px*!/
background: transparent;
box-shadow: 33px 0px 0px 6px rgb(1, 1, 1) inset;
transition: box-shadow 0.3s;
}
.o-radio:active {
box-shadow: -33px 0px 0px 6px rgb(1, 1, 1) inset;
transition-duration: 0s;
}
.o-radio:checked {
box-shadow: 0px 0px 0px 5px rgb(1, 1, 1) inset, 0px 0px 0px 15px rgb(18, 214, 255) inset;
box-shadow: 0px 0px 0px 5px rgb(1, 1, 1) inset, 0px 0px 0px 15px rgb(18, 214, 255) inset;
}
*/
.o-radio {
-webkit-appearance: none;
border: 1px solid cornflowerblue;
width: 0.8rem; /*~13px*/
height: 0.8rem; /*~13px*/
border-radius: 50%;
vertical-align: -0.0625rem; /*~-1px*/
background: transparent;
/* box-shadow: 33px 0px 0px 6px rgb(1, 1, 1) inset; */
box-shadow: 2.06250rem 0rem 0rem 0.37500rem rgb(1, 1, 1) inset; /*~33px ~0px ~0px ~6px rgb(1, 1, 1) inset*/
transition: box-shadow 0.3s;
}
.o-radio:active {
box-shadow: -2.06250rem/*-33px*/ 0px 0px 0.375rem/*~6px*/ rgb(1, 1, 1) inset;
transition-duration: 0s;
}
.o-radio:checked {
/* box-shadow: 0px 0px 0px 5px rgb(1, 1, 1) inset, 0px 0px 0px 15px...
JavaScript
;(function () {
"use strict";
const SNOW_CONTROLLER_JS_CLASS = "js-snowController";
const HIDDEN_CLASS = "is-hidden";
const mainContainer = document.getElementById("main-container");
const snowVideo = document.getElementById("snow-video-layer");
const rainVideo = document.getElementById("rain-video-layer");
const effectsControllersContainer = document.getElementById("effects-controllers-container");
const snowControllerWrapper = document.getElementById("snow-controller-wrapper");
const rainControllerWrapper = document.getElementById("rain-controller-wrapper");
snowVideo.addEventListener("canplay", snowVideoLayerOnCanPlay);
rainVideo.addEventListener("canplay", rainVideoLayerOnCanPlay);
// video.classList.add(HIDDEN_CLASS);
// snowVideo.style.opacity = 0;
// rainVideo.style.opacity = 0;
document.addEventListener("DOMContentLoaded", onDOMContentLoaded);
effectsControllersContainer.addEventListener("change", effectsControllersOnChecked, true);
function onDOMContentLoaded (e) {
_hideAllVideos(mainContainer);
};
function snowVideoLayerOnCanPlay (e) {
// video.style.opacity = 1;
snowControllerWrapper.classList.remove(HIDDEN_CLASS);
};
function rainVideoLayerOnCanPlay (e) {
rainControllerWrapper.classList.remove(HIDDEN_CLASS);
};
function effectsControllersOnChecked (e) {
const controller = e.target;
debugger;
if(controller.type !== "radio" || controller.checked === false) return;
const controllerValue = controller.value;
const container = controller.closest(".c-container");
if(container == null) return;
let videoElem;
// let classListMethod = "add";
switch(controllerValue) {
case "snow":
videoElem = container.querySelector("#snow-video-layer");
break;
case "rain":
videoElem = container.querySelector("#rain-video-layer");
break;
default:
...