Looking to make a specific animation for a div element. I want it to go down (smoothly) and when it reaches the bottom of the screen to come back up (smoothly).
The code I have is as follows:
The Javascript part at the If statement is where I am having difficulties. I want the box to come down and come back up smoothly.
HTML:
<div class="verticalDiv" id="verticalDiv" onclick="verticalMove()"></div>
CSS:
.verticalDiv {
position: absolute;
top: 0px;
right: 500px;
width: 100px;
height: 100px;
margin: 100px auto;
background: red;
}
JS:
myVar1 = setInterval(verticalMove, 50);
v = 0;
function verticalMove() {
redBox = document.getElementById('verticalDiv')
redBox.style.top = v + "px";
if (v >= 0) {
v++;}
if (v === 200) {
v--;
}
console.log(v);
}
I think, best way is to use css animation. You don't have to care about animation logic. Just use keyframes.
Here is example:
HTML
<div id="verticalDiv" class="verticalDiv"></div>
CSS
.verticalDiv {
height: 20px;
width: 20px;
background: red;
}
#keyframes move {
0% { transform: translateY(0); }
50% { transform: translateY(200px); }
100% { transform: translateY(0); }
}
.verticalDiv.move {
animation: move 3s ease-in-out;
}
JS
const verticalDiv = document.getElementById('verticalDiv');
verticalDiv.addEventListener('click', () => {
verticalDiv.classList.toggle('move');
});
WORKING DEMO click on red div to start animation.
BTW If you want animate something. It is always better to animate properties that doesn't force layout updates: transform and opacity.
Other properties, like top, bottom, margin are expensive for browser to animate. You should avoid them if possible.
Read more
You need to differentiate between the two phases, moving down and moving up. It can be a simple true/false boolean, but storing a "speed" or "delta" value (like +/-1) is also a very typical approach.
var v = 0;
var delta=1;
function verticalMove() {
redBox = document.getElementById('verticalDiv')
v += delta;
redBox.style.top = v + "px";
if (v <= 0) delta = 1;
if (v >= 50) delta = -1;
}
function startMove(event) {
setInterval(verticalMove,30);
event.target.onclick="";
}
.verticalDiv {
position: absolute;
top: 0px;
right: 500px;
width: 100px;
height: 100px;
background: red;
}
<div class="verticalDiv" id="verticalDiv" onclick="startMove(event)"></div>
Basically, I am wanting to create a visual representation of the device orientation values alpha, beta, and gamma. So far I have managed to display the values in plain text using innerHTML, but I want to create a series of "bars" for each value. I drew a very crude drawing of what I had in mind:
Basically, I want the bars to move in relation to changes in the alpha, beta and gamma values. This is how my code looks now.
<!DOCTYPE HTML>
<html>
<body>
<p>Alpha: <span id="alpha"></span></p>
<p>Beta: <span id="beta"></span></p>
<p>Gamma: <span id="gamma"></span></p>
<canvas id="myCanvas" width="400" height="400"></canvas>
<script>
// Listen for device orientation event
window.ondeviceorientation = function(eventData)
{
// Show alpha, beta and gamma values
document.getElementById('alpha').innerHTML = Math.round(eventData.alpha);
document.getElementById('beta').innerHTML = Math.round(eventData.beta);
document.getElementById('gamma').innerHTML = Math.round(eventData.gamma);
}
</script>
</body>
</html>
I highly doubt I can do this using inner HTML because I think I would need to use CSS styling. This makes me think that the canvas might work, but I have trouble initializing it using the ondeviceorientation. I would appreciate any help in accomplishing this.
You don't need a canvas. You can do this just by altering the width of your spans along with using CSS to set the colors.
Firstly make sure your spans have a CSS property of display : inline-block or display : blockor else changing the width will do nothing. Alternatively you can make them divs instead of spans. Also make sure it has a height property set such as 30px.
Next you can use css or inline-styles to set the background-color property for alpha, beta, and gamma. Then simply change the Element.style.width property (in px) based on the device orientation using javascript.
Something you might want to consider deeply is what you want the size of the bars to represent and their precise behavior. That design decision is up to you, so I won't explain in excruciating detail how the following code works, but basically I size them relative to the range of the values. I turn the value of alpha, beta, and gamma respectively into a percentage of their total range and then multiply that by the max width I would like for the bars.
I grabbed the ranges from here: https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation
The general formula is for values in range [a, b] and a maximum bar width of max_w, and alpha, beta, or gamma value of value, the calculated width of the bar is:
width = max_w * ( ( -a + value ) / (b - a) )
And don't forget to add "px" to the end.
// Listen for device orientation event
window.ondeviceorientation = function(eventData)
{
let maxWidth = 200;
// Show alpha, beta and gamma values
document.getElementById('alpha').style.width = Math.round(maxWidth * eventData.alpha / 360) + "px";
document.getElementById('beta').style.width = Math.round(maxWidth * (180 + eventData.beta) / 360) + "px";
document.getElementById('gamma').style.width = Math.round(maxWidth * (90 + eventData.gamma) / 180) + "px";
}
p span{
height : 30px;
display : inline-block;
}
#alpha{
background-color : green;
}
#beta {
background-color : yellow;
}
#gamma {
background-color : purple
}
<p> Alpha: <span id="alpha"> </span> </p>
<p> Beta: <span id="beta"> </span> </p>
<p> Gamma: <span id="gamma"> </span></p>
The previous poster, Khauri McClain, posted a good suggestion for a static representation of your orientation values. If by "move" you mean, however, an animation (and hence refer to canvas), then you can still do it without canvas, but instead using CSS keyframes. Here is quick example.
html,
body {
height: 100%;
}
body {
background-color: #f5f7f9;
color: #6c6c6c;
margin: 0;
position: relative;
}
.container {
width: 30em;
margin: 2em;
}
.label {
float: left;
width: 5em;
height: 2em;
}
.orientation {
float: right;
background-color: #e5e9eb;
height: 2em;
position: relative;
width: 24em;
}
.alpha {
animation-duration: 3s;
animation-name: alpha-anim;
animation-fill-mode: forwards;
background-color: #ff2d55;
height: 100%;
position: relative;
}
.beta {
animation-duration: 3s;
animation-name: beta-anim;
animation-fill-mode: forwards;
background-color: #4cd964;
height: 100%;
position: relative;
}
.gamma {
animation-duration: 3s;
animation-name: gamma-anim;
animation-fill-mode: forwards;
background-color: #007aff;
height: 100%;
position: relative;
}
#keyframes alpha-anim {
0% {
width: 0;
}
100% {
width: 14em;
}
}
#keyframes beta-anim {
0% {
width: 0;
}
100% {
width: 3em;
}
}
#keyframes gamma-anim {
0% {
width: 0;
}
100% {
width: 20em;
}
}
<div class="container">
<div class="label">Alpha:</div>
<div class="orientation">
<div class="alpha"></div>
</div>
</div>
<div class="container">
<div class="label">Beta:</div>
<div class="orientation">
<div class="beta"></div>
</div>
</div>
<div class="container">
<div class="label">Gamma:</div>
<div class="orientation">
<div class="gamma"></div>
</div>
</div>
It seems that by using two cases of obj.style.transform in succession results in only the first statement being executed. Please see the below code. A full example is on CodePen.
function animateObject(x, y, z){
object = document.getElementsByClassName('cub-1')[0];
object.style.transform = "rotateX(" + x + "deg)";
object.style.transform = "rotateY(" + y + "deg)";
alert("")
}
In the full example, I am reading lines from a text area which contains x, y and z positions of an object, converting the values from radians to degrees and then animating my 3d object using these values.
When applying multiple transforms to the same element, they should be added as space separated values to the same property like below. Otherwise, they would get overwritten (only the rotateY will be applied because it is the latest).
object.style.transform = "rotateX(" + x + "deg)";
object.style.transform += "rotateY(" + y + "deg)";
I have added an alert of the object.style.transform to both the original snippet and the modified version and we can see how the original one always outputs only the rotateY whereas the changed one outputs both rotateX() and rotateY() together.
Original Code with Alerts added |
Modified Code with Alerts added
You could use DOMMatrix to build/chain your CSS transform.
Then stringify the resultant matrice with its build in DOMMatrixReadOnly.toString(). Which returns CSS ready transform syntax.
window.onload = () => {
const getStyle = id => document.getElementById(id).style;
// Rotate in plane
getStyle('Ⅰ').transform = new DOMMatrix('rotateZ(45deg)');
// Rotate from [1,1] vector (diagonal) (same as Ⅰ)
getStyle('Ⅱ').transform = new DOMMatrix().rotateFromVectorSelf(1, 1);
// Rotate matrix on Z axis. (same as Ⅰ & Ⅱ)
getStyle('Ⅲ').transform = new DOMMatrix().rotateAxisAngleSelf(0, 0, 1, 45);
// Rotate matrix on X axis. Then rotate matrix on Y axis.
getStyle('Ⅳ').transform = new DOMMatrix('rotateX(45deg) rotateY(45deg)');
// Rotate matrix on X axis. Then rotate matrix on Y axis (same as Ⅳ)
getStyle('Ⅴ').transform = new DOMMatrix('rotateX(45deg)').multiply(new DOMMatrix('rotateY(45deg)'));
// Rotate matrix on Y axis. Then rotate matrix on X axis.
getStyle('Ⅵ').transform = new DOMMatrix('rotateY(45deg)').multiply(new DOMMatrix('rotateX(45deg)'));
// Rotate matrix on Y/X axis. (same as Ⅵ)
getStyle('Ⅶ').transform = new DOMMatrix().rotateSelf(45, 45);
};
.element {
transition: transform 1s;
padding: 5px;
opacity: .7;
}
.element::after {
content: attr(id);
}
#Ⅰ { background: red; transition-delay: 0s;}
#Ⅱ { background: green; transition-delay: 1s; }
#Ⅲ { background: blue; transition-delay: 2s; }
#Ⅳ { background: orange; transition-delay: 3s; }
#Ⅴ { background: purple; transition-delay: 4s; }
#Ⅵ { background: pink; transition-delay: 5s; }
#Ⅶ { background: coral; transition-delay: 6s;}
.frame {
position: relative;
margin: 20px;
display: grid;
grid-auto-flow: column;
grid-auto-columns: 80px;
grid-auto-rows: 80px;
border: 1px solid;
}
<div class="frame">
<div class="element" id="Ⅰ"></div>
<div class="element" id="Ⅱ"></div>
<div class="element" id="Ⅲ"></div>
<div class="element" id="Ⅳ"></div>
<div class="element" id="Ⅴ"></div>
<div class="element" id="Ⅵ"></div>
<div class="element" id="Ⅶ"></div>
</div>
I have a requirement to rotate a div and stop at a particular position ( The value will be received from the server).
I tried native JS to rotate and stop but it is eating up my CPU big time.
I can rotate with CSS animation but I need to create a class which will dynamically describe where to stop the animation. Something like
#-webkit-keyframes spinIt {
100% {
-webkit-transform: rotate(A_DYNAMIC_VALUE);
}
}
#-moz-keyframes spinIt {
100% {
-webkit-transform: rotate(A_DYNAMIC_VALUE);
}
}
Here is one reference
http://jsfiddle.net/bVkwH/8/
You can insert stylesheet rules dynamically to override previous styles in the head. This helps avoid adding yet another library for a single task.
var style = document.createElement('style');
style.type = 'text/css';
var keyFrames = '\
#-webkit-keyframes spinIt {\
100% {\
-webkit-transform: rotate(A_DYNAMIC_VALUE);\
}\
}\
#-moz-keyframes spinIt {\
100% {\
-webkit-transform: rotate(A_DYNAMIC_VALUE);\
}\
}';
style.innerHTML = keyFrames.replace(/A_DYNAMIC_VALUE/g, "180deg");
document.getElementsByTagName('head')[0].appendChild(style);
well i don't think it is easy to create dynamic #keyframes they are inflexible because they must be hard-coded.
Transitions are a little easier to work with, as they can gracefully respond to any CSS changes performed by JavaScript.
However, the complexity that CSS transitions can give you is pretty limited — an animation with multiple steps is difficult to achieve.
This is a problem that CSS #keyframe animations are meant to solve, but they don’t offer the level of dynamic responsiveness that transitions do.
but these links might help you
Link1 : a tool that generates a #-webkit-keyframe animation with many tiny steps. This opens the door to an unlimited selection of easing formula.
Link2 it will be a great help for you to take it as a base as it provides a UI to create animations and exports it to CSS code.
I guess this solution will definitely work for you. Its is used for dynamic keyframes
Let me share an updated (2019) answer to this.
Yes, it's possible without Javascript using CSS Variables (supported by all modern browsers).
--lightScaleStart: 0.8;
.light {
animation: grow 2s alternate infinite ease-in-out;
}
.light.yellow {
--lightScaleEnd: 1.1;
}
.light.red {
--lightScaleEnd: 1.2;
}
#keyframes grow {
from {
transform: scale(var(--lightScaleStart));
}
to {
transform: scale(var(--lightScaleEnd));
}
}
See demo on Codepen Dynamic CSS Animations with CSS Variables
Edit: Here's a CSS Tricks article about it too.
Alex Grande's answer works GREAT for a few keyframes. But, say you want to dynamically keep adding in keyframes over and over again, then your webpage get really laggy really quick. To solve this problem, just STOP creating new DOM elements. Rather, create 1 new DOM stylesheet, and just reuse it with the insertRule. If you want even more keyframes (like if you're generating a new keyframe every animationframe), then you need to set up a system which deletes old keyframes after they're no longer used. This is a good start to how something like this can be achieved.
var myReuseableStylesheet = document.createElement('style'),
addKeyFrames = null;
document.head.appendChild( myReuseableStylesheet );
if (CSS && CSS.supports && CSS.supports('animation: name')){
// we can safely assume that the browser supports unprefixed version.
addKeyFrames = function(name, frames){
var pos = myReuseableStylesheet.length;
myReuseableStylesheet.insertRule(
"#keyframes " + name + "{" + frames + "}", pos);
}
} else {
addKeyFrames = function(name, frames){
// Ugly and terrible, but users with this terrible of a browser
// *cough* IE *cough* don't deserve a fast site
var str = name + "{" + frames + "}",
pos = myReuseableStylesheet.length;
myReuseableStylesheet.insertRule("#-webkit-keyframes " + str, pos);
myReuseableStylesheet.insertRule("#keyframes " + str, pos+1);
}
}
Example usage:
addKeyFrames(
'fadeAnimation',
'0%{opacity:0}' +
'100%{opacity:1}'
);
Also, Alex Grande, I am pretty sure that document.getElementsByTagName('head')[0] and type = 'text/css' hasn't been needed since IE8, and #keyframes aren't supported till IE10. Just saying...
This is now easily achievable with the new Web Animations API, which looks like this:
const anim = document.getElementById("foo").animate(
[
{ transform: `rotate(${A_DYNAMIC_VALUE})` }
],
{ duration: 3000, iterations: Infinity }
);
// and later
anim.pause();
The first argument to .animate takes a list of keyframes, and the second takes the animation options (e.g. duration, how many times it repeats, etc).
You can change the style in CSSKeyframeRule, and this works fine for me in Chrome, just as the code below.
Hope this will help:)
<html>
<head>
<style>
#text {
display: inline-block;
}
</style>
</head>
<body>
<div id="text">TEXT</div>
<script>
// Dynamically create a keyframe animation
document.styleSheets[0].insertRule('\
#keyframes anim {\
from { transform: rotateZ(0deg); }\
to { transform: rotateZ(360deg); }\
}'
);
var div = document.getElementById('text');
div.style.animation = 'anim 1s linear forwards';
// This function will change the anim
function stopAtSomeDeg(d) {
var ss = document.styleSheets[0];
var anim;
for (var i in ss.cssRules) {
// Find your animation by name
if (ss.cssRules[i].name === 'anim') {
anim = ss.cssRules[i];
break;
}
}
var stopFrame = anim.cssRules[1]; // This indicates the second line of "anim" above.
// Change any attributes
stopFrame.style.transform = 'rotateZ(' + d + 'deg)';
}
stopAtSomeDeg(180);
</script>
</body>
</html>
With CSS variables: You can use the pseudo :root of the element to declare a css variable within the css rules, then manipulate that variable using Javascript.
:root {--variable-name:property;} which is basically the root element of the document <html>. Then change the value of the CSS root variable/s using JS with:
element.style.setProperty('--variable-name','value'). Pass the declared root variable --variable-name as the name and assign the new value. Then in your #keyframes css rules, add the root variable name, like: from: { top: var(--top-position)}, to the property within the offset #keyframe rule. Example:
:root {
--top-position-start: 0px;
--left-position-start: 0px;
--top-position-end: 200px;
--left-position-end: 200px;
}
.element {
top: var(--top-position-start);
left: var(--left-position-start);
animation: movePos 1s ease-in;
}
#keyframes movePos {
from: {
top: var(--top-position-start);
left: var(--left-position-start);
}
to: {
top: var(--top-position-end);
left: var(--left-position-end);
}
}
Then the JS would like something like:
let ran = getRandomInt(99);
let skew = ran + getRandomInt(10);
root.style.setProperty('--top-position-end', `${ran}vw`);
root.style.setProperty('--left-position-end', `${skew}vw`);
By using the CSS variable on the root element, you are able to pass it along to the #keyframes event.
See the following working example using randomly placed div using CSS left and background-color:rgb()(red, green, blue) passed using the html:root style to #keyframes within CSS.
let root = document.documentElement;
let rain = document.querySelectorAll('.drop');
function getMaxInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function getMinMaxInt(min, max) {
return Math.random() * (max - min) + min;
}
// set an interval to drop the div from randomly positioned view widths on the screen
setInterval(() => {
let ran = getMaxInt(86);
let skew = ran + getMaxInt(10);
let circle = `${getMinMaxInt(3,15)}px`;
root.style.setProperty('--keyframeLeftStart', `${ran}vw`);
root.style.setProperty('--keyframeLeftEnd', `${skew}vw`);
root.style.setProperty('--animationDuration', `${ getMaxInt(2500)}ms`);
root.style.setProperty('--width', circle);
root.style.setProperty('--height', circle);
root.style.setProperty('--red', getMinMaxInt(100, 255));
root.style.setProperty('--green', getMinMaxInt(100, 255));
root.style.setProperty('--blue', getMinMaxInt(100, 255));
}, getMaxInt(3500))
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
/* here we define some css variables for the document :root
essentially, these will be the first iteration of the elements style
then JS will take voer and set the values from script */
:root {
--keyframeTop: 0;
--keyframeBottom: 98vh;
--keyframeLeftStart: 2vw;
--keyframeLeftEnd: 10vw;
--animationDuration: 1s;
--width: 5px;
--height: 5px;
--red: 100;
--green: 100;
--blue: 100;
}
body {
width: 100vw;
height: 100vh;
background-color: #000;
}
#main {
width: calc(100vw - var(--width));
height: calc(100vh - var(--height));
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
.drop {
width: var(--width);
height: var(--height);
border-radius: 50%;
position: absolute;
animation: dropping var(--animationDuration) ease-in infinite;
top: var(--keyframeTop);
left: var(--keyframeLeftStart);
background-color: rgb(var(--red),var(--green), var(--blue));
}
#keyframes dropping {
0% {
top: var(--keyframeTop);
left: var(--keyframeLeftStart);
background-color: rgb(var(--red),var(--green), var(--blue));
}
50% {
background-color: rgb(var(--green),var(--blue), var(--red));
}
100% {
top: var(--keyframeBottom);
left: var(--keyframeLeftEnd);
background-color: rgb(var(--blue),var(--red), var(--green));
}
}
<div id="main">
<div class="drop"></div>
</div>
In JavaScript is it possible to access to the style sheet with document.styleSheets. Every sheet has a rule and/or cssRule list (browser depending) and a CSSStyleSheet.insertRule() method.
This method allows you to add a new keyframe raw as a string:
JavaScript
function insertStyleSheetRule(ruleText)
{
let sheets = document.styleSheets;
if(sheets.length == 0)
{
let style = document.createElement('style');
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
}
let sheet = sheets[sheets.length - 1];
sheet.insertRule(ruleText, sheet.rules ? sheet.rules.length : sheet.cssRules.length);
}
document.addEventListener("DOMContentLoaded", event =>
{
insertStyleSheetRule("#keyframes spinIt { 0% { transform: rotate(-20deg); } 100% { transform: rotate(20deg); } }");
insertStyleSheetRule("#box { " +
"animation: spinIt 1s infinite alternate cubic-bezier(0.5,0,0.5,1); " +
"width: 64px; height: 64px; background-color: red; border: 4px solid black; " +
"}");
});
html
<div id="box"></div>
demo: https://jsfiddle.net/axd7nteu/
You could create a new stylesheet with the animation you want in it.
For Example:
function addAnimation(keyframe){
var ss=document.createElement('style');
ss.innerText=keyframe;
document.head.appendChild(ss);
}
This would create a new stylesheet with your animation.
This method has only been tested in Chrome.
Setting a #keyframe in one call with JavaScript, and use it, using append(), Object.assign(), and template strings.
document.body.append(
Object.assign(document.createElement("style"), {
textContent: `#keyframes coolrotate { from { transform: scale(1, 1) translate(-0.1em, 0)} to { transform: scale(-1, 1) translate(0, 0) }} small { display: inline-block; font-size:2.3em; animation: 1s infinite alternate coolrotate } body {font-size: x-large}`
}),
Object.assign(document.createElement("span"), {
innerHTML: `<span>c</span><small>o</small><span>o</span><small>L</small><small>...</small>`,
style: "font-weight: 1000; font-size: 3.3em;"
})
)
the user7892745 wont work for me, need some little adjustement
1° "pos" not understand wot should be, but the console log say "undefined"
so I've remove " , pos"
2° " myReuseableStylesheet.insertRule" give me error " is not a function"
so I used "innerHTML" insted of "insertRule"
3° finally I've moved "
document.head.appendChild( myReuseableStylesheet );" at the end
but after this it work fine and it's exact what I looking for.
thanks a lot user7892745 :D
maybe the problem I had, come form the way I use it
this is the script i used with it
var getclass = document.getElementsByClassName("cls");
var countclass = getclass.length;
for (var i=0; i <countclass; i++ ){
getclass[i].addEventListener('mouseover', function(){
// get the data-name value to show element whose id are the same
var x= this.getAttribute("data-name");
var y =document.getElementById(x);
y.style.display="block";
// because the element to show have fixed width, but different text length, they have different height
// so I need to get the highness, then use the value of height to define the 100% value of animation
// or the longer ones will be cutted an the shorten have a lot of empty space a the end
var yHeig= Math.round(parseInt(getComputedStyle(y).getPropertyValue('height')));
yHeig_ = yHeig - 10; // to shorten a bit the time from end and new passage
console.log(yHeig+" - "+ yHeig_);
addKeyFrames(
'showMe',
'0%{top:35px;}' +
'100%{top:-'+ yHeig_ +'px;}'
);
y.style.animation="showMe 7s linear infinite";
},false);
getclass[i].addEventListener('mouseout', function(){
var x= this.getAttribute("data-name");
document.getElementById(x).style.display="none";
},false);
}
i know thath a html marquee cuold seem symple to do the same thing, but dont work well,
You can create a <style> element, set its content to the CSS you want, in this case, the declaration of your animation and add it to the <head> of the page.
Also, as others have suggested, if you need to create many different animations, then it would be better to reuse a single <style> tag rather than creating multiple of them and add the new styles using CSSStyleSheet.insertRule().
Lastly, if you can use ES6's template literals/strings, your code will look much cleaner:
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 myAnimation {
0% { transform: rotate(0); }
20% { transform: rotate(${ 360 * Math.random() }deg); }
60% { transform: rotate(${ -360 * Math.random() }deg); }
90% { transform: rotate(${ 360 * Math.random() }deg); }
100% { transform: rotate(${ 0 }deg); }
}
`);
document.getElementById("circle").style.animation = 'myAnimation 3s infinite';
html,
body {
height: 100vh;
}
body {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
#circle {
width: 100px;
height: 100px;
box-shadow:
0 0 48px -4px rgba(0, 0, 0, .25),
0 0 0 4px rgba(0, 0, 0, .02);
border-radius: 100%;
position: relative;
overflow: hidden;
}
#circle::before {
content: '';
position: absolute;
top: 0;
left: 50%;
transform: translate(-2px);
border-left: 4px solid #FFF;
height: 24px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, .25);
}
<div id="circle"></div>
Or even better:
let dynamicStyles = null;
function addAnimation(name, body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(`#keyframes ${ name } {
${ body }
}`, dynamicStyles.length);
}
addAnimation('myAnimation', `
0% { transform: rotate(0); }
20% { transform: rotate(${ 360 * Math.random() }deg); }
60% { transform: rotate(${ -360 * Math.random() }deg); }
90% { transform: rotate(${ 360 * Math.random() }deg); }
100% { transform: rotate(${ 0 }deg); }
`);
document.getElementById("circle").style.animation = 'myAnimation 3s infinite';
html,
body {
height: 100vh;
}
body {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
#circle {
width: 100px;
height: 100px;
box-shadow:
0 0 48px -4px rgba(0, 0, 0, .25),
0 0 0 4px rgba(0, 0, 0, .02);
border-radius: 100%;
position: relative;
overflow: hidden;
}
#circle::before {
content: '';
position: absolute;
top: 0;
left: 50%;
transform: translate(-2px);
border-left: 4px solid #FFF;
height: 24px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, .25);
}
<div id="circle"></div>
Found a simple idea with JavaScript by using CSS data URI.
Solution
function addNewCSS(css_text) {
css_text = encodeURIComponent(css_text);
const url = `data:text/css,${css_text}`;
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = url;
document.head.appendChild(link);
}
Function accepts CSS code as text and adds it as a style.
Working
Converts the CSS text to URI encoded form (for passing as data URL). Then creates a link tag with href as the url and relation as "stylesheet" (here rel attribute is required and won't work if not added) Finally appends the link tag to head tag.
Example
function addNewCSS(css_text) {
css_text = encodeURIComponent(css_text);
const url = `data:text/css,${css_text}`;
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = url;
document.head.appendChild(link);
}
const duration = 1;
const colour = ["#2196F3", "#E91E63"];
const css_data = `
#keyframes change{
0% {
background: ${colour[0]};
}
100% {
background: ${colour[1]};
}
}
body {
animation: change ${duration}s linear infinite alternate;
}
`;
addNewCSS(css_data);
<html>
<head></head>
<body>
<h1>Wait to see JS adding background color animation</h1>
</body>
</html>
Conclusion
I haven't tested on all browsers, but works in chrome, and as it is added to the end of head tag it get priority from other tags in head, If you are planning to change values frequently, instead of adding new tags, try to edit the href of previously added tags.