Change keyframes values in Javascript - javascript

I have this piece of css but I want to change the width in the keyframe with a variable in javascript. How can I do that?
#keyframes test {
100% {
width: 100%;
}
}

Does it have to be a keyframe animation? Typically you would use the CSS transition property for this kind of animation powered by JavaScript, like this:
var width = 50;
document.getElementById('button').addEventListener('click', () => {
width += 50;
document.getElementById('box').style.width = `${width}px`;
});
#box {
background: red;
height: 50px;
width: 50px;
transition: width .5s;
margin-bottom: 1em;
}
<div id="box"></div>
<button id="button">Change Width</button>

If you have a more general animation (that can't be encompassed by just doing a transition) then you can use JS to set a CSS variable.
Taking the example in the question, replace the 100% with a variable:
#keyframes test {
100% {
width: var(--bg);
}
}
and the Javascript you'd have something like:
thediv.style.setProperty('--bg', '60px');

#JohnUleis already answeared correctly. I was too late. But I add just for fun a solution. Is named: How lfar is Rom? ;-)
Cheers
let root = document.documentElement;
const div = document.querySelector('div');
root.addEventListener("mousemove", e => {
div.style.setProperty('--width', e.clientX + "px");
div.innerHTML = e.clientX + ' km';
});
:root {
--width: 100%;
}
div {
background: hotpink;
width: var(--width);
text-align: center;
color: white;
}
<div>how far is rom?</div>

Related

How to use javascript to dynamically set css animation's keyframe?

I want to make an animation on my product page. When user clicks "add to cart" the product image will be animated moving and shrinking to the cart icon in the nav bar.
Here is a sample html
$('div.test').on('animationend', (e) => {
$(e.target).remove();
})
//user click
$('div.test').addClass('animateTest');
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : 100px;
left : 1100px;
width : 0;
height : 0
}
}
.animateTest {
animation : toCart 2s;
/* animation-fill-mode: forwards; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
</div>
The hard part is, since users' viewports vary, I probably need to use javascript to get the cart icon's position(unless I can get it from CSS which I don't think is possible):
whereIsCart = $('#cartIcon').offset()
and I need to do something like
100% {
top : whereIsCart.top;
left : whereIsCart.left;
width : 0;
height : 0
}
But how can I do this?
Or, is there any better practice to achieve the same goal?
It may be easier to use css transitions instead of keyframe animations:
.test {
// ...
transition: transform 1s ease-in-out;
}
// on click
whereIsCart = $('#cartIcon').offset();
$('div.test').css('transform', 'translate(' + whereIsCart.left + 'px, ' + whereIsCart.top + 'px) scale(0)');
When working with CSS in JavaScript you may want to use the CSSOM API; more specifically, its factory functions for unit values, e.g. CSS.px(), CSS.percent().
Note that parts of the CSSOM API are still experimental, e.g. the factory functions. In production, you should make sure that the target browsers support the features you use.
Regardless of using CSS or JS for the animation itself: To get the element's current position in the viewport you can use Element.getBoundingClientRect(), or more generally Element.getClientRects() for all its relevant boxes.
CSS Custom Properties
You can use custom properties for the initial position. You can set them via JavaScript, and even provide a fallback value in CSS.
If you use them for the animating (not as animated) properties, it should just work:
const divTest = document.querySelector("div.test");
// Example for non-empty custom properties
divTest.style.setProperty("--top", CSS.px(20));
divTest.style.setProperty("--left", CSS.px(80));
// Should happen on click:
toCart(divTest);
function toCart(element) {
const rect = element.getBoundingClientRect();
element.style.setProperty("--top", CSS.px(rect.top));
element.style.setProperty("--left", CSS.px(rect.left));
element.classList.add("animateTest");
}
.test {
position: fixed;
top: var(--top, 10%);
left: var(--left, 10%);
width: 200px;
height: 300px;
background: red;
}
#keyframes toCart {
25% {
top: 80%;
left: 50%;
width: 200px;
height: 300px;
}
100% {
top: 10%;
left: 100%;
width: 0;
height: 0;
}
}
.animateTest {
animation: toCart 2s;
}
<div class="test"></div>
Sidenote: If you want to animate custom properties themselves, you have to define the in a #property rule. Otherwise CSS cannot animate it since its type may be anything (animating e.g. from a length to a color is impossible).
Web Animations API
In JavaScript, you can use the Web Animations API, which is essentially CSS animations but in JS.
You can define keyframes, duration, fill-mode and more. Since Animation.finished is a promise, you can simply react to the animation's end via await or Promise.then().
Example:
const divTest = document.querySelector("div.test");
// Should happen on click:
const animation = animateToCart(divTest);
animation.finished.then(() => console.log("Animation finished. This could start a new animation!"));
function animateToCart(element) {
const rect = element.getBoundingClientRect();
const keyframes = [
{
offset: .25,
top: CSS.percent(80),
left: CSS.percent(50),
width: CSS.px(rect.width),
height: CSS.px(rect.height)
}, {
top: CSS.percent(10),
left: CSS.percent(100),
width: 0,
height: 0
}
];
return element.animate(keyframes,
{
duration: 2000,
easing: "ease" // Is default in CSS, but not in Web Animations...
}
);
}
.test {
position: fixed;
top: 10%;
left: 10%;
width: 200px;
height: 300px;
background: red;
}
<div class="test"></div>
Multi-step animations are also easily done with Web Animations, since you can start another animation after the first animation's promise has resolved.
CSS variables sample code...
const
bluElm = document.querySelector('#blue_elm')
, btAnim = document.querySelector('#bt-anim')
, btMovE = document.querySelector('#bt-movE')
, elTest = document.querySelector('.test')
;
btMovE.onclick = () =>
{
bluElm.classList.toggle('move');
}
btAnim.onclick = () =>
{
let rect = bluElm.getBoundingClientRect();
/* change CSS variables values as style Property ------------- */
elTest.style.setProperty('--p_top', `${rect.bottom}px`);
elTest.style.setProperty('--p_left', `${rect.left}px`);
elTest.classList.add('animateTest');
}
elTest.onanimationend = () =>
{
elTest.classList.remove('animateTest');
}
#blue_elm {
position : fixed;
top : 20px;
left : 300px;
width : 20px;
height : 20px;
border-radius : 10px;
background : cornflowerblue;
}
#blue_elm.move {
top : 50px;
left : 150px;
}
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
--p_top : 0; /* CSS variables declaration */
--p_left : 0;
}
.animateTest {
animation : toCart 2s;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : var(--p_top); /* CSS variables usage */
left : var(--p_left);
width : 0;
height : 0
}
}
<button id="bt-anim"> show animation</button>
<button id="bt-movE"> move element +- 150px</button>
<div id="blue_elm"></div>
<div class="test"></div>

CSS incrementing transition animation on every button click

Given
var box = document.querySelector('.box');
document.addEventListener("click", (event) => {
if (!event.target.closest("button")) return;
if(event.target.id === "button") {
box.classList.add('move');
}
});
.box {
width: 100px;
height: 100px;
background-color: #000;
transition: transform 1s;
}
.move {
transform: translateX(20px);
}
<div class="box"></div>
<button id="button">button</button>
with a JS Fiddle here.
I want the box's x-position to increment by 20px on every button click.
I am not sure how to proceed. I initially tried using #KeyForms but that requires the from and to prefixes, on which I cannot (I think) add a variable value. Same issue arises here, it seems I cannot have a variable in the css code which I can increment. Am I using the correct function at all (transition) ?
I also tried
if(event.target.id === "button") {
if(!box.classList.contains('open')){
box.classList.add('open');
}else {
box.classList.remove('open');
}
}
but that seems to move the box back and forth repetitively.
Does anyone have any tips or ideas? (I am adding the Javascript tag, since I suspect this problem may potentially be solved in JS directly).
You can store the x position in a variable, increment by 20 every click and apply it to the transform style property:
var x = 0, box = document.querySelector('.box');
button.addEventListener('click', function() {
x += 20, box.style.transform = `translateX(${x}px)`;
})
.box {
width: 100px;
height: 100px;
background-color: #000;
transition: 1s;
}
<div class="box"></div>
<button id="button">button</button>
Just keep on adding Using javascript style
var button = document.querySelector('#button');
button.onclick = function () {
document.querySelector('.box').style.transform += "translateX(20px)";
};
.box {
width: 100px;
height: 100px;
background-color: #000;
transition: transform 1s;
}
<div class="box"></div>
<button id="button">button</button>
Using javascript style would answer your question. There is CSS style for javascript like box.style.transform = 'translateX(20px)'. In Javascript you can calculate styles values(Of course in CSS changing values with calculating is possible). So I added some codes like below.
var box = document.querySelector('.box');
let cnt = 0;
document.addEventListener("click", (event) => {
if (!event.target.closest("button")) return;
if(event.target.id === "button") {
console.log(box.style.transform)
cnt++;
console.log(cnt)
box.style.transform = `translateX(${20 * cnt}px)`;
}
});
.box {
width: 100px;
height: 100px;
background-color: #000;
transition: transform 1s;
}
.move {
transform: translateX(20px);
}
.move1 {
transform: translateX(-20px);
}
<div class="box"></div>
<button id="button">button</button>

how to change the color of the navbar after scrolling

I want my navbar to be transparent, but when the user scrolls a bit I want it to change to a solid color and I am using bootstrap for the navbar, I have done the code that is needed with javascript.
I had this javascript in my HTML file, but it doesn't seems to work and I don't really know why
<script>
var myNav = document.getElementById("mynav");
window.onscroll = function() {
use strict";
if (document.body.scrollTop >= 100) {
myNav.classList.add("scroll");
} else {
myNav.classList.remove("scroll");
}
};
</script>
and I have also added the CSS code.
.scroll {
background-color: transparent !important;
transition: all 0.5s ease-in;
}
I don't know why it doesn't work, it is not displaying any errors, I have also manually put the class and it worked so the problem is from the js code and not the CSS.
Use scrollY property of Window object.
See the Snippet below:
var myNav = document.getElementById("mynav");
window.onscroll = function() {
if (window.scrollY >= 100) {
myNav.classList.add("scroll");
} else {
myNav.classList.remove("scroll");
}
};
.scroll {
background-color: transparent !important;
transition: all 0.5s ease-in;
}
.main-container{
height: 1000px;
}
#mynav{
position: fixed;
background-color: gray;
height: 50px;
margin:0 auto;
top: 0;
bottom:0;
line-height: 50px;
padding:5px;
width: 100%;
}
<div class="main-container">
<div class="mynav" id="mynav">
Hello World! this is mynav
</div>
</div>
Try using window.scrollY instead of document.body.scrollTop.
if (window.scrollY >= 100)
You can also use document.documentElement.scrollTop. It's the html element that actually scrolls, not the body. Typically document.body.scrollTop will always be 0.

Make div fly with animation to another DOM position

I am moving an <img> element (the octopus) from the large gray <div> above (#large) to the small orange <div> below (#small) using
$(document).on("click", "#large > img", function() {
$(this).appendTo("#small");
});
This works great but I want it to transition smoothly and to 'fly' over so it will slowly interpolate its coordinates and size.
I tried adding a CSS transition
img { transition: all 3s; }
to my <img>, but that won't work as the image is readded to the DOM and not moved.
How can such animation be established?
JS Fiddle
Using the jQuery .append method won't allow you to animate the element between the 2 states.
Here is an example with an animation using CSS transition and the scale() function. This example also uses the transform-origin property to change the position the of the image on the "big" state. Fiddle here.
$(document).on("click", "img", function() {
$(this).toggleClass("big");
});
div {
margin: 20px;
padding: 10px;
}
#large {
width: 600px;
height: 400px;
background-color: gray;
}
#small {
width: 120px;
height: 90px;
background-color: orange;
}
img {
width: 100%;
height: 100%;
transition: transform .3s ease-out;
transform-origin: 0 129px;
}
img.big {
transform: scaleX(5) scaleY(4.4);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="large">
</div>
<div id="small">
<img src="https://ak.picdn.net/assets/cms/5bb580387901a83212e3028ab5a2fa8fb1153d7b-img_offset_2x.jpg" />
</div>
Note that :
you will need to add vendor prefixes to the transition, transform and transform-origin properties depending on the browsers you need to support.
this technique relies on the fact you are using hard values (in pixels). It would be possible to make this responsive (using percent values for widths, margins and paddings) but it will need more calculations.
i made a responsive solution ( so i think ) using JQ . check it out below or in jsFiddle
first i cached all the necessary selectors for cleaner and concise code .
the -20 is because of the div { margin-top:20px}`
there i calculated the TOP offset of both divs in relation to the document, then got the width and height of the small div
in the click function first i got the image's top offset so i could compare that with the #small's offset .
so if the image's distance to top is smaller than the #small's distance to top, it means that the img is in the #large div and so i move it using transform:translate giving it an Y-axis value equal to the TOP offset of the #small Div, so the img offset.top ( iOffset ) will become equal to the #small offset.top ( sOffset )
also adding width and height of the #small div to the image
else ( if iOffset is = or bigger than sOffset ) then it means that the image is not in the large div, so i need to translate it back to the offset of the #large div and add width:100% and height:100%
hope i got it right and explained correctly.
let me know if it helps
var Large = $("#large"),
Small = $("#small"),
lOffset = $(Large).offset().top - 20 + 'px',
sOffset = $(Small).offset().top - 20 + 'px',
sWidth = $(Small).width(),
sHeight = $(Small).height()
$(document).on("click", "img", function() {
var iOffset = $(this).offset().top + 'px'
if (iOffset < sOffset) {
$(this).css('transform', 'translate(0,' + sOffset + ')')
.width(sWidth).height(sHeight)
} else {
$(this).css('transform', 'translate(0,' + lOffset + ')')
.width("100%").height("100%")
}
})
div {
margin: 20px;
padding: 10px;
}
#large {
width: 600px;
height: 400px;
background-color: gray;
}
#small {
width: 120px;
height: 90px;
background-color: orange;
}
img {
width: 100%;
height: 100%;
transition: 5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="large">
<img src="https://ak.picdn.net/assets/cms/5bb580387901a83212e3028ab5a2fa8fb1153d7b-img_offset_2x.jpg" />
</div>
<div id="small">
</div>
You need to compute the current dimensions of the image, the target dimensions, and calculate the needed transform.
To make it easier, I will calculate the transform needed to make the new element (the cloned one) look like it is still at the current position.
Later, a standard animation (that just resets scale and position) will do the trick.
I avoided using jQuery so the solution is easier to port
function func (target) {
var image = document.getElementById('image');
var current = image.parentNode;
var rectImage = current.getBoundingClientRect();
var rectTarget = target.getBoundingClientRect();
evalRect (rectImage);
evalRect (rectTarget);
var scaleX = rectImage.width / rectTarget.width;
var scaleY = rectImage.height / rectTarget.height;
var translateX = rectImage.centerX - rectTarget.centerX;
var translateY = rectImage.centerY - rectTarget.centerY;
var dup = image.cloneNode();
var scale = 'scale(' + scaleX + ', ' + scaleY + ') ';
var translate = 'translate(' + translateX + 'px, ' + translateY + 'px) ';
target.appendChild(dup);
dup.style.transform = translate + scale;
current.removeChild(image);
}
function evalRect (rect) {
rect.centerX = rect.left + rect.width * 0.5;
rect.centerY = rect.top + rect.height * 0.5;
}
.container {
border: solid 1px black;
position: relative;
display: inline-block;
}
#container1 {
width: 200px;
height: 100px;
}
#container2 {
width: 400px;
height: 200px;
}
#container3 {
width: 200px;
height: 200px;
}
#image {
background: linear-gradient(45deg, yellow, tomato);
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
animation: adjust 1s forwards;
}
#keyframes adjust {
to {transform: translate(0px, 0px);}
}
<div id="container1" class="container" onclick="func(this)">click me
<div id="image"></div>
</div>
<div id="container2" class="container" onclick="func(this)">click me</div>
<div id="container3" class="container" onclick="func(this)">click me</div>
appendto do not accept animations, but this question maybe helpful for you
appendTo() animation
Just add a transition and change the size and position to match the target. On the transitionend event, append the image to the target element.
// when transition completes
$('img').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(){
// place in container
$('#target').append($('img'));
// set to corner of container
$('img').css({
top: '0',
left: '0'
});
});
// position in corner of target and make size the same
$('img').css({
position: 'absolute',
top: $('#target').offset().top + 'px',
left: $('#target').offset().left + 'px',
height: $('#target').css('height'),
width: $('#target').css('width')
});
#target {
height: 150px;
width: 150px;
border: 1px solid grey;
position: absolute;
top: 350px;
left: 5px;
z-index: 1;
}
img {
position: absolute;
top: 0;
left: 5px;
transition: all 1s;
height: 300px;
width: 300px;
z-index: 5;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=300%C3%97300&w=300&h=300" />
<div id="target">
</div>

How do I make a div go left and then right in javascript? (no jQuery)

This should be simple but I guess no jQuery makes it a bit difficult.
I want to repeat a process where a div goes 100px to the right (with animation) and then 100px to the left (so i want a continuous movement).
There seems to be plenty of jQuery answers to this question yet no pure javascript solution. I'm probably missing something obvious here yet I can't find it.
Here is the code:
var left = 0;
var id = setInterval(function(){goRight()}, 10);
var ed = setInterval(function(){goLeft()}, 10);
function goRight(){
var redpixel = document.getElementById("redpixel");
left++;
redpixel.style.left = left + "px";
if (left>100) {
clearInterval(id)
goLeft();
}
}
function goLeft(){
var redpixel = document.getElementById("redpixel");
left-=1;
redpixel.style.left = left + "px";
if (left<100) {
clearInterval(ed);
goRight()
}
}
HTML:
<button onclick="goRight()">Go Right</button>
<div id="redpixel"></div>
CSS:
.container {
width: 480px;
height: 800px;
border: 1px solid black;
}
#redpixel {
position: absolute;
top: 200px;
left: 0;
background: red;
width: 25px;
height: 25px;
}
Last comments:
The animation starts without me calling any function (without using the button), how is that possible?
The animation works but stops when it hits the first 100px.
(Additional question) - if i put the var redpixel out of the function it doesn't work at all, why?
All help appreciated, thanks!
The problem with your code is that you set left and right animations at the same time, and the left one is cleared immediately because left<100. Fixed code:
var left = 0,
id = setInterval(goRight, 10);
ed;
function goRight() {
var redpixel = document.getElementById("redpixel");
left++;
redpixel.style.left = left + "px";
if (left > 100) {
clearInterval(id);
ed = setInterval(goLeft, 10);
}
}
function goLeft() {
var redpixel = document.getElementById("redpixel");
left -= 1;
redpixel.style.left = left + "px";
if (left < 1) {
clearInterval(ed);
id = setInterval(goRight, 10);
}
}
#redpixel {
position: absolute;
top: 50px;
left: 0;
background: red;
width: 25px;
height: 25px;
}
<div id="redpixel"></div>
One more point, is as demonstrated by Adjit it really makes sense to look at CSS approach as simpler and more effective.
You don't need any JavaScript at all actually, and it is quite simple to do with CSS3.
Just need to set up keyframes and animation like so: (obviously including the necessary browser compatibility)
#box {
height: 100px;
width: 100px;
background: red;
position: relative;
animation: waver 2s infinite;
-webkit-animation: waver 2s infinite;
}
#keyframes waver {
0% {left: 0px;}
50% {left: 100px;}
100% {left: 0px;}
}
#-webkit-keyframes waver {
0% {left: 0px;}
50% {left: 100px;}
100% {left: 0px;}
}
See this fiddle for an example: http://jsfiddle.net/bwsd3eoy/

Categories

Resources