Changing the different Keyframes in css using Javascript - javascript

I'm creating a small website that tells a story by using css and sprite animation.
The problem I'm encountering is that I do not know how to change the keyframes once I have set them in the css files expecially while I try to make it compatible with all of the browsers.
Here is what I have,
the main part of the css:
#ManSprite{
overflow:hidden;
width:200px;
height:200px;
position:absolute;
top:280px;
left:140px;
background-image: url("http://imageshack.com/a/img571/1710/2r1h.png");
z-index:99;
animation: play .4s steps(2) infinite;
-webkit-animation: play .4s steps(2) infinite;
-moz-animation: play .4s steps(2) infinite;
-ms-animation: play .4s steps(2) infinite;
-o-animation: play .4s steps(2) infinite;
}
#keyframes play {
from { background-position: -200px; }
to { background-position: -600px; }
}
#-webkit-keyframes play {
from { background-position: -200px; }
to { background-position: -600px; }
}
#-moz-keyframes play {
from { background-position: -200px; }
to { background-position: -600px; }
}
#-ms-keyframes play {
from { background-position: -200px; }
to { background-position: -600px; }
}
#-o-keyframes play {
from { background-position: -200px; }
to { background-position: -600px; }
}
Then in my javascript I would like to change the background position for every keyframe but I'm not sure how to change it. I've tried the following but it does not work.
var StoryPart = 0;
var fromArray = new Array("0px", "-200px", "-800px", "-1400px", "-2200px", "-3200px");
var toArray = new Array("0px", "-600px", "-1400px", "-2200px", "-3230px", "-4000px");
var stepsArray = new Array(0, 2, 3, 4, 5, 4);
function nextBtn() {
StoryPart++;
startChange();
}
// begin the new animation process
function startChange() {
// remove the old animation from our object
document.getElementById('ManSprite').style.webkitAnimationName = "none";
document.getElementById('ManSprite').style.animationName = "none";
document.getElementById('ManSprite').style.msAnimationName = "none";
document.getElementById('ManSprite').style.oAnimationName = "none";
document.getElementById('ManSprite').style.mozAnimationName ="none";
// call the change method, which will update the keyframe animation
setTimeout(function () { change("play", fromArray[StoryPart], toArray[StoryPart]); }, 0);
}
// search the CSSOM for a specific -webkit-keyframe rule
function findKeyframesRule(rule) {
// gather all stylesheets into an array
var ss = document.styleSheets;
// loop through the stylesheets
for (var i = 0; i < ss.length; ++i) {
// loop through all the rules
for (var j = 0; j < ss[i].cssRules.length; ++j) {
// find the -webkit-keyframe rule whose name matches our passed over parameter and return that rule
if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule
|| ss[i].cssRules[j].type == window.CSSRule.KEYFRAMES_RULE && ss[i].cssRules[j].name == rule
)
return ss[i].cssRules[j];
}
}
// rule not found
return null;
}
// remove old keyframes and add new ones
function change(anim, fromValue, toValue) {
// find our -webkit-keyframe rule
var keyframes = findKeyframesRule(anim);
// remove the existing 0% and 100% rules
keyframes.deleteRule("0%");
keyframes.deleteRule("100%");
// create new 0% and 100% rules with random numbers
keyframes.insertRule("0% {background-position: " + fromValue + ";}");
keyframes.insertRule("100% {background-position: " + toValue + ";}");
// assign the animation to our element (which will cause the animation to run)
document.getElementById('ManSprite').style.webkitAnimationName = anim;
document.getElementById('ManSprite').style.animationName = anim;
document.getElementById('ManSprite').style.msAnimationName = anim;
document.getElementById('ManSprite').style.oAnimationName = anim;
document.getElementById('ManSprite').style.mozAnimationName = anim;
}
For some reason in Chrome, it doesn't find any cssRule inside the findKeyframesRule method, but in firefox it does. Firefox crashes, however, on this line
keyframes.insertRule("0% {background-position: " + fromValue + ";}");
which confuses me because it was able to use deleteRule well.
Anyone ever work with these?

Related

Animate a car using jquery and css using live acceleration data

I have a basic car model that is animated using CSS.
On the other hand, I have a live data stream consists of acceleration on the x and z-axis The data are retrieved from a remote server using server-sent-event (sse). I want to be able to use the acceleration data to animate the car accordingly. The goal is to move the car forward up/down using accelerationZ and right/left using accelerationX.
This is how the car look like in 2D:
I am just not sure how to include continuously changing live data in css and make the animation changes according to the input data so the car can accelerate and decelerate.
This is the current code of the car simulation model based on html/css/JS. Currently I am using constant value for track velocity and wheel rotation speed like shown below:
.track{
height:60vh;
width:800vw;
background-image: url(./img/track.png);
position:absolute;
top:70vh;
background-repeat: repeat-x;
animation: carMove linear 13s infinite;
}
.car{
height: 500px;
width: 500px;
background-image: url(./img/miningtruck.png);
background-size: cover;
background-repeat: no-repeat;
position: absolute;
left: 500px;
bottom:15vh;
animation: shake linear .5s infinite;
}
.wheel1 img{
width: 210px;
position: relative;
top:215px;
left:42px;
animation: wheelRotation linear .5s infinite;
}
.wheel2 img{
width: 210px;
position: relative;
top: -0px;
left: 275px;
animation: wheelRotation linear .5s infinite;
My concern is how to replace the 13s or .5s by a dynamically changing value accX and accZ
This is how I get the live feed using sse to get accx and accz:
// globally initial the sensorAddr, accX and accZ to be able to use in other function
// these values comes from the onMsg function
let sensorAddr = 0;
let accX = 0;
let accZ = 0;
// how to include the jquery code here to send
// the acceleration to the css to dynamically animate
// the vehicle
StartRetrieveLiveData();
function onMsg(event) {
var received_msg = event.data;
var obj = JSON.parse(JSON.parse(received_msg));
if (obj !== null) {
if (
obj.hasOwnProperty("DataMapChangedObjectsAddressValue") &&
obj["DataMapChangedObjectsAddressValue"][0]["DataMapAddress"] !==
undefined
) {
sensorAddr =
obj["DataMapChangedObjectsAddressValue"][0]["DataMapAddress"];
if (sensorAddr == 1) {
accX =
obj["DataMapChangedObjectsAddressValue"][0]["Value"];
console.log("AccX", accX);
}
if (sensorAddr == 3) {
accZ =
obj["DataMapChangedObjectsAddressValue"][0]["Value"];
console.log("AccZ", accZ);
}
if (sensorAddr == 204) {
timeStamp =
obj["DataMapChangedObjectsAddressValue"][0]["Value"];
}
}
}
}
function onOpen(e) {
console.log("SSE connected");
}
function onError(e) {
// console.log(`[error] ${error.message}`);
if (e.eventPhase == EventSource.CLOSED) this.source.close();
if (e.target.readyState == EventSource.CLOSED) {
console.log("SSE Disconnected");
} else if (e.target.readyState == EventSource.CONNECTING) {
console.log("SSE Connecting ...");
}
}
function StartRetrieveLiveData() {
if (!!window.EventSource) {
this.source = new EventSource("/sse");
} else {
console.log("Your browser doesn't support SSE");
}
this.source.addEventListener("message", e => this.onMsg(e));
this.source.addEventListener("open", e => this.onOpen(e), false);
this.source.addEventListener("error", e => this.onError(e), false);
}
I would appreciate any feedback. Any hint or a better approach would be great. I just want a head starter making any significant to the code.

Softly Transition HTML Images with Javascript

I am trying to use JS to switch images, the code does what it is supposed to and switches the images, however, I want a fade out-fade in effect to the image transition. I tried to declare a CSS Transition for opacity and change the opacity first, which didn't work, then I tried to change the opacity with plain JS, however that didn't work either, what would be the best way to achieve this?
My Poorly Designed Image Change Code:
image = [
"image_1.png",
"image_2.png",
"image_3.jpeg"
];
updateImg = async() => {
console.log("Pulling Image Change")
var img = document.getElementById("img-pan");
console.log(`Got ${img} with current src ${img.src}`)
var exi_bool = false;
for(i = 0; i < image.length - 1; i++) {
if(img.src.endsWith(image[i])) { exi_bool = true; console.log(`Found current src to == image[${i}]`) ;break; }
}
if(!exi_bool) {
img.src = image[0];
}else {
if(i < image.length - 1) { i++ }else { i = 0 }
img.src = image[i];
}
}
If I understood well, before you replace the image add a class that define the opacity to 0.3 for example.
document.getElementById("MyElement").classList.add('MyClass');
when the image change you remove the class.
document.getElementById("MyElement").classList.remove('MyClass');
Note that your image has to be set on css as opacity: 1 and transition x seconds.
Will use css style animation, just change class name, is simple to use and build.
but when css animation start to change css property,no way could change same property but other css animation.
let imgArray = [
'https://fakeimg.pl/100x100/f00',
'https://fakeimg.pl/100x100/0f0',
'https://fakeimg.pl/100x100/00f'
];
let img = document.getElementsByTagName('img')[0];
//only two function
async function fadeOut(element) {
element.className = 'fade-out';
}
async function fadeIn(element) {
element.className = 'fade-in';
}
//
let i = 0;
function loop() {
img.src = imgArray[i % 3];
i++;
fadeIn(img).then(res => {
setTimeout(() => {
fadeOut(img).then(res => {
setTimeout(() => {
loop();
}, 1000);
})
}, 1000);
})
}
loop();
img {
position: relative;
left: 0; /* or use transform */
opacity: 1;
transition: 1s;
width: 100px;
display: block;
margin: auto;
}
.fade-in {
animation: fade-in 1s;
}
#keyframes fade-in {
0% {
left: 100px; /* or use transform */
opacity: 0;
}
100% {
left: 0; /* or use transform */
opacity: 1;
}
}
.fade-out {
animation: fade-out 1s both;
}
#keyframes fade-out {
0% {
left: 0; /* or use transform */
opacity: 1;
}
100% {
left: -100px; /* or use transform */
opacity: 0;
}
}
<img src="https://fakeimg.pl/100x100/#f00">

How to get #keyframes current transform-value using javascript?

ANSWER for the translateX value (thanks to Albert Portnoy):
var style = window.getComputedStyle(div);
var xVal = parseFloat(style.transform.split(" ")[4].replace(",", ''));
How do I get the current transform value of an element which is animated with #keyframes in javascript?
There doesn't seem to be a change of the css value I used (translateX) when it is animated or it has finished
<style>
#keyframes anim {
from {
transform: translateX(0px)
}
to {
transform: translateX(400px)
}
}
div {
width: 50px;
height: 50px;
background: black;
animation: anim 5s forwards linear;
}
</style>
<div></div>
<script>
var div = document.getElementsByTagName('div')[0];
function loop() {
setTimeout(function () {
console.log(div.style);
loop()
}, 100)
}
loop()
</script>
You can use (the not yet standard) feature CSSMatrix.
Another helpful explanation about Calculating element vertex data from CSS transforms
Here is a running example, note that i used requestanimationFrame instead of a callback loop with setTimeout to run the code on each frame.
var el = document.getElementById('root');
let count = 0;
function loop() {
requestAnimationFrame(() => {
var style = window.getComputedStyle(el);
var matrix = new WebKitCSSMatrix(style.webkitTransform);
console.log('translateX: ', matrix.m41);
if(count > 100){
// just to stop the infinite loop...
return;
}
count++;
loop();
})
}
loop()
#keyframes anim {
from {
transform: translateX(0px)
}
to {
transform: translateX(400px)
}
}
#root {
width: 50px;
height: 50px;
background: black;
animation: anim 5s forwards linear;
}
<div id="root"/>

How to delete this element after it runs the animation?

The delelement code is working fine but I want to delete the element after the css animation is finished for "delorean", then I need the animation to start again every time the button is clicked, how can I use my "delelement" code to achieve this? thanks.
function touchDelorean(event) {
event.preventDefault();
if (showDelorean == true) {
delorean();
}
}
function delorean() {
var image = document.createElement("img");
image.id = "delorean";
image.src = "Images/delorean/" + where + ".png";
document.getElementById("deloreanContainer").appendChild(image);
// delelement("deloreanContainer");
}
function delelement(elem) {
var element = document.getElementById(elem);
while (element.firstChild) { element.removeChild(element.firstChild); }
}
CSS
#delorean {
position: absolute;
top: 0%;
left: 0%;
width: 29%;
height: 9.8%;
opacity: 1.0;
-webkit-animation: delorean_anim 10s linear;
-webkit-animation-direction: normal, horizontal ;
-webkit-animation-timing-function: cubic-bezier(1, 0, 1, 0);
}
#-webkit-keyframes delorean_anim {
0% { -webkit-transform: translate(-24vw, 0vw) scale(0.6, 0.6) rotate(0deg) }
20% { -webkit-transform: translate(137vw, 36vw) scale(3.5, 3.5) rotate(0deg) }
100% { -webkit-transform: translate(137vw, 36vw) scale(3.5, 3.5) rotate(0deg)}
}
Use setTimeout() and do a time equal or greater to that of your CSS Transition time... so if:
transition: all 3s ease;
then...
function delorean() {
var image = document.createElement("img");
image.id = "delorean";
image.src = "Images/delorean/" + where + ".png";
document.getElementById("deloreanContainer").appendChild(image);
setTimeout( function { delelement("deloreanContainer"); }, 3000 );
}
function delelement(elem) {
var element = document.getElementById(elem);
while (element.firstChild) { element.removeChild(element.firstChild); }
}
On the element that runs the animation, given that it is image you can add an eventlistener..
// Chrome, Safari and Opera
image.addEventListener('webkitAnimationEnd', function() {
image.remove()
});
// Standard syntax
image.addEventListener('animationend', function() {
image.remove()
});
If you're dealing with a css transition and not a css animation then you're looking for transitionend. These events can however be unreliable sometimes so if you know the length of the animation, you're better off with setTimeout as suggested above.
In react.js the infamous CSSTransitionGroup component had many problems due to the transition event not fireing as expected. They "solved" the problem by requiring specifying transition timeout values for the component..

Changing css animation in javascript

I have a blinking red box in my html it uses css animations. I want to to be able to change it from blinking red and white to green and white. I know this can be done on id elements by using getElementbyId but how would get access to the green aminated box in the css.
The red box looks like this:
#-webkit-keyframes 'noConnection'
{
1% { background-color: red; }
33% { background: white; }
66% { background: red; }
100% { background: white; }
}
The green is this:
#-webkit-keyframes 'Connection'
{
1% { background-color: green; }
33% { background: white; }
66% { background: green; }
100% { background: white; }
}
The animate looks like this:
#animate {
height: 15px;
width: 15px;
}
.cssanimations #animate {
-webkit-animation-direction: normal;
-webkit-animation-duration: 5s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-name: Connection;
-webkit-animation-timing-function: ease;
and I think I have to change the attribute -webkit-animation-name: from javascript to do this but I dont know how to get a handle on it to change it.
Or would I be better off creating a duplicate #animate and renaming it using the getElementById?
Here is a simple web page that demonstrates how to use Javascript to modify a CSS animation. It contains a simple div, and a little Javascript causes the div to move randomly around the page.
In your specific case, you just need to touch up the line that calls "insertRule" to insert your new blinking rule.
<html><head><style></style></head>
<body>
<div id="mymovingdiv">
<h1>Moving Div</h1>
<p>And some little text too</p>
</div>
<script>
function animatedMove(id, xStart, yStart, xEnd, yEnd, secs)
{
// Remove any CSS rules inserted by a previous call to this method
let rulename = `AnimMove${id}`;
let ss = document.styleSheets; // all stylesheets
for (let i = 0; i < ss.length; ++i) { // for each stylesheet...
for (let j = ss[i].cssRules.length - 1; j > 0; j--) { // for each rule...
if (ss[i].cssRules[j].name === rulename) { // does the name match?
ss[i].deleteRule(j);
}
}
}
// Insert a CSS rule for this animation
document.styleSheets[0].insertRule(`#keyframes ${rulename} { 0% { left: ${xStart}px; top: ${yStart}px; } 100% { left: ${xEnd}px; top: ${yEnd}px } }`);
// Remove any CSS rules inserted by a previous call to this method
for (let i = 0; i < ss.length; ++i) { // for each stylesheet...
for (let j = ss[i].cssRules.length - 1; j > 0; j--) { // for each rule...
if (ss[i].cssRules[j].name === rulename) { // does the name match?
ss[i].deleteRule(j);
}
}
}
// Insert a CSS rule for this animation
document.styleSheets[0].insertRule(`#keyframes ${rulename} { 0% { left: ${xStart}px; top: ${yStart}px; } 100% { left: ${xEnd}px; top: ${yEnd}px } }`);
// assign the animation to our element
let el = document.getElementById(id);
el.style.position = 'absolute';
el.style.animation = `${rulename} ${secs}s`;
// Make the element stay where the animation ends
el.style.left = `${xEnd}px`;
el.style.top = `${yEnd}px`;
// Re-clone the element, to reset the animation
el.parentNode.replaceChild(el.cloneNode(true), el);
}
let x = 0;
let y = 0;
function randomMove()
{
let newX = Math.floor(Math.random() * 800);
let newY = Math.floor(Math.random() * 600);
animatedMove('mymovingdiv', x, y, newX, newY, 1);
x = newX;
y = newY;
}
setInterval(randomMove, 1000);
</script>
</body>
</html>
The easiest way to do this is to created two classes in CSS and then toggle between the two.
So something like:
.connection {
animation: 'Connection' 5s ease infinite
}
.no-connection {
animation 'noConnection' 5s ease infinite
}
And then use Javascript to toggle between the two
function toggleConnectionStatus(){
var element = document.getElementById('animate');
if(/* connection is active */) element.className = 'connection';
else element.className = 'no-connection'
}

Categories