Tread sofly, I've just started and couldn't find a similar problem... at least what I think would be the solution ;)
So I've got a robot written in HTML and CSS and I want to animate its lips on click. The idea is to make it like a kiss shape, so I need to scale it to a tiny size(1) and then put it back to normal (2). For now I don't really know how to do it. I tried just changing the width and height size but it didn't work. My guess is I need to do something with the transfort scaleX and scaleY but no idea what and how.
This is what I've already got:
var lips = document.getElementById("lips");
lips.addEventListener("click", kiss);
function kiss(e) {
var partRobo = e.target;
var sizeX= 1;
var sizeY= 1;
var id = setInterval(action, 20);
function action() {
partRobo.style.transform = scale(sizeX, sizeY);
sizeX-0.1;
sizeY-0.1;
if (sizeX === 0 && sizeY=== 0) {
clearInterval(id);
}
}
}
There are many ways to do this, and here is one of them
document.querySelector("div").addEventListener("click", function () {
this.style.transform = "scale(1.2)";
setTimeout(()=>{
this.style.transform = "scale(1)";
},500)
});
div {
width: 100px;
height: 100px;
background-color: red;
transition: cubic-bezier(0, 2.28, 0.95, 0.69) 0.5s;
}
<div></div>
You can use a CSS animation, JS would be needed only for triggering the animation. Of course you can change the duration, easing function and other params of the animation.
#lips {
height: 50px;
width: 100px;
background: red;
cursor: pointer;
}
.kiss {
animation: kiss 4s;
}
#keyframes kiss {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<div id="lips" onclick="this.classList.add('kiss')"></div>
I would make a css rule along the lines of
.lips-kiss {
transform: scale(0.6);
}
and then in the function kiss, apply and remove the class. So your code would look like,
var lips = document.getElementById("lips");
lips.addEventListener("click", kiss);
function kiss() {
lips.classList.add('lips-kiss');
setTimeout(() => {
lips.classList.remove('lips-kiss');
}, 20);
}
Related
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"/>
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..
I have the following code which is going to fade some images but I am interested if there is a way to handle this in CSS.
$("#top").stop(true, true).delay(0).fadeTo(fadeTime * 100, 0);
$("#back").stop(true, true).delay(0).fadeTo(fadeTime * 100, 1, function () {
if (curLoop < loops) {
if (curImg < imgNo) {
prevImg = curImg
curImg++
} else {
prevImg = imgNo
curImg = 1;
curLoop++console.log("LOOP");
}
document.getElementById("back").style.opacity = 0;
document.getElementById("top").style.opacity = 1;
document.getElementById("back").src = "frames/frame_" + curImg + ".jpg";
document.getElementById("top").src = "frames/frame_" + prevImg + ".jpg";
} else {
console.log("STOPPED");
window.clearInterval(myVarSTD);
}
if (!initialized) {
myVarSTD = setInterval(function () {
startAnimation()
}, delay * 1000);
initialized = true;
}
});
You can't loop through image sources in a pure CSS animation but the below fade effect is possible with CSS3 animations. Here the front and back images are absolutely positioned and using opacity animation they are faded in and out in an infinite loop. I have used 2 div with background-image but you could do the same for img element also.
Refer inline comments within the snippet for more explanation of the animation's CSS code.
.front,
.back {
position: absolute; /* absolute positioning puts them one on top of other */
height: 200px;
width: 200px;
animation: fade-in-out 10s linear infinite; /* first is animation keyframe's name, second is the duration of the animation, third is timing function and fourth is iteration count */
}
.front {
background-image: url(http://lorempixel.com/200/200/nature/1);
}
.back {
background-image: url(http://lorempixel.com/200/200/nature/2);
z-index: -1; /* keeps the element behind the front */
animation-delay: 5s; /* delay is equal to half the animation duration because the back has to fade-in once the front has faded-out at 50% */
}
#keyframes fade-in-out { /* animation settings */
0%, 100% {
opacity: 1; /* at start and end, the image is visible */
}
50% {
opacity: 0; /* at the mid point of the animation, the image is invisible */
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='front'></div>
<div class='back'></div>
Yes, it is. Your code capture some elements using getElementById as back and top.
You can use the following code to change CSS properties of those elements:
$('#back').css({'opacity':'1'});
Implemented in your code:
$("#top").stop(true, true).delay(0).fadeTo(fadeTime*100, 0);
$("#back").stop(true, true).delay(0).fadeTo(fadeTime*100, 1, function(){
if(curLoop<loops){
if(curImg<imgNo){
prevImg = curImg
curImg++
}else{
prevImg = imgNo
curImg = 1;
curLoop++
console.log("LOOP");
}
$('#back').css({'opacity':'0'});
$('#top').css({'opacity':'1'});
document.getElementById("back").src = "frames/frame_"+curImg+".jpg";
document.getElementById("top").src = "frames/frame_"+prevImg+".jpg";
}else{
console.log("STOPPED");
window.clearInterval(myVarSTD);
}
if(!initialized){
myVarSTD = setInterval(function(){startAnimation()},delay*1000);
initialized = true;
}
});
jQuery Transit is an awesome plugin which mimics jQuery's animation functions but in CSS. You get a much higher framerate using CSS too!
I tried the CSS route but all new to me and learning still but cannot seem to work it out. My JS below switches the background position to show a new image in the sprite every 1 second but wondering if anyone knew how I can kind of give it a small scale effects so when it changes grows a little then back to normal size before change to the next background position?
JS:
// Avatar animations
var avatarInterval;
function startAvatarAnimation() {
var i = 0;
var avatarSpeed = 500;
var avatarCount= 11;
var avatarHeight = 250;
var avatarTotalHeight = 2750;
avatarInterval = setInterval(function(){
i++;
if(i > 11){
i = 0;
}
$(".avatars").css({'background-position' : '0 -' + (i*avatarHeight) + 'px' });
$(".avatars").toggleClass('scaleIn', 'scaleOut');
}, avatarSpeed);
return false;
}
function stopAvatarAnimation(){
clearInterval(avatarInterval);
$(".avatars").css({'background-position' : '0 0' });
return false;
}
JS below switches the background position to show a new image in the
sprite every 1 second but wondering if anyone knew how i can kind of
give it a small scale effects so when it changes grows a little then
back to normal size before change to the next background position?
Try utilizing transition at css , setting duration to half of avatarSpeed or half of total duration of background-position effect ; setting transitionend event at .one() to prevent recursive call to transitionend handler , .removeClass() , .addClass() to toggle scale effect defined at css
css
.avatars {
transition: transform 500ms ease-in-out;
width: 100px;
height: 100px;
background-image: url(/path/to/background-image);
}
.avatars.scaleIn {
transform: scale(1.1, 1.1);
}
.avatars.scaleOut {
transform: scale(1.0, 1.0);
}
js
// Avatar animations
var avatarInterval;
function startAvatarAnimation() {
var i = 0;
var avatarSpeed = 500;
var avatarCount= 11;
var avatarHeight = 250;
var avatarTotalHeight = 2750;
avatarInterval = setInterval(function(){
i++;
if(i > 11){
i = 0;
}
$(".avatars").css({'background-position' : '0 -' + (i*avatarHeight) + 'px' })
.removeClass("scaleOut").addClass("scaleIn")
.one("transitionend", function() {
$(this).removeClass("scaleIn").addClass("scaleOut");
});
}, avatarSpeed);
return false;
}
$(".avatars").on("click", function() {
$(this).removeClass("scaleOut").addClass("scaleIn")
.one("transitionend", function() {
$(this).removeClass("scaleIn").addClass("scaleOut");
})
})
.avatars {
transition: transform 500ms ease-in-out;
width: 100px;
height: 100px;
background-image: url(http://lorempixel.com/100/100/nature);
}
.avatars.scaleIn {
transform: scale(1.1, 1.1);
}
.avatars.scaleOut {
transform: scale(1.0, 1.0);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class="avatars"></div>
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'
}