This code I am trying to get working has the play buttons at the bottom inside their own container.
How the code works is, click on a button, and then a video should appear on the screen.
To test jsitor code, press run, not update.
https://jsitor.com/MIYiywN4HC / https://jsfiddle.net/kxhyLdr8/
<div class="playButtonContainer">
<button class="playa cover" type="button"></button>
<button class="playb cover" type="button"></button>
<button class="playc cover" type="button"></button>
<button class="playd cover" type="button"></button>
<button class="playe cover" type="button"></button>
<button class="playf cover" type="button"></button>
<button class="playg cover" type="button"></button>
<button class="playh cover" type="button"></button>
<button class="playi cover" type="button"></button>
</div>
What is weird about the above code is that after clicking on the buttons, no errors are appearing, when there should be.
By errors I mean, in the console log, I was referring to, usually errors will appear in there if something is broken. I am stuck trying to figure out how to get the broken code working.
For comparison, here is a demo code of it working with the play buttons not inside their own container.
The only difference between the demo code and the code I am working on is that in the code I am working on, the play buttons are inside their own container.
To test jsitor code, press run, not update.
https://jsitor.com/qTWoiuHp7z / https://jsfiddle.net/24xf0avp/
The only difference between the working and non working example is just the extra layer of HTML.
Nothing was touched in any other part of the code.
Would anyone be able to check and see if their able that code to work, the code with the play buttons in their own container?
No one has been able to figure out how that would be done, getting the play buttons to work inside their own container.
I was told by my instructor, the code needs to work with the play buttons inside their own container, separate from the HTML.
I have no idea how that would be done, and because there are no errors appearing in the code, that further confuses me.
I was told this:
The problem here is inside the body of the function showCover, which
adds the class active and calls the function show (which removes the
class hide) to and from the parent of the button , instead of the
.container element where it should be.
Could it be that you just need to extend your JS code so that it
reaches the inner html node?
on the button click event it probably captures the parent element
instead of the target element?
Binding of the click of the play button
So you should either wrap them together, put the play button in the
outer, and then go to the closest('.outer') then
querySelector('.video') or whatever you want to do. Or put an
attribute on the .playa button (eg data-player-id="play1") that you
can use to directly find from the click and then use this to call the
.container.play1 element
Here I created a very simple demo with one button.
<div class="playButtonContainer">
<button class="playa cover" type="button"></button>
</div>
Here is the working demo code with only 1 button.
https://jsitor.com/Qer_1Oi9Jv / https://jsfiddle.net/1bmrwuzn/
Here is the broken code with only 1 button.
https://jsitor.com/D38vgjs3g / https://jsfiddle.net/gt57k29h/
What is meant by broken is, is that it is not working or functioning the same way as the demo code I provided.
Smaller, reproducible example code:
const manageCover = (function makeManageCover() {
const config = {};
const body = document.body;
let currentPlayButton = {};
function show(el) {
el.classList.remove("hide");
}
function hide(el) {
el.classList.add("hide");
}
function hideAll(elements) {
elements.forEach(hide);
}
function resetBackground(backgroundSelector) {
const allBackgrounds = document.querySelectorAll(backgroundSelector);
function hideBackground(background) {
background.classList.add("bg1");
}
allBackgrounds.forEach(hideBackground);
}
function resetButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function hideButton(button) {
button.classList.add("isOpen");
}
allButtons.forEach(hideButton);
}
function resetPage() {
resetBackground("body");
resetButtons(".outer");
}
function markAsPlayed(played) {
played.classList.add("played");
}
function showCover(playButton) {
hideAll(config.containers);
resetPage();
markAsPlayed(playButton);
const cover = playButton.parentElement;
cover.classList.add("active");
show(cover);
}
function animationEndHandler(evt) {
const animationName = evt.animationName;
if (animationName === "initial-fade") {
body.classList.remove("initial-fade");
showCover(currentPlayButton);
}
}
function coverClickHandler(evt) {
currentPlayButton = evt.currentTarget;
body.classList.add("initial-fade");
}
function addClickToButtons(playButtons) {
playButtons.forEach(function playButtonHandler(playButton) {
playButton.addEventListener("click", coverClickHandler);
});
}
function addCoverHandler(coverSelector, handler) {
const cover = document.querySelector(coverSelector);
cover.addEventListener("click", handler);
}
function init(selectors) {
config.containers = document.querySelectorAll(selectors.container);
const playButtons = document.querySelectorAll(selectors.playButton);
addClickToButtons(playButtons);
body.addEventListener("animationend", animationEndHandler);
}
return {
addCoverHandler,
init
};
}());
const manageUI = (function makeManageUI() {
const body = document.body;
const players = [];
function findPlayers() {
const allCovers = document.querySelectorAll(".cover");
const allWrappers = document.querySelectorAll(".wrap");
allCovers.forEach(function addToPlayers(cover, index) {
players.push({
"cover": cover,
"wrapper": allWrappers[index]
});
});
}
// inline arrow function, direct return
function getWrapper(cover) {
const index = players.findIndex(
(player) => player.cover === cover
);
return players[index].wrapper;
}
function resetBackground(backgroundSelector) {
const allBackgrounds = document.querySelectorAll(backgroundSelector);
function showBackground(background) {
background.classList.remove("bg1");
}
allBackgrounds.forEach(showBackground);
}
function resetCurtains(curtainSelector) {
const allCurtains = document.querySelectorAll(curtainSelector);
function showCurtain(curtain) {
curtain.classList.remove("active");
}
allCurtains.forEach(showCurtain);
}
function showAllButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function showButton(button) {
button.classList.remove("hide");
}
allButtons.forEach(showButton);
}
function resetButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function showButton(button) {
button.classList.remove("isOpen");
}
allButtons.forEach(showButton);
}
function animationEndHandler(evt) {
const animationName = evt.animationName;
console.log(animationName);
if (animationName === "fadingOut") {
fadeReset();
}
}
function fadeReset() {
body.classList.remove("fadingOut");
resetBackground("body");
resetCurtains(".with-curtain");
showAllButtons(".hide");
resetButtons(".outer");
}
function resetPage() {
body.classList.add("fadingOut");
}
function exitClickHandler() {
resetPage();
}
function addClickToExit(exitButtons) {
exitButtons.forEach(function addExitButtonHandler(exitButtons) {
exitButtons.addEventListener("click", exitClickHandler);
});
}
function addExitHandlers(callback) {
const resetVideo = document.querySelectorAll(".exit");
resetVideo.forEach(function resetVideoHandler(video) {
video.addEventListener("click", callback);
});
}
function init() {
findPlayers();
const exitButtons = document.querySelectorAll(".exit");
addClickToExit(exitButtons);
body.addEventListener("animationend", animationEndHandler);
}
return {
addExitHandlers,
getWrapper,
init
};
}());
const videoPlayer = (function makeVideoPlayer() {
const tag = document.createElement("script");
tag.src = "https://www.youtube.com/player_api";
const firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onPlayerReady(event) {
const player = event.target;
player.setVolume(100);
}
function onPlayerStateChange(event) {
const player = event.target;
return player;
}
function addPlayer(video, playerOptions) {
playerOptions.videoId = playerOptions.videoId || video.dataset.id;
playerOptions.events = playerOptions.events || {};
playerOptions.events.onReady = onPlayerReady;
playerOptions.events.onStateChange = onPlayerStateChange;
const player = new YT.Player(video, playerOptions);
return player;
}
return {
addPlayer
};
}());
const managePlayer = (function makeManagePlayer() {
const playerVars = {
autoplay: 0,
controls: 1,
disablekb: 1,
enablejsapi: 1,
fs: 0,
iv_load_policy: 3
};
const defaults = {
height: 360,
host: "https://www.youtube-nocookie.com",
playerVars,
width: 640
};
function show(el) {
el.classList.remove("hide");
}
function combinePlayerOptions(opts1 = {}, opts2 = {}) {
const combined = Object.assign({}, opts1, opts2);
Object.keys(opts1).forEach(function checkObjects(prop) {
if (typeof opts1[prop] === "object") {
combined[prop] = Object.assign({}, opts1[prop], opts2[prop]);
}
});
return combined;
}
function createPlayer(videoWrapper, playerOptions = {}) {
const video = videoWrapper.querySelector(".video");
const options = combinePlayerOptions(defaults, playerOptions);
return videoPlayer.addPlayer(video, options);
}
function playerAdder(wrapper, playerOptions) {
return function addPlayerCallback() {
initPlayer(wrapper, playerOptions);
};
}
function removePlayer(wrapper) {
wrapper.player.destroy();
delete wrapper.player;
console.log("removePlayer");
}
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrapper = container.querySelector(".wrap");
if (wrapper.player) {
return removePlayer(wrapper);
}
}
function initPlayer(wrapper, playerOptions) {
show(wrapper);
const player = createPlayer(wrapper, playerOptions);
wrapper.player = player;
}
return {
adder: playerAdder,
removePlayerHandler
};
}());
const players = (function coverUIPlayerFacade() {
function addPlayer(coverSelector, playerOptions) {
const cover = document.querySelector(coverSelector);
const wrapper = manageUI.getWrapper(cover);
const callback = managePlayer.adder(wrapper, playerOptions);
manageCover.addCoverHandler(coverSelector, callback);
}
function init() {
manageCover.init({
container: ".container",
playButton: ".cover"
});
manageUI.init({});
manageUI.addExitHandlers(managePlayer.removePlayerHandler);
}
return {
add: addPlayer,
init
};
}());
players.init();
function onYouTubeIframeAPIReady() {
players.add(".playa", {});
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
body {
background: #353198;
}
.initial-fade {
animation: initial-fade 500ms ease forwards;
}
#keyframes initial-fade {
to {
opacity: 0;
}
}
.initial-fade,
.fadingOut {
cursor: default;
}
.initial-fade .cover,
.initial-fade .cover * {
pointer-events: none !important;
}
.container {
display: flex;
justify-content: center;
position: relative;
/*z-index: 2;*/
}
.container.active {
flex: 1 0 0;
}
/*body.*/
.bg1 {
animation: fadeInBody 5s ease 0s forwards;
animation-delay: 0s;
opacity: 0;
}
#keyframes fadeInBody {
100% {
opacity: 1;
}
}
/*body.*/
.bg1 .with-curtain:before {
content: "";
position: fixed;
/*z-index: 1;*/
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: 165px 165px;
background-image:
}
.playButtonContainer {
display: flex;
flex-wrap: wrap;
min-height: 100%;
margin: auto;
justify-content: center;
align-content: center;
width: 290px;
gap: 10px;
animation: fadeInButtons 3s ease 0s forwards;
}
#keyframes fadeInButtons {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.outer.isOpen {
width: auto;
}
.fadingOut .isOpen {
animation: fadingOut 1s;
animation-delay: 11.3s;
}
#keyframes fadingOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.inner-container {
display: none;
}
/* when container is active hide the cssPlay and show the inner container*/
.container.active .cover {
display: none;
}
.container.active .inner-container {
display: flex;
}
.container.active .inner-container.curtain {
display: block;
}
.cover {
-webkit-appearance: none;
appearance: none;
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 90px;
height: 90px;
border-radius: 50%;
cursor: pointer;
border: 9px solid blue;
background: transparent;
filter: drop-shadow(3px 3px 3px rgba(0, 0, 0, 0.7));
}
.cover::before {
content: "";
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 27px solid blue;
transform: translateX(4px);
}
.cover:hover {
box-shadow: 0 0 0 5px rgba(43, 179, 20, 0.5);
}
.cover:focus {
outline: 0;
box-shadow: 0 0 0 5px rgba(0, 255, 255, 0.5);
}
.played {
border-color: green;
}
.played::before {
border-left-color: green;
}
.curtain {
flex: 1 0 0;
margin: auto;
max-width: 642px;
/*box-shadow: inset 0 -2px 0px 0px #0a0a0a;*/
border: 20px solid black;
border-radius: 3.2px;
border-color: #000 #101010 #000 #101010;
position: relative;
}
.curtain::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: radial-gradient(circle, transparent 0% 35%, #0a0a0a 35%);
}
.ratio-keeper {
position: relative;
height: 0;
padding-top: 56.25%;
margin: auto;
overflow: hidden;
border: 1px solid #333;
}
.video-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
iframe {
user-select: none;
}
.panel-left,
.panel-right {
position: absolute;
height: 100%;
width: calc(50% + 1px);
/* rounding error fix */
top: 0%;
transition: all ease 10s;
/*background-image: url("https://picsum.photos/600");
background-size: cover;
background-repeat: no-repeat;
background-position: center;*/
overflow: hidden;
}
.panel-left {
left: 0;
/*background-color: rgb(91, 96, 106);*/
}
.panel-right {
right: 0;
/*background-color: rgb(229, 211, 211);*/
}
.panel-left::before,
.panel-right::before {
content: "";
position: absolute;
height: 100%;
width: 200%;
top: 0;
left: 0;
background-size: auto;
background-repeat: no-repeat;
background-position: 0 0;
}
.panel-right::before {
left: -100%;
}
.container.active .curtain .panel-left {
animation: curtain1-open 8s forwards 520ms;
}
#keyframes curtain1-open {
to {
transform: translateX(calc(-100% - 1px));
}
}
.container.active .curtain .panel-right {
animation: curtain2-open 8s forwards 520ms;
}
#keyframes curtain2-open {
to {
transform: translateX(calc(100% + 1px));
}
}
.fadingOut .container.active .curtain .panel-left {
animation: curtain1-close 8s ease forwards;
transform: translateX(calc(-100% - 1px));
animation-delay: 3.3s;
}
#keyframes curtain1-close {
to {
transform: translateX(0);
}
}
.fadingOut .container.active .curtain .panel-right {
animation: curtain2-close 8s ease forwards;
transform: translateX(calc(100% + 1px));
animation-delay: 3.3s;
}
#keyframes curtain2-close {
to {
transform: translateX(0);
}
}
.exit {
position: absolute;
top: auto;
bottom: -47px;
margin: auto;
right: 0;
left: 0;
width: 47px;
height: 47px;
cursor: pointer;
background: black;
border-radius: 50%;
border: 5px solid red;
animation: fadeInExit 1s forwards;
opacity: 0;
pointer-events: none;
}
.exit::before,
.exit::after {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto;
width: 100%;
height: 5px;
background: red;
transform: rotate(45deg);
transition: all 1s ease;
}
.exit::after {
transform: rotate(-45deg);
}
#keyframes fadeInExit {
99% {
pointer-events: none;
}
100% {
pointer-events: initial;
opacity: 1;
}
}
.exit:hover::before,
.exit:hover::after,
.fadingOut .exit::before,
.fadingOut .exit::after {
background: green;
}
.fadingOut .exit {
animation: fadeOutExit 8s forwards;
pointer-events: none;
opacity: 1;
}
#keyframes fadeOutExit {
to {
opacity: 0;
}
}
.hide {
display: none;
}
<div class="outer">
<div class="container play1 with-curtain">
<div class="inner-container curtain curtain1">
<div class="ratio-keeper">
<div class="wrap">
<div class="video video-frame" data-id="CHahce95B1g"></div>
</div>
<div class="sliding-panels">
<div class="panel-left"></div>
<div class="panel-right"></div>
</div>
</div>
<button class="exit" type="button" title="Exit" aria-label="Close"></button>
</div>
</div>
</div>
<div class="playButtonContainer">
<button class="playa cover" type="button" aria-label="Open"></button>
</div>
I modified and copied a snippet from codepen to my local machine. I linked the css and js files with html file. But the css part is not working properly. The code was working fine in codepen.
index.html
<head>
<link src="http://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
<link src="https://fonts.googleapis.com/css2?family=Nunito:wght#800&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./styles.css" />
</head>
<body>
<menu class="menu">
<button class="menu__item active">
<div class="menu__icon" >
<i class="fa-solid fa-house"></i>
</div>
<strong class="menu__text active">home</strong>
</button>
<button class="menu__item">
<div class="menu__icon" >
<i class="fa-solid fa-user"></i>
</div>
<strong class="menu__text">About</strong>
</button>
<button class="menu__item">
<div class="menu__icon" >
<i class="fa-solid fa-briefcase"></i>
</div>
<strong class="menu__text">
Products</strong>
</button>
<button class="menu__item">
<div class="menu__icon" >
<i class="fa-solid fa-question"></i>
</div>
<strong class="menu__text">
Concerns</strong>
</button>
<button class="menu__item">
<div class="menu__icon" >
<i class="fa-solid fa-blog"></i>
</div>
<strong class="menu__text">
Blog</strong>
</button>
<button class="menu__item">
<div class="menu__icon" >
<i class="fa-solid fa-envelope"></i>
</div>
<strong class="menu__text">Contact</strong>
</button>
</menu>
<script src="./index.js"></script>
</body>
styles.css
#import url('https://fonts.googleapis.com/css2?family=Nunito:wght#800&display=swap');
html {
box-sizing: border-box ;
--duration: .45s ;
--cubic: cubic-bezier(0.4, 0, 0.2, 1) ;
--color-1: white ;
--color-2: #000 ;
--theme-bg-color:rgba(16 18 27 / 40%)
--border-color: rgba(113 119 144 / 25%);
--theme-color: #f9fafb;
}
html *,
html *::before,
html *::after {
box-sizing: inherit ;
}
body {
margin: 0 ;
height: 20vh ;
display: flex ;
overflow: hidden ;
align-items: center ;
justify-content: center ; background: #2193b0;
background: -webkit-linear-gradient(to left, #6dd5ed, #2193b0);
background: linear-gradient(to left, #6dd5ed, #2193b0);
font-family: 'Dosis', sans-serif ;
}
.menu {
margin: 0 ;
width: 45em;
display: flex ;
height: 5em ;
user-select: none ;
position: relative ;
align-items: center ;
padding: 0 1.7em ;
justify-content: center ;
/* From https://css.glass */
background: rgba(245, 245, 245, 0.4);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 1em 1em 2.5em 2.5em;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
max-width: 1250px;
max-height: 860px;
-webkit-tap-highlight-color: transparent ;
}
#media (max-width: 42.625em) {
.menu {
font-size: .55em ;
}
}
.menu__item {
all: unset ;
flex-grow: 1 ;
display: flex ;
cursor: pointer ;
overflow: hidden ;
padding-top: 0.5em ;
position: relative ;
align-items: left ;
color: var(--color-1) ;
justify-content: center ;
transition: flex-grow var(--duration) var(--cubic) ;
}
.menu__icon {
font-size: 1.5em;
stroke: currentColor ;
transition: transform var(--duration) var(--cubic) ;
}
.menu__item::after {
left: -1.15em;
bottom: 0 ;
content: " " ;
height: 0.25em ;
position: absolute ;
border-radius: 2em ;
transform-origin: left center ;
background-color: currentColor ;
width: calc( var(--lineWidth) + 5px ) ;
transform: translate3d(3em , 0, 0) scaleX(0) ;
transition: transform calc( var(--duration) + .2s) var(--cubic) ;
}
.menu__text {
left: 4.3em ;
height: 1.5em;
font-size: 1.25em ;
position: absolute ;
text-transform: capitalize ;
letter-spacing: .01em ;
transform: translate3d(0, 109%, 0) ;
transition: transform calc( var(--duration) / 3.7 ) ;
}
.menu__item.active {
flex-grow: 2.3 ;
color: var(--color-2) ;
}
.menu__item.active .menu__icon {
transform: translate3d(-95% , 0, 0) ;
}
.menu__item.active::before {
transform: scale(1) ;
}
.menu__item.active::after {
transform: translate3d(6.3em , 0, 0) scaleX(1) ;
transition: transform var(--duration) var(--cubic) ;
}
.menu__text.active {
transform: translate3d(0 , 0, 0) ;
transition: transform calc(var(--duration) / 1.5) ;
}
.icon {
--duration-icon: 1s ;
fill: none ;
width: 2.5em ;
height: 2.5em ;
display: block ;
stroke-width: 15 ;
stroke-miterlimit: 10 ;
}
.active #home-anm {
animation: home var(--duration-icon) ;
}
#keyframes home {
25% {
transform: translate3d(0, -.8em , 0) ;
}
50% {
transform: translate3d(0, .5em , 0) ;
}
}
#strategy-anm {
transform: scaleX(.85) ;
transform-origin: center ;
}
.active #strategy-anm {
animation: strategy var(--duration-icon) ;
}
#keyframes strategy {
50% {
transform: scaleX(1) ;
}
100%{
transform: scaleX(.85) ;
}
}
.active #strategy-cir1 {
animation: strategy-cir1 var(--duration-icon);
}
.active #strategy-cir2 {
animation: strategy-cir2 var(--duration-icon) .1s;
}
.active #strategy-cir3 {
animation: strategy-cir3 var(--duration-icon) .2s;
}
#keyframes strategy-cir1 {
50% {
transform: translate3d(-.7em,-0.7em,0);
}
100%{
transform: translate3d(0,0,0);
}
}
#keyframes strategy-cir2 {
35% {
transform: translate3d(0,-0.7em,0);
}
100%{
transform: translate3d(0,0,0);
}
}
#keyframes strategy-cir3 {
35% {
transform: translate3d(.7em,-0.7em,0);
}
100%{
transform: translate3d(0,0,0);
}
}
.active #period-anm {
transform-origin: center 100% ;
animation: period var(--duration-icon) ;
}
.active #period-cir {
transform-origin: center ;
animation: period-cir var(--duration-icon) ;
}
.active #period-line {
stroke-dasharray: 66 ;
animation: period-line calc( var(--duration-icon) / 2.5 ) reverse ;
}
#keyframes period {
35% {
transform: scaleY(.85) ;
}
60% , 70% {
transform: scaleY(1.2) ;
}
100% {
transform: scaleY(1) ;
}
}
#keyframes period-cir {
0%{
opacity: 0 ;
}
35% {
opacity: 1 ;
transform: translate3d(15%, -55%, 0) ;
}
60%{
opacity: 0 ;
transform: translate3d(-8%, -50%, 0) ;
}
}
#keyframes period-line {
100% {
stroke-dashoffset: 66 ;
}
}
.active #security-cir {
transform-box: fill-box ;
transform-origin: center ;
animation: security-cir calc( var(--duration-icon) / 1.5 ) ;
}
#keyframes security-cir {
0% {
transform: scale(0) ;
}
100% {
transform: scale(1) ;
}
}
.active #security-strok {
stroke-dasharray: 96;
animation: security-strok calc( var(--duration-icon) / 1.2 ) ;
}
#keyframes security-strok {
0% {
stroke-dashoffset: 60 ;
}
100% {
stroke-dashoffset: 230 ;
}
}
.active #settings-anm {
transform-box: fill-box ;
transform-origin: center ;
animation: settings-anm calc( var(--duration-icon) / 1.5 ) ;
}
#keyframes settings-anm {
0% {
transform: rotate(-60deg);
}
50% {
transform: rotate(60deg);
}
}
index.js
/* Intagram: #ui.tormenta*/
const buttons = document.querySelectorAll(".menu__item");
let activeButton = document.querySelector(".menu__item.active");
buttons.forEach(item => {
const text = item.querySelector(".menu__text");
setLineWidth(text, item);
window.addEventListener("resize", () => {
setLineWidth(text, item);
})
item.addEventListener("click", function() {
if (this.classList.contains("active")) return;
this.classList.add("active");
if (activeButton) {
activeButton.classList.remove("active");
activeButton.querySelector(".menu__text").classList.remove("active");
}
handleTransition(this, text);
activeButton = this;
});
});
function setLineWidth(text, item) {
const lineWidth = text.offsetWidth + "px";
item.style.setProperty("--lineWidth", lineWidth);
}
function handleTransition(item, text) {
item.addEventListener("transitionend", (e) => {
if (e.propertyName != "flex-grow" ||
!item.classList.contains("active")) return;
text.classList.add("active");
});
}
I put those css and js files in the same folder. When I open the html file, it doesn't show any font-awesome icons or any text, but it shows the navbar layout. I am a complete beginner and any help is appreciated.
Changing the directory path of the CSS link to "folder-name/styles.css" worked for me.
Using './' means the file your looking for exists in the you current directory. I am not sure why the JavaScript worked but to target a file in a folder located in your active directory you should use 'folder/file.txt'
As for the Font Awesome it doesn't look like you have imported it. Go to the below link to see your Font Awesome kits and link one in your HTML file
https://fontawesome.com/kits
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>