If I have a CSS keyframe animation like this
#keyframes flash-red {
50% {
background: #f00;
}
}
#goflash.anm-flash {
animation-name: flash-red;
animation-duration: .5s;
background: rgba(255, 0, 0, 0);
}
Then I can always trigger the animation like this:
var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
}, 50);
Is there any way to override the animation-duration/animation-timing-function to be dependent on JavaScript? I'd like to be able to say something like gf.animate("flash-red", "50%") to make the background of gf red, or gf.animate("flash-red", "75%") to make the background more like rgba(255, 0, 0, .5).
Ideally, the same technique would work for transitions. gf.transitionTo("new-class", "50%") would show the element as half way transitioned.
Obviously the flash-red is just an example—I'd like to be able to do this with any animation.
With the built-in animation:
Unfortunately, no
The internals of the transition isn't exposed for JavaScript so you cannot tap into it to set or get the data. And this is for a purpose - if the data were exposed it would mean reduced efficiency as the JavaScript event queue had to be updated. As JS is single-threaded and the animation goes on a separate thread you'll would soon loose the benefit of it running in compiled code internally on a separate thread.
You can however make your own transitions. This involve calculation transitions yourselves.
This is not as complicated as it sounds like as you simply use an interpolation formula for what you want to animate:
current = source + (destination - source) * fraction;
For example, for color you can use it with the color component. Lets assume we have color objects with properties r, g, b:
var color1 = {r: 100, g: 200, b: 55}; //some random color
var color2 = {r: 0, g: 100, b: 100};
var fraction = 0.5; //0-1
Here the current RGB would be:
r = color1.r + (color2.r - color1.r) * fraction;
g = color1.g + (color2.g - color1.g) * fraction;
b = color1.b + (color2.b - color1.b) * fraction;
For positions:
var pos1x = 100;
var pos1y = 100;
var pos2x = 500;
var pos2y = 250;
var fraction = 1; //0-1
posX = pos1x + (pos2x - pos1x) * fraction;
posY = pos1y + (pos2y - pos1y) * fraction;
And so forth.
By making wrapper functions you can easily calculate these and even put them in a loop to animate them.
Example function for setting transition between color 1 and color 2.
Style can be ie. backgroundColor, color etc.:
function setColor(element, style, color1, color2, fraction) {
var r = color1.r + (color2.r - color1.r) * fraction;
var g = color1.g + (color2.g - color1.g) * fraction;
var b = color1.b + (color2.b - color1.b) * fraction;
element.style[style] = 'rgb(' + (r|0) + ',' + (g|0) + ',' + (b|0) + ')';
}
(the r|0 is simply cutting off the decimal part).
And for position, for example:
var pos1 = {x: 0, y: 0};
var pos2 = {x: 200, y: 0};
function setPosition(element, pos1, pos2, fraction) {
var x = pos1.x + (pos2.x - pos1.x) * fraction;
var y = pos1.y + (pos2.y - pos1.y) * fraction;
element.style.left = x + 'px';
element.style.top = y + 'px';
}
A simple demo (use Chrome or Aurora 23 to see sliders, slider comes in next version of FF 23).
Fiddle
Manually set transition at any point between source and destiny, or animate them.
say you have only one animation over your element gf, you can simply control it with animation-delay and animation-play-state:
gf.__proto__.animate = function(percent) {
this.style["animation-play-state"] = "paused";
this.style["animation-delay"] = (
(parseFloat(this.style["animation-duration"] || 1) * -percent) + "s"
);
};
and you can get the computed style as following:
window.getComputedStyle(gf).background
to step through at any speed:
(function animation(time) {
gf.animate( ((time || 0) % desireSpeed ) / desireSpeed );
requestAnimationFrame(animation);
})();
note: this will override animation-delay from css so you'll probably want to keep it in a vairable and add it as an offset in gf.__proto__.animate().
You can't do that as you want it.
Your only posibility is to change play-state after a given delay.
In your case, since the animation lasts 0.5 seconds, to get the animation at 50% you should set a timeout of 0.25 seconds and then set animation-play-state : paused.
Of course that won't be exactly at 50%, don't trust the precision of this method.
editing
Added demo for webkit:
fiddle
The HTML is trivial
<div id="goflash">TEST</div>
<input type="button" value="animate" onclick="animate()">
And the CSS easy
#goflash {
width: 200px;
height: 200px;
left: 35px;
top: 35px;
position: absolute;
background-color: red;
}
.anm-flash {
-webkit-animation-name: flash;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: 1;
}
#-webkit-keyframes flash {
from { -webkit-transform: rotate(0deg);
background-color: red; }
50% { -webkit-transform: rotate(120deg);
background-color: yellow;}
to { -webkit-transform: rotate(360deg);
background-color: red;
}
}
And the javascript is an extension from what you supplied:
function animate () {
var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
gf.style.webkitAnimationPlayState = "running";
}, 50);
setTimeout(function() {
gf.style.webkitAnimationPlayState = "paused";
}, 2550);
}
You reset the class, after a small pause start the animation, and a calculated delay after the start, you stop it.
Since the animation time was 5s,and the initial delay 50 ms, the second delay has to be (5000/2) + 50.
Since you have set now the play state to paused, to de able to re-run the animation you have to set the state to running again.
Perhaps using CSS DOM to parse animation's intent (if that's even possible?) and then reconstructing everything in JavaScript.
But that's no mean feat!
I wonder if a CSS preprocessor would help constructing code like this. This is very much all in theory.
Yes,
You can just overide the duration or timing of an animation. Hope I understood what you want to do:
http://jsfiddle.net/SEHyW/
var gf = document.querySelector("#goflash"),
animationDuration = '1s'
gf.classList.remove("anm-flash");
setTimeout(function() {
gf.classList.add("anm-flash");
gf.style["-webkit-animation-duration"] = animationDuration;
}, 1000);
Related
I have a simple JS script which listens to keyboard input and displays, at a random position, a short animation of every typed letter fading out and getting smaller.
'use strict'
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
document.addEventListener('keydown', function(e) {
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
.anim {
position: fixed;
text-align: center;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0%;
}
#keyframes fade {
0% { opacity: 100%; font-size: 300px;}
100% { opacity: 0%; font-size: 100px;}
}
By animating the font-size property, each letter gets smaller. However, since its "anchor point" is the top of the div, the visible effect is a letter getting smaller and moving slightly upwards. I would like each letter to shrink towards the vertical center of the div instead.
I can calculate the center the div easily and add the proper top coordinate to the #keyframe property, but I don't know how to modify that property in JS, individually for each div. Is this possible at all via CSS? Or should I rewrite the whole thing in pure JS?
You don't need to adjust the div's top value at all. As there is no border or anything else displayed for the DIV tag itself - just the letter within it - you can adjust either the margin, the border and/or the padding to achieve the same effect as increasing the top value for the DIV. As each of these can be handled within the css transition, you could do something like:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
document.addEventListener('keydown', function(e) {
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key;
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
.anim {
display:block;
position: fixed;
text-align: center;
width:200px;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0;
margin:0px;
}
#keyframes fade {
0% { opacity:1; font-size: 300px;}
100% { opacity:0; font-size: 100px; margin-top:100px;}
}
The initial state of the DIV is with margin:0px. Adding a margin-top setting to the keyframes css, increases this from 0 to 100 during the transition. The effect of that is to push the DIV down - and, as noted above, as nothing is being displayed for the DIV itself, the user will not see it move. Note that I have fixed the width of the DIV at 200px so ensure that everything is always centered horizontally - otherwise the DIV width is based on the width of the character, so would change during transition and the character would move to the left as the centre line changes. I've moved some of the code around to make it easier to test - but the only actual change is in the CSS styling. Also note that opacity is a value from 0 to 1, so should not be shown as a percentage.
UPDATE
Have a look at the following snippet. I think that it may be possible to have random font sizes AND random positions using transform rather than animate.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
function zoomOUT(){
let d = document.getElementById("test");
d.classList.remove("zoomIN");
d.classList.add("zoomOUT");
}
function zoomIN(){
let d = document.getElementById("test");
let dletter = document.getElementById("testletter");
let t = getRandomInt(20, 60) * 10;
let l = getRandomInt(20, 100) * 10;
let fs = getRandomInt(10, 20) * 10;
dletter.innerHTML = letters[getRandomInt(0, 61)];
d.style.top = t + "px";
d.style.left = l + "px";
d.style.fontSize = fs + "%";
d.classList.remove("zoomOUT");
d.classList.add("zoomIN");
}
#test {
position:absolute;
padding: 50px;
margin: 0 auto;
text-align:center;
vertical-align:middle;
}
.zoomIN {
opacity:1;
transform: scale(3);
transition: transform 2s;
}
.zoomOUT {
opacity:0.5;
transform: scale(0.1);
transition: transform 3s;
}
<button onclick="zoomOUT();" z-index=1>Play</button><button onclick="zoomIN();" z-index=1>Restart</button>
<div id="test" class="zoomIN" style="top:300px; left:300px;" z-index=0><div id="testletter" style="font-size:600%; width:100%; height:100%">A</div></div>
Transform seems to keep things in the same place, so there is no need to adjust any top/margin/border/padding settings at all. In fact, the only things that change are the font-size (using scale(..)) and opacity. The size of the font is determined by the code. Note that this requires the character to be in a div within a div. This is just a test, but should give you enough to convert things into your code requirements.
https://jsfiddle.net/j4qcsksy/
function random(min,max){
return Math.round(Math.random() * (max-min) + min);
}
function dropDiv(){
var length = random(0, 54) * 22.5;
var velocity = 3000;
var size = 1.5;
var thisBox = $("<div/>", {
class: "falling-box",
style: `width:${size}%; height:0%;padding-bottom:${size}%;
left:${length}px; transition: transform ${velocity}ms linear`,
});
//insert box element
$(".container").append(thisBox);
//random start for animation
setTimeout(function(){
thisBox.addClass("move");
}, 40 );
//remove this object when animation is over
thisBox.one("webkitTransitionEnd otransitionend oTransitionEnd
msTransitionEnd transitionend",
function(event) {
$(this).remove();
});
}
//falling divs
setInterval(function(){
dropDiv();
}, 1000);
I have made a script where I am randomly generating a bunch of divs along the width of a div container. When these divs are generated, they are assigned a class of "falling-box".
Right now, the "falling-box" divs are black. I want some of the divs to take on the class of "falling-box green" and "falling-box red." I would also like for the red divs to appear less frequently.
I am thinking that I should put all these in an array (or a prototype?) but I am not sure how I should go about it. I have only been coding for 7 weeks, and I am trying my best to learn!
Please follow this below code.
css:
.falling-box{
position:absolute;
top: -150px;
transition: transform 1.5s linear;
z-index:100;
}
In above class please removed the background color.
Javascript:
function dropDiv(){
var color_array = ["black","red","green","yellow"];
var temp_color = color_array[Math.round(Math.random(0,color_array.length + 1))];
var length = random(0, 54) * 22.5;
var velocity = 3000;
var size = 1.5;
// var size = 20; ---->og px
var thisBox = $("<div/>", {
class: "falling-box",
style: 'width:1.5%;height:20%;padding-bottom:${size}%; left:${length}px; transition: transform ${velocity}ms linear',
// style: `width:${size}px; height:${size}px; left:${length}px; transition: transform ${velocity}ms linear`
});
//insert box element
$(".container").append(thisBox);
$(".falling-box").css("background",temp_color);
Here in above function what i did is:
Created an array.
Then get the random number from starting 0 to length of the array and then rounded that number to integer.If we will not use Math.round() function then random number would be like: 0.1486262298191667 So it's better to use Math.round or Other math function.
In above function i have used some static width and height so please remove if you needed.
And last
$(".falling-box").css("background",temp_color);
Using above code i have apply the background color.
If the left half of the screen is a specific rgb value, and the right is another, how would one smoothly transition depending on mouse position between those two on mousemove?
So that when the mouse in at the far left, it's one colour, and far right it's the other. In the middle it would be the half way point.
Something like this: http://jsfiddle.net/j08691/BrZjJ/ - But based on set colours, not arbitrary ones.
var $el = $('div');
var p_x, p_y,
window_width = window.innerWidth,
window_height = window.innerHeight;
var m = {
moving_left: false,
moving_up: false,
last_x: 0,
last_y: 0,
x: 0,
y: 0
};
var colors = {
orange: { r: 238, g: 119, b: 0 },
blue: { r: 40, g: 203, b: 215 }
};
$el.on('mousemove', function(e){
m.x = e.pageX;
m.y = e.pageY;
m.moving_left = (m.x < m.last_x) ? true : false;
m.moving_up = (m.y < m.last_y) ? true : false;
m.last_x = m.x;
m.last_y = m.y;
});
function animateBackground() {
p_width = m.x / window_width;
p_height = m.y / window_height;
p_x = p_width.toFixed(2);
p_y = p_height.toFixed(2);
switch(true) {
// top left
case p_x <= 0.5 && p_y <= 0.5:
fr = colors.orange.r;
fg = colors.orange.g;
fb = colors.orange.b;
break;
// bottom right
default:
fr = colors.blue.r;
fg = colors.blue.g;
fb = colors.blue.b;
break;
}
$el.css({
backgroundColor: 'rgb('+fr+', '+fg+', '+fb+')'
});
requestAnimationFrame(animateBackground);
}
requestAnimationFrame(animateBackground);
https://jsfiddle.net/o32juay5/
I'm using something similar to the above, (except using 4 colours on four corners, but for simplicity...). I just can't work out in my head how to transition between two colours...
Any ideas?
Thanks.
You need to calculate the colors mathematically according to the mouse position.
For each of the R, G, B values: Take values for the two colors, determine the range between them, select a position inside that range according to the mouse position, and combine them into a background-color to be used in CSS.
https://jsfiddle.net/kbpyxxcn/6/
If you want to use this method with four colors, your calculations will become more complicated, unless you also define a color for the center.
Add the CSS property transition and it works:
https://jsfiddle.net/o32juay5/2/
div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-ms-transition: background-color 1s;
-webkit-transition: background-color 1s;
transition: background-color 1s;
}
If you make it with javascript the performance will be less than this css property.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I want to continuously rotate an HTML5 image or div using CSS3 animation effects.
Need the javascript to perform all of the basic functions related to this:
1) Set rotation speed of the image.
2) Get the current rotation value, in degrees.
3) Start and stop rotation.
If possible, please provide a working example of the html, css, js. A js class would be lovely.
Thanks very much.
Here is a working example of some of the features that you asked for.
The features that are not present are pretty easy to implement.
It's a very basic implementation.
Please run it on codepen.io otherwise the stylesheet reference will not work.
http://codepen.io/chocobowings/full/qOOzry/
//console.log(document.styleSheets[2]);
// find the right style sheet //
var rule = document.styleSheets[2].cssRules[1];
//console.log(rule);
function change(){
// first remove the old rules //
rule.deleteRule("0%");
rule.deleteRule("100%");
var angle1 = "-360deg"
var angle2 = "720deg"
// then add new rules //
rule.appendRule("0% { border-radius:0%; transform: rotate("+ angle1 + ");}");
rule.appendRule("90% { border-radius:30%; transform: rotate("+ angle2 + ");}");
// log the variable after the changes //
// console.log(rule);
// log the rules new text //
// you can extract from the cssText any information that you need
// console.log(rule.cssRules[0].cssText);
// console.log(rule.cssRules[1].cssText);
}
function get()
{
//console.log(document.styleSheets[2]);
var el = document.getElementById("a");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"FAIL";
// With rotate(30deg)...
// matrix(0.866025, 0.5, -0.5, 0.866025, 0px, 0px)
console.log('Matrix: ' + tr);
// rotation matrix - http://en.wikipedia.org/wiki/Rotation_matrix
var values = tr.split('(')[1].split(')')[0].split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
var scale = Math.sqrt(a*a + b*b);
console.log('Scale: ' + scale);
// arc sin, convert from radians to degrees, round
var sin = b/scale;
// next line works for 30deg but not 130deg (returns 50);
// var angle = Math.round(Math.asin(sin) * (180/Math.PI));
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
console.log('Rotate: ' + angle + 'deg');
}
.a {
background-color: #344565;
width: 500px;
height: 500px;
position: absolute;
left:30vw;
top: 30vh;
animation: move 20s infinite;
}
#keyframes move {
0% {
border-radius:0%;
transform: rotate(360deg);
}
100% {
border-radius:30%;
transform: rotate(-720deg);
}
}
<button onclick="change()">Change Values</button>
<button onclick="get()">Get Angle</button>
<div class="a" id="a">
</div>
It was hard to explain with words, so I tried to explain with graphics.
There is a div here with its style.
Now if I change its width with 400px here...
because of it is a transformed (rotated) object, something happens and "TOP-LEFT" corner of it, moves down.
Now I want to keep its "TOP-LEFT" position fixed. But I couldnt find a correct correlation to fix it. I guess I need a trigonometric formula using rotation angle.
Also I know it is related with 'scale' and 'transform-origin' and can be easily done with them but I dont want to use any other transformation parameters. Especialy 'transform-origin' because of lack of browser support.
Does anybody here who can help me with the correlation which will be used in JavaScript to fix its corner. Maybe getBoundingClientRect() can be used for this.
Here is the FIDDLE
Thank you.
CSS transforms are really matrices, where transforming the elements are done with
matrix(a, b, c, d, tx, ty).
Then someone clever figured out it would be too complicated for webdesigners to understand such a matrix, so they added shorthand solutions, like transform: rotate() etc.
In other words, if you view the computed styles, there won't be a style whith the rotated degrees, and you can't do element.style.transform and get the rotation angle back again, all you'll get is the matrix.
Since the browsers use a matrix, all browsers that support CSS transform, also support changing the origin of that transform, so if you can rotate the element, you can change the origin.
The exception is Microsoft's filters, but even there you can rotate and change the origin, it's just a little more complicated to figure out.
As it makes no sense to not just change the origin of the transformation, and calculating it yourself would do the exact same thing, only a hundred times more complicated, you should really just add this to the CSS to solve the issue
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
FIDDLE
Just to confirm this, looking at MDN, at the bottom of the following pages, you'll find browser support, and it's just about the same for transform and transform-origin, as you generally never have one without the other
https://developer.mozilla.org/en-US/docs/Web/CSS/transform
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin
As a final note, if it were me, I wouldn't even worry about IE8 and below, as those users are probably used to things looking weird these days anyway.
If you don't want to use transform-origin you can do this :
FIDDLE
$(function () {
var isScaled = false;
$('#box').on('click', function () {
if (isScaled) {
isScaled = false;
$(this).width('200').css({'top':'50px','left':'50px'})
} else {
isScaled = true;
$(this).width('400').css({'top':'24px','left':'46px'});
}
})
});
As other people has stated, you can use transform-origin. However, if you still want to do it via Javascript, I've done it for you in this jsfiddle.
Basically, what I do is to calculate the rotated position of the top left corners of each figure using the matrix transform for rotations (simplified), assuming the center point of the figures as (0, 0), which is, basically, what the browser does. Once I calculate the new positions for the corners, I calculate the difference, and substract that difference from the original left and top positions. I hope you find it instructive.
$(function () {
var isScaled = false;
var box = $('#box');
box.on('click', function () {
if (isScaled) {
isScaled = false;
$(this).width('200');
placeBox(400, 200);
} else {
isScaled = true;
$(this).width('400')
placeBox(200, 400);
}
});
var left = parseInt(box.css('left'));
var top = parseInt(box.css('top'));
var angle = (345 / 360) * 2 * Math.PI; //in radians;
function placeBox(oldWidth, newWidth) {
var midHeight = box.height() / 2;
var midOldWidth = oldWidth / 2;
var midNewWidth = newWidth / 2;
var cos = Math.cos(angle);
var sin = Math.sin(angle);
//rotation center coordinates
var cx1 = left + midOldWidth;
var cx2 = left + midNewWidth;
var cy = top + midHeight;
var mx1 = -midOldWidth * cos + midHeight * sin;
var my1 = -midOldWidth * sin - midHeight * cos;
var mx2 = -midNewWidth * cos + midHeight * sin;
var my2 = -midNewWidth * sin - midHeight * cos;
var difX = cx2 + mx2 - cx1 - mx1;
var difY = my2 - my1;
//now, position the element where it should:
box.css({
left: (left - difX) + 'px',
top: (top - difY) + 'px'
});
}
})
This Fiddle is showing the problem: http://jsfiddle.net/y343Z/19/
If you change the "shadow" size, it is moving alongside it's X Y axis.
Here one possibile soution.
http://jsfiddle.net/y343Z/18/
Just place shadow inside of the tranformed element:
<div id="box">
<div id="box-shadow" style="width:400px;"></div>
</div>
With this CSS:
#box {
width:200px;
height:200px;
position:absolute;
top:50px;
left:50px;
-moz-transform:rotate(345deg);
-webkit-transform:rotate(345deg);
-o-transform:rotate(345deg);
-ms-transform:rotate(345deg);
}
#box-shadow {
width: inherit;
height: inherit;
background-color:silver;
position:absolute;
opacity: 0.3;
}
#box {
background-color:orange;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
-moz-opacity: 0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
}
Just to clarify: I know that is not desired for a real shadow, since this has to be outside of the transformed box. But i think your shadow object is a "helper" that contains handles like in your screenshot.
Edit:
As other user posted, you may also use transform-origin: http://jsfiddle.net/y343Z/20/
transform-origin: left center 0;