Add Animated Line in Mesh Animation - javascript

How could I add animated lines in this sample mesh animation?
HAML / HTML
%div.wrap
-300.times do
%div.c
CSS/SCSS
// best in chrome
$total: 300; // total particles
$orb-size: 100px;
$particle-size: 2px;
$time: 14s;
$base-hue: 0; // change for diff colors (180 is nice)
html, body {
height: 100%;
}
body {
background: black;
overflow: hidden; // no scrollbars..
}
.wrap {
position: relative;
top: 50%;
left: 50%;
width: 0;
height: 0;
transform-style: preserve-3d;
perspective: 1000px;
animation: rotate $time infinite linear; // rotate orb
}
#keyframes rotate {
100% {
transform: rotateY(360deg) rotateX(360deg);
}
}
.c {
position: absolute;
width: $particle-size;
height: $particle-size;
border-radius: 50%;
opacity: 0;
}
#for $i from 1 through $total {
$z: (random(360) * 1deg); // random angle to rotateZ
$y: (random(360) * 1deg); // random to rotateX
$hue: ((40/$total * $i) + $base-hue); // set hue
.c:nth-child(#{$i}){ // grab the nth particle
animation: orbit#{$i} $time infinite;
animation-delay: ($i * .01s);
background-color: hsla($hue, 100%, 50%, 1);
}
#keyframes orbit#{$i}{
20% {
opacity: 1; // fade in
}
30% {
transform: rotateZ(-$z) rotateY($y) translateX($orb-size) rotateZ($z); // form orb
}
80% {
transform: rotateZ(-$z) rotateY($y) translateX($orb-size) rotateZ($z); // hold orb state 30-80
opacity: 1; // hold opacity 20-80
}
100% {
transform: rotateZ(-$z) rotateY($y) translateX( ($orb-size * 3) ) rotateZ($z); // translateX * 3
}
}
}
https://codepen.io/natewiley/pen/GgONKy
Desired animation:

Related

Animated elements causing overflow

I'm trying to randomly generate some circles with javascript that animate around the edges of a page. My issue is that they cause horizontal scrolling, interestingly they only cause the right side to overflow. I've tried putting overflow-x: hidden; on the body; on which I'm appending said circles, but it doesn't seem to work.
I'm not super well-versed in javascript, so I'm certain it has something to do with how I'm generating/positioning the circles, but I can't figure it out, and all the threads I can find simply say to out overflow: hidden onto the container, which doesn't seem to work.
Update: The issue seems more persistent on Safari, as I've been able to eliminate it on Chrome simply by making sure no circles are placed outside the clientWidth. I'm still quite confused by the fact that the issue is only occurring on the right side.
My code:
var numberOfBalls = 10;
for (var i = 0; i < numberOfBalls; i++) {
var circle = document.createElement('div');
circle.classList.add('bg-circle');
var circleWidth = Math.floor(Math.random() * 200) + 80;
circle.style.width = circleWidth + 'px';
circle.style.animationDuration = Math.floor(Math.random() * 34) + 28 + 's';
circle.style.animationDelay = i * 200 + 'ms';
circle.style.top = Math.floor(Math.random() * (document.body.clientHeight - circleWidth * 2)) + 'px';
if (Math.random() > 0.5) {
circle.style.left = document.body.clientWidth + 'px';
} else {
circle.style.left = 0 - circleWidth + 'px';
}
document.body.appendChild(circle);
}
body {
overflow-x: hidden;
height: 100vh;
padding: 0;
margin: 0;
}
.bg-circle {
top: 0;
left: 0;
animation-delay: 0s;
width: 200px;
aspect-ratio: 1 / 1;
border-radius: 50%;
background: rgba(235, 88, 63);
position: absolute;
z-index: -1;
}
#media (prefers-reduced-motion: no-preference) {
.bg-circle {
animation: wiggle ease-in-out infinite forwards;
}
}
/*Commented out this style for renderings*/
/*#media screen and (max-width: 1800px) {
.bg-circle {
display: none;
}
}*/
.bg-circle:nth-child(odd) {
animation-direction: reverse;
}
#keyframes wiggle {
0% {
transform: translate(0%, 0%);
}
20% {
transform: translate(140%, -20%)
}
40% {
transform: translate(120%, -80%)
}
60% {
transform: translate(-90%, 180%)
}
80% {
transform: translate(-130%, -80%);
}
100% {
transform: translate(0%, 0%);
}
}

How to conform top glare to all child elements

Goal
Make the glare layer on all visible children like a clip-path
––––––––––––
Heads Up
Child elements can be any shape with any animation
Child elements can be any svg shape with any kind of animation attached to it
So glare must be automatically dynamic and conforming
––––––––––––
What I've Done
I create an apple TV effect…
But the glare only works as a box on top of other boxes.
The glare does not conform to other shapes
Example Below
––––––––––––
What I Can't Use
Canvas - No Canvas Please - I'm not familiar with it
Clip-Path - Because child elements can be anything overflowing outside of the glare
––––––––––––
What I'm looking for
Some kind of magical CSS line of code that makes the glare layer conform to all elements under it… like a normal glare would work.
Is this possible?
Is there some way Javascript can glare it automatically?
Is there some kind of mix-blend-mode I can use to make the glare just work?
Or is this something that is just impossible?
Glare should not look like a box
––––––––––––
What I tried
I tried to scale the glare layer to scale(1.1) and use some mix-blend-mode
But I couldn't figure out how to make it work.
appleTV();
function appleTV(){
appleTVComponents = 0;
function rotateX(n) {return ' rotateX('+n+'deg)'}
function rotateY(n) {return ' rotateY('+n+'deg)'}
function translateX(n) {return ' translateX('+n+'px)'}
function translateY(n) {return ' translateY('+n+'px)'}
function perspective(n) {return 'perspective('+n+'px)'}
function scale(n) {return ' scale3d('+n+','+n+','+n+')'}
function section(s='',e) {e=document.createElement('section');e.className='appletv_'+s;return e;}
function getWidth(e) {return e.clientWidth || e.offsetWidth || e.scrollWidth}
function setPerspective(e) {e.style.transform = perspective(getWidth(e)*3);}
function preventScroll(state) {if(supportsTouch){win.preventScroll=state||false;}}
function preventDefault(e) {if (supportsTouch&&win.preventScroll){e.preventDefault();}}
function isTouchScreen() {return 'ontouchstart' in window || navigator.msMaxTouchPoints}
function child(e) {return e.firstChild;}
function children(e) {return [...e.children]}
let body = document.body,
win = window,
imgs = document.querySelectorAll('.appletv'),
totalImgs = imgs.length,
supportsTouch = isTouchScreen(),
move = 'mousemove',
start = 'mouseenter',
end = 'mouseleave';
if(supportsTouch){move='touchmove'; start='touchstart'; end='touchend';}
if(totalImgs <= 0){return;}
for(var l=0;l<totalImgs;l++){
var thisImg = imgs[l],
layerElems = [...thisImg.querySelectorAll('.appletv_layer')];
if(!layerElems.length){continue;}
while(thisImg.firstChild) {thisImg.removeChild(thisImg.firstChild);}
var containerHTML = section(''),
shineHTML = section('gloss'),
shadowHTML = section('shadow'),
layersHTML = section('layer'),
layers = [];
thisImg.id = 'appletv_'+(++appleTVComponents);
layerElems.forEach((e,i)=>{
let layer_ = section('rendered_layer')
layer = section(''),
img = e.getAttribute('data-img');
layer_.setAttribute('data-layer',i);
[...e.children].forEach(c=>{layer.appendChild(c)})
if (img) {layer.style.backgroundImage = 'url('+img+')';}
layer_.appendChild(layer);
layersHTML.appendChild(layer_);
layers.push(layer);
});
[shadowHTML,layersHTML,shineHTML].forEach(e=>{containerHTML.appendChild(e)});
thisImg.appendChild(containerHTML);
var w = getWidth(thisImg);
setPerspective(thisImg)
preventScroll();
(function enableMovements(_thisImg,_layers,_totalLayers,_shine) {
thisImg.addEventListener(move, e=>{processMovement(e,supportsTouch,_thisImg,_layers,_totalLayers,_shine);});
thisImg.addEventListener(start, e=>{processEnter(_thisImg);});
thisImg.addEventListener(end, e=>{processExit(_thisImg,_layers,_totalLayers,_shine);});
})(thisImg,layers,layerElems.length,shineHTML);
};
function processMovement(e, touchEnabled, elem, layers, totalLayers, shine){
preventDefault(e)
let bdst = body.scrollTop,
bdsl = body.scrollLeft,
pageX = (touchEnabled)? e.touches[0].pageX : e.pageX,
pageY = (touchEnabled)? e.touches[0].pageY : e.pageY,
offsets = elem.getBoundingClientRect(),
w = elem.clientWidth || elem.offsetWidth || elem.scrollWidth, // width
h = elem.clientHeight || elem.offsetHeight || elem.scrollHeight, // height
wMultiple = 320/w,
offsetX = 0.52 - (pageX - offsets.left - bdsl)/w, //cursor position X
offsetY = 0.52 - (pageY - offsets.top - bdst)/h, //cursor position Y
dy = (pageY - offsets.top - bdst) - h / 2, //#h/2 = center of container
dx = (pageX - offsets.left - bdsl) - w / 2, //#w/2 = center of container
yRotate = (offsetX - dx)*(0.07 * wMultiple), //rotation for container Y
xRotate = (dy - offsetY)*(0.1 * wMultiple), //rotation for container X
imgCSS = rotateX(xRotate)+rotateY(yRotate), //img transform
arad = Math.atan2(dy, dx), //angle between cursor and center of container in RAD
angle = arad * 180 / Math.PI - 90; //convert rad in degrees
if (angle < 0) {angle = angle + 360;}
if(elem.firstChild.className.indexOf(' over') != -1){imgCSS += scale(1.07);}
elem.firstChild.style.transform = imgCSS;
shine.style.background = 'linear-gradient(' + angle + 'deg, rgba(255,255,255,' + (pageY - offsets.top - bdst)/h * 0.4 + ') 0%,rgba(255,255,255,0) 80%)';
shine.style.transform = translateX((offsetX * totalLayers) - 0.1)+translateY((offsetY * totalLayers) - 0.1);
var revNum = totalLayers;
for(var ly=0;ly<totalLayers;ly++){
layers[ly].style.transform = translateX((offsetX * revNum) * ((ly * 2.5) / wMultiple))+translateX((offsetY * totalLayers) * ((ly * 2.5) / wMultiple));
revNum--;
}
}
function processEnter(e){preventScroll(true);setPerspective(e);child(e)&&child(e).classList.add('over');}
function processExit(elem, layers, totalLayers, shine){preventScroll();
child(elem).classList.remove('over')
child(elem).style.transform = '';
shine.style = '';
layers.forEach(e=>{e.style.transform = ''})
}
}
body,
html {
height: 100%;
min-height: 100%;
}
body {background: linear-gradient(to bottom, #f6f7fc 0%, #d5e1e8 40%);}
.center{
position: absolute;
left: 50%;
margin: 10px auto;
transform: translateX(-50%);
}
.appletv {
position: relative !important;
margin: 0 auto !important;
display: inline-block;
width: 300px;
height: 150px;
border-radius: 5px;
transform-style: preserve-3d;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
cursor: pointer;
backface-visibility: hidden;
}
.appletv.depressed {
margin-top: 25px;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.4);
}
.appletv_ {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
transition: all 0.2s ease-out;
background: teal;
}
.appletv_container.over {z-index: 1;}
.appletv_container.over .appletv_shadow {box-shadow: 0 45px 100px rgba(14, 21, 47, 0.4), 0 16px 40px rgba(14, 21, 47, 0.4);}
.appletv_layer {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
/*overflow: hidden;*/
transform-style: preserve-3d;
}
.appletv_rendered_layer {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
overflow: hidden;
border-radius: 5px;
transition: all 0.1s ease-out;
transform-style: preserve-3d;
}
.appletv_rendered_layer > :first-child {
position: absolute;
width: 104%;
height: 104%;
top: -2%;
left: -2%;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
background-size: cover;
transition: all 0.1s ease-out;
}
.appletv_shadow {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
transition: all 0.2s ease-out;
box-shadow: 0 8px 30px rgba(14, 21, 47, 0.6);
}
.appletv_gloss {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 5px;
/*display: none !important;*/
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 40%);
}
[data-layer="1"] {overflow: visible !important;}
[data-layer="1"] > section > section {
position: absolute;
background: rgb(50, 141, 210);
width: 60px;
height: 60px;
border-radius: 10px;
}
[data-layer="1"] > section > section:first-child {
left: -30px;
top: -10px;
}
[data-layer="1"] > section > section:last-child {
right: -20px;
top: 50px;
}
#keyframes rotate {
0% {transform: rotate(0);}
100% {transform: rotate(359deg);}
}
.appletv_gloss {
/*display: none;*/
background-blend-mode: multiply;
}
.appletv [data-layer="1"] {
transform: scale(0.5);
transition: .3s ease-in-out 0s;
}
.appletv:hover [data-layer="1"] {
transform: scale(1);
}
.appletv:hover [data-layer="1"] > section > section {
animation: rotate 10s linear 0s infinite;
}
.appletv:hover [data-layer="1"] > section > section:last-child {
animation: rotate 25s linear 0s infinite;
}
#hover {
font-size: 30px;
position: absolute;
top: 37%;
text-align: center;
width: 100%;
color: white;
text-shadow: 0 2px 2px rgba(0,0,0,0.3) ;
}
<html>
<body>
<section class="center">
<section class="appletv">
<section class="appletv appletv_layer" data-img="https://source.unsplash.com/random">
<section id="hover">Hover Corners</section>
</section>
<section class="appletv appletv_layer">
<section></section>
<section></section>
</section>
</section>
</section>
</body>
</html>

Confetti falling animation problem transparencies

Good evening all,
i added the confetti falling animation to my website.
The confetti are superimposed on my buttons and i can't click them, how can i make the confetti visible but transparent so that i can click what's underneath?
This is the code that i used:
<canvas id="my-canvas"></canvas>
<script src="assets/index.min.js"></script>
<script> var confettiSettings = { target: 'my-canvas' };
var confetti = new ConfettiGenerator(confettiSettings);
confetti.render();
</script>
<style>
#my-canvas
{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: 100000;
}
</style>
You can use the pointer-events CSS property to control some aspects of how the pointer interacts with an element. Setting pointer-events: none; will let pointer events (like hover and click) pass right through an element as though it wasn't there. Here's some documentation: pointer-events
here I hope this helps:
index.html
<div class="js-container container"></div>
index.css
#keyframes confetti-slow {
0% { transform: translate3d(0, 0, 0) rotateX(0) rotateY(0); }
100% { transform: translate3d(25px, 105vh, 0) rotateX(360deg) rotateY(180deg); }
}
#keyframes confetti-medium {
0% { transform: translate3d(0, 0, 0) rotateX(0) rotateY(0); }
100% { transform: translate3d(100px, 105vh, 0) rotateX(100deg) rotateY(360deg); }
}
#keyframes confetti-fast {
0% { transform: translate3d(0, 0, 0) rotateX(0) rotateY(0); }
100% { transform: translate3d(-50px, 105vh, 0) rotateX(10deg) rotateY(250deg); }
}
.container {
width: 100vw;
height: 100vh;
background: #f0f0f0;
}
.confetti-container {
perspective: 700px;
position: absolute;
overflow: hidden;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.confetti {
position: absolute;
z-index: 1;
top: -10px;
border-radius: 0%;
&--animation-slow {
animation: confetti-slow 2.25s linear 1 forwards;
}
&--animation-medium {
animation: confetti-medium 1.75s linear 1 forwards;
}
&--animation-fast {
animation: confetti-fast 1.25s linear 1 forwards;
}
}
script file
const Confettiful = function(el) {
this.el = el;
this.containerEl = null;
this.confettiFrequency = 3;
this.confettiColors = ['#fce18a', '#ff726d', '#b48def', '#f4306d'];
this.confettiAnimations = ['slow', 'medium', 'fast'];
this._setupElements();
this._renderConfetti();
};
Confettiful.prototype._setupElements = function() {
const containerEl = document.createElement('div');
const elPosition = this.el.style.position;
if (elPosition !== 'relative' || elPosition !== 'absolute') {
this.el.style.position = 'relative';
}
containerEl.classList.add('confetti-container');
this.el.appendChild(containerEl);
this.containerEl = containerEl;
};
Confettiful.prototype._renderConfetti = function() {
this.confettiInterval = setInterval(() => {
const confettiEl = document.createElement('div');
const confettiSize = (Math.floor(Math.random() * 3) + 7) + 'px';
const confettiBackground = this.confettiColors[Math.floor(Math.random() * this.confettiColors.length)];
const confettiLeft = (Math.floor(Math.random() * this.el.offsetWidth)) + 'px';
const confettiAnimation = this.confettiAnimations[Math.floor(Math.random() * this.confettiAnimations.length)];
confettiEl.classList.add('confetti', 'confetti--animation-' + confettiAnimation);
confettiEl.style.left = confettiLeft;
confettiEl.style.width = confettiSize;
confettiEl.style.height = confettiSize;
confettiEl.style.backgroundColor = confettiBackground;
confettiEl.removeTimeout = setTimeout(function() {
confettiEl.parentNode.removeChild(confettiEl);
}, 3000);
this.containerEl.appendChild(confettiEl);
}, 25);
};
window.confettiful = new Confettiful(document.querySelector('.js-container'));
Here is a link of a Codepen.io: https://codepen.io/jacobgunnarsson/pen/pbPwga

Continue animation where ended

I have a 3D cube that completes one of 2 animations depending on what number is selected by math.random. The animation holds its end position after it has ended (due to "forwards") but if the cube is clicked, to run the animation again, it returns back to its original position. How to I make the cube complete the animation starting from the last time's ending position?
.scene {
perspective:200px;
width:100px;
height:100px;
}
.die {
position:relative;
width:100%;
height:100%;
transform-style:preserve-3d;
transform: translateZ(-50px);
transition: transform 1s;
animation:;
}
.face {
position:absolute;
width:100px;
height:100px;
color:white;
top:0;
bottom:0;
left:0;
right:0;
background-color:purple;
}
.one {
transform: rotateY(0deg) translateZ(50px);
}
.two {
transform:rotateY(90deg) translateZ(50px);
}
.three {
transform:rotateY(180deg) translateZ(50px);
}
.four {
transform:rotateY(-90deg) translateZ(50px);
}
.five {
transform:rotateX(90deg) translateZ(50px);
}
.six {
transform:rotateX(-90deg) translateZ(50px);
}
#keyframes one {
0% {transform: translateZ(-5em) rotateY(0deg) rotateX(0deg);}
100% {transform: translateZ(-5em) rotateY(0deg) rotateX(360deg);}
}
#keyframes two {
0% {transform: translateZ(-5em) rotateY(0deg) rotateX(0deg);}
100% {transform: translateZ(-5em) rotateY(-90deg) rotateX(360deg);}
}
<div class="scene">
<div class="die" id="die" onclick="spinDie()">
<div class="face one">one
</div>
<div class="face two">two
</div>
<div class="face three">three
</div>
<div class="face four">four
</div>
<div class="face five">five
</div>
<div class="face six">six
</div>
</div>
</div>
</div>
function spinDie() {
var num = Math.floor(Math.random() * 1) + 1;
if (num === 1) {
document.getElementById("die").style.animation="one 2s forwards"
}
if(num === 2) {
document.getElementById("die").style.animation="two 2s forwards"
}
}
Use transition instead of animation.
Define the transitions inside an Array faces
Get a random face {x: N, y: N}
Addup += spins to the current random face x and y valuex. I.e: x += (360 * randomBetween(n, N))
const EL_dice = document.getElementById("dice");
// https://en.wikipedia.org/wiki/Dice
const faces = [
{x:0, y:0}, // 1 front
{x:0, y:-90}, // 2 right
{x:-90, y:0}, // 3 top
{x:90, y:0}, // 4 bottom
{x:0, y:90}, // 5 left
{x:0, y:180}, // 6 back
];
function spinDice() {
const rand = ~~(Math.random() * 6); // Generate random 0 to 5
const face = faces[rand]; // Get random face
face.x += 360 * (~~(Math.random() * 4) + 1); // Addup some x spins
face.y += 360 * (~~(Math.random() * 4) + 1); // Addup some y spins
console.clear(); console.log(rand + 1);
EL_dice.style.cssText = `
transition: 3s cubic-bezier(0.2, -0.2, 0.5, 1.1);
transform: rotateX(${face.x}deg) rotateY(${face.y}deg);
`;
}
EL_dice.addEventListener("click", spinDice);
.dice-perspective {
display: inline-flex;
perspective: 500px;
margin: 20px;
user-select: none;
}
.dice {
--size: 80px; /* SET HERE THE DESIRED DICE SIZE */
--half: calc(var(--size) / 2);
width: var(--size); height: var(--size);
position: relative;
transform-style: preserve-3d;
}
.dice>div {
height: inherit; width: inherit;
position: absolute;
background: #0bf; color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: calc(var(--size) * 1.4);
line-height: 0;
}
.front {transform: rotateY(0deg) translateZ(var(--half));}
.right {transform: rotateY(90deg) translateZ(var(--half));}
.top {transform: rotateX(90deg) translateZ(var(--half));}
.bottom {transform: rotateX(-90deg) translateZ(var(--half));}
.left {transform: rotateY(-90deg) translateZ(var(--half));}
.back {transform: rotateX(180deg) translateZ(var(--half));}
Click dice to spin
<div class="dice-perspective">
<div id="dice" class="dice">
<div class="front">⚀</div>
<div class="right">⚁</div>
<div class="top">⚂</div>
<div class="bottom">⚃</div>
<div class="left">⚄</div>
<div class="back">⚅</div>
</div>
</div>

How to create Ripple effect on Click - Material Design

I'm new to CSS animations and I've been trying to make their animation work for the last hours by looking at their code, but I can't make it work for now.
I'm talking about this effect: https://angular.io/ (menu effect).
Basically, it's an animation on click that spreads a circle from the mouse cursor.
Seems it comes down to these 2 lines:
transition: box-shadow .4s cubic-bezier(.25,.8,.25,1),background-color .4s cubic-bezier(.25,.8,.25,1),-webkit-transform .4s cubic-bezier(.25,.8,.25,1);
transition: box-shadow .4s cubic-bezier(.25,.8,.25,1),background-color .4s cubic-bezier(.25,.8,.25,1),transform .4s cubic-bezier(.25,.8,.25,1);
PS: Maybe there's some jQuery I didn't see.
Ripple effect in Material Design using jQuery and CSS3
To create a UX Ripple effect basically you need to:
append to any element an oveflow:hidden element to contain the ripple circle (you don't want to alter your original element overflow, neither see the ripple effect go outside of a desired container)
append to the overflow container the ripple wave translucent radial element
get the click coordinates and CSS3 animate the scaling and opacity of the ripple element
Listen for the animationend event and destroy the ripple container.
The basic code:
Basically add data-ripple (default as white ripple) or data-ripple="#000" to a desired element:
<a data-ripple> EDIT </a>
<div data-ripple="rgba(0,0,0, 0.3)">Lorem ipsum</div>
CSS:
/* MAD-RIPPLE EFFECT */
.ripple{
position: absolute;
top:0; left:0; bottom:0; right:0;
overflow: hidden;
-webkit-transform: translateZ(0); /* to contain zoomed ripple */
transform: translateZ(0);
border-radius: inherit; /* inherit from parent (rounded buttons etc) */
pointer-events: none; /* allow user interaction */
animation: ripple-shadow 0.4s forwards;
-webkit-animation: ripple-shadow 0.4s forwards;
}
.rippleWave{
backface-visibility: hidden;
position: absolute;
border-radius: 50%;
transform: scale(0.7); -webkit-transform: scale(0.7);
background: rgba(255,255,255, 1);
opacity: 0.45;
animation: ripple 2s forwards;
-webkit-animation: ripple 2s forwards;
}
#keyframes ripple-shadow {
0% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
20% {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
#-webkit-keyframes ripple-shadow {
0% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
20% {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
#keyframes ripple {
to {transform: scale(24); opacity:0;}
}
#-webkit-keyframes ripple {
to {-webkit-transform: scale(24); opacity:0;}
}
jQuery
jQuery(function($) {
// MAD-RIPPLE // (jQ+CSS)
$(document).on("mousedown", "[data-ripple]", function(e) {
var $self = $(this);
if($self.is(".btn-disabled")) {
return;
}
if($self.closest("[data-ripple]")) {
e.stopPropagation();
}
var initPos = $self.css("position"),
offs = $self.offset(),
x = e.pageX - offs.left,
y = e.pageY - offs.top,
dia = Math.min(this.offsetHeight, this.offsetWidth, 100), // start diameter
$ripple = $('<div/>', {class : "ripple",appendTo : $self });
if(!initPos || initPos==="static") {
$self.css({position:"relative"});
}
$('<div/>', {
class : "rippleWave",
css : {
background: $self.data("ripple"),
width: dia,
height: dia,
left: x - (dia/2),
top: y - (dia/2),
},
appendTo : $ripple,
one : {
animationend : function(){
$ripple.remove();
}
}
});
});
});
Here's a full-featured demo:
jQuery(function($) {
// MAD-RIPPLE // (jQ+CSS)
$(document).on("mousedown", "[data-ripple]", function(e) {
var $self = $(this);
if($self.is(".btn-disabled")) {
return;
}
if($self.closest("[data-ripple]")) {
e.stopPropagation();
}
var initPos = $self.css("position"),
offs = $self.offset(),
x = e.pageX - offs.left,
y = e.pageY - offs.top,
dia = Math.min(this.offsetHeight, this.offsetWidth, 100), // start diameter
$ripple = $('<div/>', {class : "ripple",appendTo : $self });
if(!initPos || initPos==="static") {
$self.css({position:"relative"});
}
$('<div/>', {
class : "rippleWave",
css : {
background: $self.data("ripple"),
width: dia,
height: dia,
left: x - (dia/2),
top: y - (dia/2),
},
appendTo : $ripple,
one : {
animationend : function(){
$ripple.remove();
}
}
});
});
});
*{box-sizing:border-box; -webkit-box-sizing:border-box;}
html, body{height:100%; margin:0;}
body{background:#f5f5f5; font: 14px/20px Roboto, sans-serif;}
h1, h2{font-weight: 300;}
/* MAD-RIPPLE EFFECT */
.ripple{
position: absolute;
top:0; left:0; bottom:0; right:0;
overflow: hidden;
-webkit-transform: translateZ(0); /* to contain zoomed ripple */
transform: translateZ(0);
border-radius: inherit; /* inherit from parent (rounded buttons etc) */
pointer-events: none; /* allow user interaction */
animation: ripple-shadow 0.4s forwards;
-webkit-animation: ripple-shadow 0.4s forwards;
}
.rippleWave{
backface-visibility: hidden;
position: absolute;
border-radius: 50%;
transform: scale(0.7); -webkit-transform: scale(0.7);
background: rgba(255,255,255, 1);
opacity: 0.45;
animation: ripple 2s forwards;
-webkit-animation: ripple 2s forwards;
}
#keyframes ripple-shadow {
0% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
20% {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
#-webkit-keyframes ripple-shadow {
0% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
20% {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
#keyframes ripple {
to {transform: scale(24); opacity:0;}
}
#-webkit-keyframes ripple {
to {-webkit-transform: scale(24); opacity:0;}
}
/* MAD-BUTTONS (demo) */
[class*=mad-button-]{
display:inline-block;
text-align:center;
position: relative;
margin: 0;
white-space: nowrap;
vertical-align: middle;
font-family: "Roboto", sans-serif;
font-size: 14px;
font-weight: 500;
text-transform: uppercase;
text-decoration: none;
border: 0; outline: 0;
background: none;
transition: 0.3s;
cursor: pointer;
color: rgba(0,0,0, 0.82);
}
[class*=mad-button-] i.material-icons{
vertical-align:middle;
padding:0;
}
.mad-button-raised{
height: 36px;
padding: 0px 16px;
line-height: 36px;
border-radius: 2px;
box-shadow: /*amb*/ 0 0 2px rgba(0,0,0,0.15),
/*key*/ 0 1px 3px rgba(0,0,0,0.25);
}.mad-button-raised:hover{
box-shadow: /*amb*/ 0 0 2px rgba(0,0,0,0.13),
/*key*/ 0 2px 4px rgba(0,0,0,0.2);
}
.mad-button-action{
width: 56px; height:56px;
padding: 16px 0;
border-radius: 32px;
box-shadow: /*amb*/ 0 0 2px rgba(0,0,0,0.13),
/*key*/ 0 5px 7px rgba(0,0,0,0.2);
}.mad-button-action:hover{
box-shadow: /*amb*/ 0 0 2px rgba(0,0,0,0.11),
/*key*/ 0 6px 9px rgba(0,0,0,0.18);
}
[class*=mad-button-].mad-ico-left i.material-icons{ margin: 0 8px 0 -4px; }
[class*=mad-button-].mad-ico-right i.material-icons{ margin: 0 -4px 0 8px; }
/* MAD-COLORS */
.bg-primary-darker{background:#1976D2; color:#fff;}
.bg-primary{ background:#2196F3; color:#fff; }
.bg-primary.lighter{ background: #BBDEFB; color: rgba(0,0,0,0.82);}
.bg-accented{ background:#FF4081; color:#fff; }
/* MAD-CELL */
.cell{padding: 8px 16px; overflow:auto;}
<link href='https://fonts.googleapis.com/css?family=Roboto:500,400,300&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div class="cell">
<button data-ripple class="mad-button-raised mad-ico-left bg-primary"><i class="material-icons">person</i>User settings</button>
<a data-ripple href="#" class="mad-button-action bg-accented"><i class="material-icons">search</i></a>
</div>
<div data-ripple class="cell bg-primary-darker">
<h1>Click to Ripple</h1>
<p>data-ripple</p>
</div>
<div data-ripple="rgba(0,0,0, 0.4)" class="cell bg-primary">
<p>data-ripple="rgba(0,0,0, 0.4)"</p>
<p> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore....</p>
<p><a data-ripple class="mad-button-raised mad-ico-right bg-accented">Edit<i class="material-icons">edit</i></a></p>
</div>
I have used this sort of code before on a few of my projects.
Using jQuery we can position the effect to its not just static and then we add the span element onclick. I have added comments so it makes it easier to follow.
Demo Here
jQuery
$("div").click(function (e) {
// Remove any old one
$(".ripple").remove();
// Setup
var posX = $(this).offset().left,
posY = $(this).offset().top,
buttonWidth = $(this).width(),
buttonHeight = $(this).height();
// Add the element
$(this).prepend("<span class='ripple'></span>");
// Make it round!
if(buttonWidth >= buttonHeight) {
buttonHeight = buttonWidth;
} else {
buttonWidth = buttonHeight;
}
// Get the center of the element
var x = e.pageX - posX - buttonWidth / 2;
var y = e.pageY - posY - buttonHeight / 2;
// Add the ripples CSS and start the animation
$(".ripple").css({
width: buttonWidth,
height: buttonHeight,
top: y + 'px',
left: x + 'px'
}).addClass("rippleEffect");
});
CSS
.ripple {
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
position: absolute;
opacity: 1;
}
.rippleEffect {
animation: rippleDrop .6s linear;
}
#keyframes rippleDrop {
100% {
transform: scale(2);
opacity: 0;
}
}
This can be achieved with box-shadows. The positioning of the circle origin under the mouse when clicked will need JS.
li{
font-size:2em;
background:rgba(51, 51, 254, 0.8);
list-style-type:none;
display:inline-block;
line-height:2em;
width:6em;
text-align:center;
color:#fff;
position:relative;
overflow:hidden;
}
a{color:#fff;}
a:after{
content:'';
position:absolute;
border-radius:50%;
height:10em; width:10em;
top: -4em; left:-2em;
box-shadow: inset 0 0 0 5em rgba(255,255,255,0.2);
transition: box-shadow 0.8s;
}
a:focus:after{
box-shadow: inset 0 0 0 0em rgba(255,255,255,0.2);
}
<ul>
<li>button</li>
</ul>
Here is a CSS - only implementation i.e. no javascript required.
Source: https://ghinda.net/article/css-ripple-material-design/
body {
background: #fff;
}
button {
position: relative;
overflow: hidden;
padding: 16px 32px;
}
button:after {
content: '';
display: block;
position: absolute;
left: 50%;
top: 50%;
width: 120px;
height: 120px;
margin-left: -60px;
margin-top: -60px;
background: #3f51b5;
border-radius: 100%;
opacity: .6;
transform: scale(0);
}
#keyframes ripple {
0% {
transform: scale(0);
}
20% {
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(1);
}
}
button:not(:active):after {
animation: ripple 1s ease-out;
}
/* fixes initial animation run, without user input, on page load.
*/
button:after {
visibility: hidden;
}
button:focus:after {
visibility: visible;
}
<button>
Button
</button>
You could use http://mladenplavsic.github.io/css-ripple-effect/ (note: I'm the author of that product)
Pure CSS solution
<link href="https://cdn.rawgit.com/mladenplavsic/css-ripple-effect/35c35541/dist/ripple.min.css" rel="stylesheet"/>
<button class="ripple">Click me</button>
You can get the same effect with the help of Materialize css, making it with that is quite easy. All you have to do is just add a class to where you want the effect.
Submit
If you want to go with pure CSS check this codepen it : Ripple effect
Here is Material Design button component "The wave effect" Done Using pure CSS3 and JavaScript no libraries no framework
Material Design button component "The wave effect"
https://codepen.io/Mahmoud-Zakaria/pen/NvbORQ
HTML
<div class="md" >Click</div>
CSS
#keyframes glow-out {
30%,80% {
transform: scale(7);
}
100% {
opacity: 0;
}
}
.md {
--y: 0;
--x: 0;
display: inline-block;
padding: 20px 70px;
text-align: center;
background-color: lightcoral;
margin: 5em;
position: relative;
overflow: hidden;
cursor: pointer;
border-radius: 4px;
color: white;
}
.is-clicked {
content: '';
position: absolute;
top: calc(var(--y) * 1px);
left: calc(var(--x) * 1px);
width: 100px;
height:100px;
background: rgba(255, 255, 255, .3);
border-radius: 50%;
animation: glow-out 1s ease-in-out forwards;
transform: translate(-50%, -50%);
}
JS
// Material Design button Module
let md_module = (function() {
let btn = document.querySelectorAll(".md");
let md_btn = Array.prototype.slice.call(btn);
md_btn.forEach(eachCB)
function eachCB (item, index, array){
function md(e) {
let offsetX = e.clientX - item.offsetLeft;
let offsetY = e.clientY - item.offsetTop;
item.style.setProperty("--x", offsetX);
item.style.setProperty("--y", offsetY);
item.innerHTML += '<div class="is-clicked"></div>';
}
function rm() {
let state = item.querySelectorAll(".is-clicked");
console.log(state)
for (let i = 0; i < state.length; i++) {
if (state[i].className === "is-clicked") {
state[i].remove();
}
}
}
item.addEventListener("click", md);
item.addEventListener("animationend", rm);
}
})();
CSS Paint API (introduced in 2018)
The new CSS Paint API (part of the CSS "Houdini" draft) allows to write JavaScript functions to be used in CSS. Quote of the linked document:
CSS Paint API allows you to programmatically generate an image whenever a CSS property expects an image. Properties like background-image or border-image are usually used with url() to load an image file or with CSS built-in functions like linear-gradient(). Instead of using those, you can now use paint(myPainter) to reference a paint worklet.
This means you can implement a paint function in JavaScript and use it inside your CSS.
Browser support (May 2019)
Currently, only Chrome and Opera support the Paint API of the Houdini draft. Firefox has signaled "intent to implement". See ishoudinireadyyet.com or caniuse.com for more information.
Code sample
There is a working "ripple" example implemented by the Houdini task force available here. I extracted the "core" from the example below. It implements the custom paint function, adds custom CSS properties like --ripple-color and uses a JavaScript function to implement the animation and to start and stop the effect.
Note, that it adds the custom paint function like this:
CSS.paintWorklet.addModule('https://googlechromelabs.github.io/houdini-samples/paint-worklet/ripple/paintworklet.js');
If you want to use the effect on your website, I recommend you download the file and reference it locally.
// Adds the custom paint function
CSS.paintWorklet.addModule('https://googlechromelabs.github.io/houdini-samples/paint-worklet/ripple/paintworklet.js');
// the actual animation of the ripple effect
function rippleEffect(element) {
let start, x, y;
element.addEventListener('click', function (evt) {
this.classList.add('animating');
[x, y] = [evt.offsetX, evt.offsetY];
start = performance.now();
const raf = (now) => {
const tick = Math.floor(now - start);
this.style.cssText = `--ripple-x: ${x}; --ripple-y: ${y}; --animation-tick: ${tick};`;
if (tick > 1000) {
this.classList.remove('animating');
this.style.cssText = `--animation-tick: 0`;
return;
}
requestAnimationFrame(raf);
};
requestAnimationFrame(raf);
});
}
rippleEffect(document.querySelector('.ripple'));
.ripple {
font-size: 5em;
background-color: rgb(0, 169, 244);
/* custom property */
--ripple-color: rgba(255, 255, 255, 0.54);
}
.ripple.animating {
/* usage of the custom "ripple" paint function */
background-image: paint(ripple);
}
<button class="ripple">Click me!</button>
Realization javascript + babel -
javascript -
class ImpulseStyleFactory {
static ANIMATION_DEFAULT_DURATION = 1;
static ANIMATION_DEFAULT_SIZE = 300;
static ANIMATION_RATIO = ImpulseStyleFactory.ANIMATION_DEFAULT_DURATION / ImpulseStyleFactory.ANIMATION_DEFAULT_SIZE;
static circleImpulseStyle( x, y, size, color = `#fff`, duration = 1 ){
return {
width: `${ size }px`,
height: `${ size }px`,
background: color,
borderRadius: `50%`,
display: `inline-block`,
pointerEvents: `none`,
position: `absolute`,
top: `${ y - size / 2 }px`,
left: `${ x - size / 2 }px`,
animation: `impulse ${ duration }s`,
};
}
}
class Impulse {
static service = new Impulse();
static install( container ) {
Impulse.service.containerRegister( container );
}
static destroy( container ){
Impulse.service.containerUnregister( container );
}
static applyToElement( {x, y}, container ){
Impulse.service.createImpulse( x, y, container );
}
constructor(){
this.impulse_clickHandler = this.impulse_clickHandler.bind(this);
this.impulse_animationEndHandler = this.impulse_animationEndHandler.bind(this);
this.actives = new Map();
}
containerRegister( container ){
container.addEventListener('click', this.impulse_clickHandler);
}
containerUnregister( container ){
container.removeEventListener('click', this.impulse_clickHandler);
}
createImpulse( x, y, container ){
let { clientWidth, clientHeight } = container;
let impulse = document.createElement('div');
impulse.addEventListener('animationend', this.impulse_animationEndHandler);
let size = Math.max( clientWidth, clientHeight ) * 2;
let color = container.dataset.color;
Object.assign(impulse.style, ImpulseStyleFactory.circleImpulseStyle(
x, y, size, color
));
if( this.actives.has( container ) ){
this.actives.get( container )
.add( impulse );
}else{
this.actives.set( container, new Set( [ impulse ] ) );
}
container.dataset.active = true;
container.appendChild( impulse );
}
impulse_clickHandler({ layerX, layerY, currentTarget: container }){
this.createImpulse( layerX, layerY, container );
}
impulse_animationEndHandler( {currentTarget: impulse} ){
let { parentNode: container } = impulse;
this.actives.get( container )
.delete( impulse );
if( ! this.actives.get( container ).size ){
this.actives.delete( container );
container.dataset.active = false;
}
container.removeChild(impulse);
}
}
css -
#keyframes impulse {
from {
opacity: .3;
transform: scale(0);
}
to {
opacity: 0;
transform: scale(1);
}
}
to use so -
html -
<div class="impulse" data-color="#3f1dcb" data-active="false">
<div class="panel"></div>
</div>
javascript -
let impulses = document.querySelectorAll('.impulse');
let impulseAll = Array.from( impulses );
impulseAll.forEach( Impulse.install );
Life example Impulse.install ( impulse create in coords of click, add handler event click ) -
class ImpulseStyleFactory {
static ANIMATION_DEFAULT_DURATION = 1;
static ANIMATION_DEFAULT_SIZE = 300;
static ANIMATION_RATIO = ImpulseStyleFactory.ANIMATION_DEFAULT_DURATION / ImpulseStyleFactory.ANIMATION_DEFAULT_SIZE;
static circleImpulseStyle( x, y, size, color = `#fff`, duration = 1 ){
return {
width: `${ size }px`,
height: `${ size }px`,
background: color,
borderRadius: `50%`,
display: `inline-block`,
pointerEvents: `none`,
position: `absolute`,
top: `${ y - size / 2 }px`,
left: `${ x - size / 2 }px`,
animation: `impulse ${ duration }s`,
};
}
}
class Impulse {
static service = new Impulse();
static install( container ) {
Impulse.service.containerRegister( container );
}
static destroy( container ){
Impulse.service.containerUnregister( container );
}
static applyToElement( {x, y}, container ){
Impulse.service.createImpulse( x, y, container );
}
constructor(){
this.impulse_clickHandler = this.impulse_clickHandler.bind(this);
this.impulse_animationEndHandler = this.impulse_animationEndHandler.bind(this);
this.actives = new Map();
}
containerRegister( container ){
container.addEventListener('click', this.impulse_clickHandler);
}
containerUnregister( container ){
container.removeEventListener('click', this.impulse_clickHandler);
}
createImpulse( x, y, container ){
let { clientWidth, clientHeight } = container;
let impulse = document.createElement('div');
impulse.addEventListener('animationend', this.impulse_animationEndHandler);
let size = Math.max( clientWidth, clientHeight ) * 2;
let color = container.dataset.color;
Object.assign(impulse.style, ImpulseStyleFactory.circleImpulseStyle(
x, y, size, color
));
if( this.actives.has( container ) ){
this.actives.get( container )
.add( impulse );
}else{
this.actives.set( container, new Set( [ impulse ] ) );
}
container.dataset.active = true;
container.appendChild( impulse );
}
impulse_clickHandler({ layerX, layerY, currentTarget: container }){
this.createImpulse( layerX, layerY, container );
}
impulse_animationEndHandler( {currentTarget: impulse} ){
let { parentNode: container } = impulse;
this.actives.get( container )
.delete( impulse );
if( ! this.actives.get( container ).size ){
this.actives.delete( container );
container.dataset.active = false;
}
container.removeChild(impulse);
}
}
let impulses = document.querySelectorAll('.impulse');
let impulseAll = Array.from( impulses );
impulseAll.forEach( Impulse.install );
#import "https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css";
/*#import url('https://fonts.googleapis.com/css?family=Roboto+Mono');*/
* {
box-sizing: border-box;
}
html {
font-family: 'Roboto Mono', monospace;
}
body {
width: 100%;
height: 100%;
margin: 0;
position: absolute;
}
main {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.container {
position: absolute;
top: 0;
left: 0;
}
.centred {
display: flex;
justify-content: center;
align-items: center;
}
.shadow-xs {
box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px, rgba(0, 0, 0, 0.117647) 0px 1px 4px;
}
.sample-impulse {
transition: all .5s;
overflow: hidden;
position: relative;
}
.sample-impulse[data-active="true"] {
box-shadow: rgba(0, 0, 0, 0.156863) 0px 3px 10px, rgba(0, 0, 0, 0.227451) 0px 3px 10px;
}
.panel {
width: 300px;
height: 100px;
background: #fff;
}
.panel__hidden-label {
color: #fff;
font-size: 2rem;
font-weight: bold;
pointer-events: none;
z-index: 1;
position: absolute;
}
.panel__default-label {
pointer-events: none;
z-index: 2;
position: absolute;
}
.sample-impulse[data-active="true"] .panel__default-label {
display: none;
}
#keyframes impulse {
from {
opacity: .3;
transform: scale(0);
}
to {
opacity: 0;
transform: scale(1);
}
}
<main class="centred">
<div class="sample-impulse impulse centred shadow-xs" data-color="#3f1dcb" data-active="false">
<div class="group centred">
<div class="panel"></div>
<span class="panel__hidden-label">StackOverflow</span>
<span class="panel__default-label">click me</span>
</div>
</div>
</main>
Life example Impulse.applyToElement ( impulse coords setby user, not add handler event click ) -
class ImpulseStyleFactory {
static ANIMATION_DEFAULT_DURATION = 1;
static ANIMATION_DEFAULT_SIZE = 300;
static ANIMATION_RATIO = ImpulseStyleFactory.ANIMATION_DEFAULT_DURATION / ImpulseStyleFactory.ANIMATION_DEFAULT_SIZE;
static circleImpulseStyle( x, y, size, color = `#fff`, duration = 1 ){
return {
width: `${ size }px`,
height: `${ size }px`,
background: color,
borderRadius: `50%`,
display: `inline-block`,
pointerEvents: `none`,
position: `absolute`,
top: `${ y - size / 2 }px`,
left: `${ x - size / 2 }px`,
animation: `impulse ${ duration }s`,
};
}
}
class Impulse {
static service = new Impulse();
static install( container ) {
Impulse.service.containerRegister( container );
}
static destroy( container ){
Impulse.service.containerUnregister( container );
}
static applyToElement( {x, y}, container ){
Impulse.service.createImpulse( x, y, container );
}
constructor(){
this.impulse_clickHandler = this.impulse_clickHandler.bind(this);
this.impulse_animationEndHandler = this.impulse_animationEndHandler.bind(this);
this.actives = new Map();
}
containerRegister( container ){
container.addEventListener('click', this.impulse_clickHandler);
}
containerUnregister( container ){
container.removeEventListener('click', this.impulse_clickHandler);
}
createImpulse( x, y, container ){
let { clientWidth, clientHeight } = container;
let impulse = document.createElement('div');
impulse.addEventListener('animationend', this.impulse_animationEndHandler);
let size = Math.max( clientWidth, clientHeight ) * 2;
let color = container.dataset.color;
Object.assign(impulse.style, ImpulseStyleFactory.circleImpulseStyle(
x, y, size, color
));
if( this.actives.has( container ) ){
this.actives.get( container )
.add( impulse );
}else{
this.actives.set( container, new Set( [ impulse ] ) );
}
container.dataset.active = true;
container.appendChild( impulse );
}
impulse_clickHandler({ layerX, layerY, currentTarget: container }){
this.createImpulse( layerX, layerY, container );
}
impulse_animationEndHandler( {currentTarget: impulse} ){
let { parentNode: container } = impulse;
this.actives.get( container )
.delete( impulse );
if( ! this.actives.get( container ).size ){
this.actives.delete( container );
container.dataset.active = false;
}
container.removeChild(impulse);
}
}
const generateRandomPointByRectdAll = ( { width, height }, length = 1 ) => {
let result = [];
while( length-- ){
result.push( {
x: Math.round( Math.random() * width ),
y: Math.round( Math.random() * height )
} );
}
return result;
};
const delayTask = ( task, delay ) => new Promise( ( resolve, reject ) => {
let timeoutID = setTimeout( () => task( ), delay )
} );
document.addEventListener( 'click', () => {
const MAX_IMPULSE_DELAY_TIME = 5000;
let container = document.querySelector('.custom-impulse');
let pointAll = generateRandomPointByRectdAll( {
width: container.clientWidth,
height: container.clientHeight
}, 5 );
let taskAll = pointAll.map( point => () => Impulse.applyToElement( point, container ) );
let delayTaskAll = taskAll.map( task => delayTask( task, Math.round( Math.random() * MAX_IMPULSE_DELAY_TIME ) ) );
} );
#import "https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css";
/*#import url('https://fonts.googleapis.com/css?family=Roboto+Mono');*/
* {
box-sizing: border-box;
}
html {
font-family: 'Roboto Mono', monospace;
}
body {
width: 100%;
height: 100%;
margin: 0;
position: absolute;
}
main {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.container-fill {
width: 100%;
height: 100%;
}
.container {
position: absolute;
top: 0;
left: 0;
}
.centred {
display: flex;
justify-content: center;
align-items: center;
}
.custom-impulse {
will-change: transform, opasity;
position: absolute;
}
#keyframes impulse {
from {
opacity: .3;
transform: scale(0);
}
to {
opacity: 0;
transform: scale(1);
}
}
<main class="centred">
<div class="custom-impulse container-fill centred" data-color="#3f1dcb" data-active="false">
<span>click me</span>
</div>
</main>
You can use Tronic247 Material framework to make the ripple effect.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Basic HTML5 Template</title>
<link href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/tronic247/material/dist/css/material.min.css" rel="stylesheet" />
</head>
<body class="container">
<div class="background-light-grey elevation-4 ripple-e dark-ripple" style="height: 200px;width: 200px;"></div>
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/tronic247/material/dist/js/material.min.js"></script>
</body>
</html>

Categories

Resources