How can I set a CSS keyframes in Javascript? - javascript

I have a CSS keyframes shown as below, my problem is I would like to set it as JavaScript (to put inside my div which already have some functions) so that I can return the "element" value to my function
.cylon-eye {
background-color: yellow;
background-image: linear-gradient( to right, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.9) 75%);
color: none;
height: 100%;
width: 20%;
animation: 4s linear 0s infinite alternate move-eye;
}
#keyframes move-eye {
from {
margin-left: -20%;
}
to {
margin-left: 100%;
}
}
I've tried to convert as below after suggestion, is that the return value i should call is var cylon-eye = document.getElementById("cylon-eye");?
<script type="text/javascript">
function appendStyle(styles) {
var css = document.createElement('style');
css.type = 'text/css';
if (css.styleSheet) css.styleSheet.cssText = styles;
else css.appendChild(document.createTextNode(styles));
document.getElementsByTagName("head")[0].appendChild(css);
}
var styles = '#cylon-eye { background-color: yellow; background-image: linear-gradient(to right,rgba(255,255,255, 0.9) 25%,rgba(255,255,255, 0.1) 50%,rgba(255,255,255, 0.9) 75%); color: none; height: 100%; width: 20%;animation: 4s linear 0s infinite alternate move-eye; z-index: 10;}';
var keyFrames = '\
#keyframes move-eye {\
from {\
margin-left: -20%;\
}\
to {\
margin-left: 100%;\
}\
}';
window.onload = function() { appendStyle(styles) };
</script>

let me share with you two snippets, one is using CSS + javascript and another is only using javascript, you can use whatever preferred to you. Hope its helpful to you.
WITH JAVASCRIPT
let dynamicStyles = null;
function addAnimation(body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(body, dynamicStyles.length);
}
addAnimation(`
#keyframes move-eye {
from {
margin-left: -20%;
}
to {
margin-left: 100%;
}
}
`);
var element = document.createElement("div");
element.className = "cylon-eye";
element.style.height = "50px";
element.style.width = "50px";
element.style.backgroundImage = "linear-gradient(to right,rgba(255, 0, 0, 0.1) 25%,rgba(255, 0, 0) 50%,rgba(255, 0, 0, 0.1) 75%)";
element.style.animation = "4s linear 0s infinite alternate move-eye";
document.body.appendChild(element);
WITH CSS + JAVASCRIPT
var element = document.createElement("div");
element.className = "cylon-eye";
element.style.height = "50px";
element.style.width = "50px";
document.body.appendChild(element);
.cylon-eye {
background-color: yellow;
background-image: linear-gradient(to right, rgba(255, 0, 0, 0.9) 25%, rgba(255, 0, 0, 0.1) 50%, rgba(255, 0, 0, 0.9) 75%);
color: none;
height: 100%;
width: 20%;
animation: 4s linear 0s infinite alternate move-eye;
}
#keyframes move-eye {
from {
margin-left: -20%;
}
to {
margin-left: 100%;
}
}

this works fine for me, Hope this will help :)
var style = document.createElement('style');
style.type = 'text/css';
var keyFrames = '\
#keyframes slidein {\
from {\
margin-left: 100%;\
width: 300%; \
}\
to {\
margin-left: 0%;\
width: 100%;\
}\
}';
document.getElementsById('slideDiv')[0].appendChild(style);

It would be wise to use Native API functionality to alter CSSStyleSheets. You can access existing stylesheets using the following method
document.styleSheets[0].cssRules
This will return all applied rules and you will be able to edit the rule where the selectorText matches your selector

have you tried using a <style></style> tag?
For example:
const loading = document.querySelector('.loading');
const keyFrames = document.createElement("style");
keyFrames.innerHTML = `
#keyframes loading {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading {
animation: loading 1s ease infinite;
}
`;
loading.appendChild(keyFrames);
.loading {
width: 100px;
height: 100px;
background-size: cover;
background-image: url('https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fmedia.24ways.org%2F2009%2F15%2Fassets%2Fimg%2Fspinner.png&f=1&nofb=1');
background-repeat: no-repeat;
}
<!doctype html>
<html>
<head>
<title>Keyframes in js</title>
</head>
<body>
<div class="loading"></div>
</body>
</html>

2022 SOLUTION
Now it's possible to use the method .animate() as in this example
document.getElementById("tunnel").animate([
// key frames
{ transform: 'translateY(0px)' },
{ transform: 'translateY(-300px)' }
], {
// sync options
duration: 1000,
iterations: Infinity
});
Docs:
https://developer.mozilla.org/es/docs/Web/API/Element/animate
https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats (blink effect example)

Related

Huge fps drop when images have loaded on screen

So I'm making a gallery page where the user can just upload images to the website on that page to store them or allow other people to look at them. And I've come across a performance issue and I'm not entirely sure why as I wouldn't have thought it would have any effect on performance.
Here is an image
The reason I can tell the fps is dropping is because I have a gradient in the background that moves to create an animation/glowing effect.
Here are all relevant pieces of code:
<body class='crt'>
<script>
let og_target = null;
function add_full_image(url, id, element) {
og_target = element;
let container = document.getElementById('full-image-container');
let container_inner = document.getElementById('full-image-container-inner');
let pre_img = document.getElementById(id);
container.style.zIndex = '1';
container.classList.remove('hidden');
container_inner.removeChild(container_inner.lastChild);
let img = document.getElementById(id).cloneNode(false);
img.className = 'full-image';
container_inner.appendChild(img);
}
document.addEventListener('click', function(event) {
let container_inner = document.getElementById('full-image-container-inner');
let container = document.getElementById('full-image-container');
const outside_click = typeof event.composedPath === 'function' && !event.composedPath().includes(container_inner);
const on_target = typeof event.composedPath === 'function' && event.composedPath().includes(og_target);
console.log(outside_click, container.classList.contains('hidden'), container_inner.children.length);
if (outside_click && !on_target && !container.classList.contains('hidden') && container_inner.children.length > 0) {
container.classList.add('hidden');
}
});
</script>
<div class='background-layer-1'> </div>
<div class='background-layer-2'> </div>
<div id='full-image-container' class='full-image-container'>
<div id='full-image-container-inner'>
</div>
</div>
<div class='gallery-grid'>
% for i, image in enumerate(images):
<a href='#' class='gallery-image' onclick='add_full_image("/user_data/gallery/{{image[1]}}", "{{i}}", this)'>
<img id='{{i}}' src="/user_data/gallery/{{image[1]}}" loading='lazy'/>
</a>
% end
</div>
</body>
</html>
.background-layer-1 {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: -1;
background: linear-gradient(90deg, rgb(29, 42, 52) 0%, rgba(20,20,31,1) 25%, rgb(25, 26, 42) 80%, rgb(28, 41, 47) 100%);
background-size: 400% 400%;
animation: gradient 10s ease infinite;
height: 100%;
}
.background-layer-2 {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: -1;
background: linear-gradient(127deg, rgba(50, 114, 55, 0.4) 0% , rgba(20, 20, 31, 0.4) 25%, rgba(25, 26, 42, 0.4) 70%, rgba(95, 66, 185, 0.4) 100%);
background-size: 400% 400%;
animation: 10s 4s ease infinite gradient, 5s forwards background_layer_2;
height: 100%;
}
.crt::after {
content: " ";
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(18, 16, 16, 0.1);
opacity: 0;
z-index: 2;
pointer-events: none;
animation: flicker 0.15s infinite;
}
.crt::before {
content: " ";
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
z-index: 2;
background-size: 100% 2px, 3px 100%;
pointer-events: none;
}
#keyframes flicker {
0% { opacity: 0.27861; }
5% { opacity: 0.34769; }
10% { opacity: 0.23604; }
15% { opacity: 0.90626; }
20% { opacity: 0.18128; }
25% { opacity: 0.83891; }
30% { opacity: 0.65583; }
35% { opacity: 0.67807; }
40% { opacity: 0.26559; }
45% { opacity: 0.84693; }
50% { opacity: 0.96019; }
55% { opacity: 0.08594; }
60% { opacity: 0.20313; }
65% { opacity: 0.71988; }
70% { opacity: 0.53455; }
75% { opacity: 0.37288; }
80% { opacity: 0.71428; }
85% { opacity: 0.70419; }
90% { opacity: 0.7003; }
95% { opacity: 0.36108; }
100% { opacity: 0.24387; }
}
#keyframes gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
I've included everything that I think could have an affect on the fps.
I set the image loading to lazy to force them to load after everything else has loaded to not create any extra delay when initially loading the page. I've tried removing different things but it seems to only be the amount of images that load that have an effect on the fps so I'm wondering if there is something I can do to fix it.
Also the lines starting with % are python lines

animate when changing an property from JavaScript css

If I try this code I just wrote:
function changeBackgroundColor() {
const clickingDiv = document.getElementById("test");
clickingDiv.style.setProperty("--primary-color-new", "#3474A7");
clickingDiv.style.setProperty("--secondary-color-new", "#ffd340");
clickingDiv.style.animation = "change-background 1s";
clickingDiv.classList.add("change-bg");
setTimeout(() => {
clickingDiv.style.setProperty("--primary-color", "#3474A7");
clickingDiv.style.setProperty("--secondary-color", "#ffd340");
clickingDiv.style.animation = "";
clickingDiv.classList.remove("change-bg");
}, 1000);
}
#keyframes change-background {
from {
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
}
to {
background: linear-gradient(to right, var(--primary-color-new), var(--secondary-color-new));
}
}
#test {
width: 500px;
height: 500px;
}
.change-bg {
animation-name: change-background;
animation-duration: 1s;
animation-iteration-count: 1;
animation-fill-mode: both;
animation-timing-function: ease;
}
<div id="test" style="--primary-color: #2568f6; --secondary-color: #804cda;">
<button onclick="changeBackgroundColor()">click me</button>
</div>
the animation don't work and it directly switch from the first color to the other. (Normally I retrieve the color from an API)
I would want to do a transition between the 2 values
I found out that linear-gradient transition / animation doesn't work. To fix this error I just used a wrapper with the opacity like this so here's the modified code:
function changeBackgroundColor() {
const wrapper = document.getElementById("wrapper");
const element = document.getElementById("test");
if (!wrapper.classList.contains("wrapper-moving")) {
wrapper.classList.add("wrapper-moving");
wrapper.style.setProperty("--new-primary-color", "#2568f6");
wrapper.style.setProperty("--new-secondary-color", "#804cda");
setTimeout(() => {
element.style.setProperty("--primary-color", "#2568f6");
element.style.setProperty("--secondary-color", "#804cda");
wrapper.classList.remove("wrapper-moving");
}, 1000);
}
}
#test {
width: 500px;
height: 500px;
background: linear-gradient(to right, #3474A7, #ffd340);
z-index: 1;
}
.wrapper {
z-index:-1;
height: 600px;
width: 600px;
position: absolute;
transition: all 1s;
background: linear-gradient(to right, var(--new-primary-color), var(--new-secondary-color));
}
.wrapper:not(.wrapper.wrapper-moving) {
opacity: 0;
}
.wrapper.wrapper-moving {
opacity: 1;
}
<div id="test" style="--primary-color: #2568f6; --secondary-color: #804cda;">
<div id="wrapper" class="wrapper"></div>
<button onclick="changeBackgroundColor()">click me</button>
</div>
(I couldn't get it to work properly in my case but it is the answer)

CSS click animation with opacity and particles

I want to make a custom click animation for my website, I want to do something like this:
Here's a capture from that gif
My first aproach was something like this:
It has some problems, for example when I click the animation is triggered but the animation follows the mouse instead of stay in the clicks coords, it lacks of many things like those shiny particles that spread across the click zone and that blurred halo, I dont know how to do this thing, someone know what should I do to accomplish this? like, what should I study or search to get what I want? I lack of expertise so I would really like some little guidance or anything
I dont know if this helps even a little bit but still I'll paste the code of my first approach
const cursor = document.querySelector('.cursor');
document.addEventListener('mousemove', e => {
cursor.setAttribute("style", "top: " + (e.pageY - 10) + "px; left: " + (e.pageX - 10) + "px;");
})
document.addEventListener('click', () => {
cursor.classList.add("expand");
setTimeout(() => {
cursor.classList.remove("expand");
}, 500);
})
body {
margin: 0;
height: 100vh;
background-color: black;
}
.cursor {
width: 20px;
height: 20px;
border-radius: 50%;
position: absolute;
}
#keyframes cursorAnim3 {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 0;
}
}
.expand {
animation: cursorAnim3 .3s forwards;
border: 2px solid rgba(255, 255, 255, 0.7);
}
<div class="cursor"></div>
Any suggestion is welcome :c
What I added to your code is an if statement inside of your mousemove event. I don't know how to explain, I just added it and it works... Hope that's what you wanted! :) PS: I also added overflow-x: hidden and overflow-y: hidden since the body size was increasing. It is located inside of
body {
}
const cursor = document.querySelector('.cursor');
document.addEventListener('mousemove', e => {
if (cursor.classList.length === 1) {
cursor.setAttribute("style", "top: " + (e.pageY - 10) + "px; left: " + (e.pageX - 10) + "px;");
}
})
document.addEventListener('click', () => {
cursor.classList.add("expand");
setTimeout(() => {
cursor.classList.remove("expand");
}, 500);
})
body {
margin: 0;
height: 100vh;
overflow-x: hidden;
overflow-y: hidden;
background-color: black;
}
.cursor {
width: 20px;
height: 20px;
border-radius: 50%;
position: absolute;
}
#keyframes cursorAnim3 {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 0;
}
}
.expand {
animation: cursorAnim3 .3s forwards;
border: 2px solid rgba(255, 255, 255, 0.7);
}
<div class="cursor"></div>

How to change body's background for a linear gradient using a fade in effect

I'm trying to get the body's background to change onload for a linear gradient onload event.
I have done this by far:
$(document).ready(function (){
$("body").addClass("bc");
});
/*CSS*/
.bc{
transition: background 1s;
background: red; /*This actually gets the fade in animation effect*/
/*background: linear-gradient(30deg, red, yellow) This doesn't get the effect*/
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I also tried to use Keyframes to change the background for a linear gradient but it changes it sharply
Here you have an example with keyframes animation:
$(document).ready(function (){
$("body").addClass("bc");
});
/*CSS*/
#-webkit-keyframes GradientAnimation {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#-moz-keyframes GradientAnimation {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#-o-keyframes GradientAnimation {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#keyframes GradientAnimation {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.bc{
background-color: red;
background: linear-gradient(270deg, #e4cc08, #e45708);
background-size: 400% 400%;
-webkit-animation: GradientAnimation 15s ease infinite;
-moz-animation: GradientAnimation 15s ease infinite;
-o-animation: GradientAnimation 15s ease infinite;
animation: GradientAnimation 15s ease infinite;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
And nice generator for this here: https://www.gradient-animator.com/
Since you cannot add transition on linear-gradient, you may use pseudo element that you make fading and you can easily adjust it's background and also the background of the body to create the needed effect:
setTimeout(function() {
$('body').addClass('bc')
}, 500); /*Control the start of fading here */
/*CSS*/
body {
transition: background 5s;
background:linear-gradient(60deg, yellow, red);
height:100vh;
margin:0;
}
body:before {
content: "";
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
opacity: 0;
transition: opacity 3s; /* control the speed of fading here*/
background: linear-gradient(30deg, red, pink)
}
body.bc:before {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
CSS transition works, but it lays an overlay over background. If you have text, which is placed above background, it will be overlaid.
i wrote a solution with jQuery, where you can define colors and order in which they would be changed from one to another:
in the example below, the animation goes from green to purple, and then back to green, and so on, until the animation stops after defined number of seconds
var stopAfterSec = 23;
var speed = 15;
var purple = [255, 26, 26];
var green = [26, 255, 118];
var sea_green = [26, 255, 244];
var order = [green, sea_green, purple];
var current = 0;
var direction = -1;
var color = end_color = order[current];
function updateGradient() {
if (color[0] == end_color[0] && color[1] == end_color[1] && color[2] == end_color[2]) {
direction = (current > 0 && current < order.length - 1) ? direction : (-1) * Math.sign(direction);
current += direction;
end_color = order[current];
}
$('.animGradientEfron').css({
background: "-webkit-radial-gradient(center, ellipse cover, rgba(0, 0, 0, 1) 0%, rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 0.48) 100%)"
});
for (var i = 0; i <= 2; i++) {
if (color[i] != end_color[i]) {
color[i] += Math.sign((end_color[i] - color[i]));
}
}
}
jQuery(document).ready(function() {
var startGradientAnimation = setInterval(updateGradient, speed);
setTimeout(function() {
clearInterval(startGradientAnimation);
}, stopAfterSec * 1000);
});
.animGradientEfron {
position: absolute;
top: 25%;
left: 0%;
width: 100%;
height: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="animGradientEfron"></div>
please refer it
.css {
background: -moz-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0.65) 100%), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, 0)), color-stop(59%, rgba(0, 0, 0, 0)), color-stop(100%, rgba(0, 0, 0, 0.65))), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
background: -webkit-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0.65) 100%), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
background: -o-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0.65) 100%), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
background: -ms-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0.65) 100%), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 59%, rgba(0, 0, 0, 0.65) 100%), url('http://www.skrenta.com/images/stackoverflow.jpg') no-repeat;
height: 200px;
}
<div class="css"></div>
Use css gradient over background image

CSS Transition Triggered by JavaScript

I want to have the following JavaScript function to transition function between from have none display to block when generate_loading_screen() is called to to when it finishes transition between display block to none. How do I do this?
function generate_loading_screen() {
window.setInterval(function(){
if (progress_percent < 75) {
document.getElementById("loading_screen").style.display = "block";
document.getElementById("body_of").style.filter = "grayscale(1)";
}
else {
document.getElementById("loading_screen").style.display = "none";
document.getElementById("body_of").style.filter = "none";
stop_generating_loading();
}
}, 50);
};
function stop_generating_loading() {
clearInterval(generate_loading_screen);
};
.loading {
position: fixed;
border: 16px solid #dbdbdb;
border-radius: 50%;
border-top: 16px solid #53f442;
margin-left: 44%;
margin-top: 10%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
<div class="loading" id="loading_screen" style="display: none;"></div>
Just extra info: progress_percent is a variable that determines how much of the rest of the web-app has loaded. The grayscale filter does not affect the whole page, just the ID body_of
Thanks in advance
Probably better to use a opacity transition by adding a class when your percent reaches 100.
Codepen for working example or see below.
HTML:
<div class="loading" id="loading_screen"></div>
CSS:
.loading {
position: fixed;
border: 16px solid #dbdbdb;
border-radius: 50%;
border-top: 16px solid #53f442;
margin-left: 44%;
margin-top: 10%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
opacity: 100%;
transition: opacity 1s ease-in-out;
}
.done_loading {
opacity: 0;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Javascript:
var progress_percent = 25;
var interval;
function generate_loading_screen() {
interval = window.setInterval(function(){
progress_percent += 1; //totest
if (progress_percent > 75) {
document.getElementById("loading_screen").className = "loading done_loading";
//stop_generating_loading();
}
//TESTING
if(progress_percent > 100){
console.log("Reached 100%");
document.getElementById("loading_screen").className = 'loading';
progress_percent = 0;
}
//
}, 50);
};
function stop_generating_loading() {
clearInterval(interval);
};
document.addEventListener('DOMContentLoaded', function(){
generate_loading_screen();
});
Remove all the testing code to get this to work once, you might need to add additional code for your body div. Let me know if you need me to add more to this example!
window.setInterval returns an intervalId which you need to cancel in order to stop the interval
let timer;
function generate_loading_screen() {
timer = window.setInterval(function(){
if (progress_percent < 75) {
document.getElementById("loading_screen").style.display = "block";
document.getElementById("body_of").style.filter = "grayscale(1)";
}
else {
document.getElementById("loading_screen").style.display = "none";
document.getElementById("body_of").style.filter = "none";
stop_generating_loading();
}
}, 50);
};
function stop_generating_loading() {
clearInterval(timer);
};

Categories