Detect CSS Animation Keyframes with Javascript - javascript

So I have a simple CSS animation (a circle that grows, and then shrinks back down). I am able to successfully use Javascript to detect the start and end of the animation, but can't figure out how to detect the individual keyframes of that animation.
So my question is: how can I detect when the 50% keyframe has been reached in my animation?
Demo: http://codepen.io/tymichaels/pen/Mprrxw
HTML
<div class="center">
<svg class="center circle-animation" xmlns="https://www.w3.org/TR/SVG/">
<g>
<circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
<text x="35" y="75" font-size="18" class="output">circle</text>
</g>
</svg>
</div>
CSS
svg { width:150px;}
text {color:#000; font-family: sans-serif; font-weight: bold;}
.center{
margin-top:100px;
text-align:center;
padding-bottom:50px;
}
.circle-animation{
animation-delay:2s;
animation-duration: 4s;
animation-name: circle-animation;
animation-direction: normal;
animation-timing-function: linear;
}
#-webkit-keyframes circle-animation {
0% {transform: scale( 1 );}
50% {transform: scale( 2.25 );}
100% {transform: scale( 1 );}
}
JS
window.onload = function() {
var elm = document.querySelector('.circle-animation');
var op = document.querySelector('.output');
elm.addEventListener('animationend', function(e) {
op.innerHTML = 'ended';
});
elm.addEventListener('animationstart', function(e) {
op.innerHTML = 'started';
});
}

You can dispatch a custom event on animationstart with setInterval and clear the interval on animationend.
window.onload = function() {
var elm = document.querySelector('.circle-animation');
var op = document.querySelector('.output');
var eventOnAnimate = new Event('onanimate');
var time = 0;
elm.addEventListener('animationend', function(e) {
op.innerHTML = 'ended';
clearInterval(elm.interval);
time = 0;
});
elm.addEventListener('animationstart', function(e) {
op.innerHTML = 'started';
time = 0;
elm.interval = setInterval(function(){
eventOnAnimate.data = {sampleData: ++time};
elm.dispatchEvent(eventOnAnimate);
});
});
elm.addEventListener('onanimate', function(e) {
op.innerHTML = e.data.sampleData + 'ms';
});
}
svg { width:150px;}
text {color:#000; font-family: sans-serif; font-weight: bold;}
.center{
margin-top:30px;
text-align:center;
padding-bottom:50px;
}
.circle-animation{
animation-delay:2s;
animation-duration: 4s;
animation-name: circle-animation;
animation-direction: normal;
animation-timing-function: linear;
}
#-webkit-keyframes circle-animation {
0% {transform: scale( 1 );}
50% {transform: scale( 2.25 );}
100% {transform: scale( 1 );}
}
<div class="center">
<svg class="center circle-animation" xmlns="https://www.w3.org/TR/SVG/">
<g>
<circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
<text x="35" y="75" font-size="18" class="output">circle</text>
</g>
</svg>
</div>

There is no native event support for listening keyframe-by-keyframe, but you can create a workaround with setTimeout and window.getComputedStyle (to get the animation-duration property).
Below is an onKeyframes utility which can be used to listen for an arbitrary number of keyframe events using a more intuitive percentage-based syntax:
onKeyframes(elm, {
0: function() {
op.textContent = 'started'
},
50: function() {
op.textContent = 'midpoint'
},
100: function() {
op.textContent = 'ended'
}
})
Demo Snippet:
function onKeyframes(element, handlers) {
var from = handlers[0] || handlers.from
var to = handlers[100] || handlers.to
delete handlers.from
delete handlers[0]
delete handlers.to
delete handlers[100]
handlers = Object.keys(handlers).map(function(k) {
return [k, this[k]]
}, handlers)
element.addEventListener('animationstart', function() {
from && from.apply(this, arguments)
if (handlers.length) {
var match = /(\d+)(m?)s/.exec(window.getComputedStyle(element).animationDuration)
var duration = (match[2] ? 1 : 1e3) * match[1]
handlers.forEach(function(pair) {
setTimeout(pair[1], pair[0] / 100 * duration)
})
}
})
to && element.addEventListener('animationend', to)
}
window.onload = function() {
var elm = document.querySelector('.circle-animation')
var op = document.querySelector('.output')
onKeyframes(elm, {
0: function() {
op.textContent = 'started'
},
50: function() {
op.textContent = 'midpoint'
},
100: function() {
op.textContent = 'ended'
}
})
}
svg {
width: 150px;
}
text {
color: #000;
font-family: sans-serif;
font-weight: bold;
}
.center {
margin-top: 100px;
text-align: center;
padding-bottom: 50px;
}
.circle-animation {
animation-delay: 2s;
animation-duration: 4s;
animation-name: circle-animation;
animation-direction: normal;
animation-timing-function: linear;
}
#-webkit-keyframes circle-animation {
0% {
transform: scale( 1);
}
50% {
transform: scale( 2.25);
}
100% {
transform: scale( 1);
}
}
<div class="center">
<svg class="center circle-animation" xmlns="https://www.w3.org/TR/SVG/">
<g>
<circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
<text x="35" y="75" font-size="18" class="output">circle</text>
</g>
</svg>
</div>

Related

Need to increase height and width of circle

I need to increase circle height and width to double. But it's not working. Please suggest me where I put the error in the code. I have no idea about the value of r. Please help me to update the value of r.
Here is my sample code.
var time = 10;
var initialOffset = '440';
var i = 1
var r = $(".circle_animation").attr("r");
var interval = setInterval(function() {
$('.circle_animation').css(
'stroke-dashoffset',
initialOffset - (i * (initialOffset / time) * (r / 69.85699))
);
$('h2').text(i);
if (i == time) {
clearInterval(interval);
}
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align: center;
position: absolute;
line-height: 125px;
width: 100%;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440;
/* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="item html">
<h2>0</h2>
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</g>
</svg>
</div>
As you guessed, you have to change the value of r to change the radius.
If we look at the javascript code:
var r = $(".circle_animation").attr("r");
It means that r is the value of the html attribute r of the element with class name circle_animation.
If we then look at the html markup:
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
we see that r is set to 69.85699 on the circle with class name circle_animation
After that you have to remember that since your drawing a larger circle, you will also have to double both the size of the svg and the stroke-dasharray and stroke-dashoffset (these values are supposed to be 2*2*PI*r for this animation).
Here is your example with doubled radius as you requested:
var time = 10;
var initialOffset = '440';
var i = 1
var r = $(".circle_animation").attr("r");
var interval = setInterval(function() {
$('.circle_animation').css(
'stroke-dashoffset',
initialOffset-(i*(initialOffset/time)*(r/139.71398))
);
$('h2').text(i);
if (i == time) {
clearInterval(interval);
}
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align:center;
position: absolute;
line-height: 125px;
width: 100%;
margin-top: 60px;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 878; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 878;
transition: all 1s linear;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="item html">
<h2>0</h2>
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" class="circle_animation" r="139.71398" cy="162" cx="162" stroke-width="8" stroke="#6fdb6f" fill="none"/>
your radius is 69.85699 on the circle. lets consider 70 for simplicity.
double will be 140, circumference of this circle would be 880
now your cx and cy should also consider the offset/width of the stroke.
so, 140 + (8/2)
most other calculations are simple as you have already done them.
var time = 10;
var initialOffset = '880';
var i = 1
var r = $(".circle_animation").attr("r");
var interval = setInterval(function() {
$('.circle_animation').css(
'stroke-dashoffset',
initialOffset-(i*(initialOffset/time)*(r/140))
);
$('h2').text(i);
if (i == time) {
clearInterval(interval);
}
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align: center;
position: absolute;
line-height: 265px;
width: 100%;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 880; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 880;
transition: all 1s linear;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="item html">
<h2>0</h2>
<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" class="circle_animation" r="140" cy="144" cx="144" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</g>
</svg>
</div>
You need this, Check this fiddle and code https://jsfiddle.net/rmcej7vk/
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="item html">
<h2>0</h2>
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" class="circle_animation" r="139.7139" cy="162" cx="162" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</g>
</svg>
</div>
JS
var time = 10;
var initialOffset = '880';
var i = 1
var r = $(".circle_animation").attr("r");
var interval = setInterval(function() {
$('.circle_animation').css(
'stroke-dashoffset',
initialOffset - (i * (initialOffset / time) * (r / 139.7139))
);
$('h2').text(i);
if (i == time) {
clearInterval(interval);
}
i++;
}, 1000);
CSS
.item {
position: relative;
float: left;
}
.item h2 {
text-align: center;
position: absolute;
line-height: 250px;
width: 100%;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 880;
/* this value is the pixel circumference of the circle */
stroke-dashoffset: 880;
transition: all 1s linear;
}

countdown timer loader animation

Circle outline completes before timer finish. Although it is working fine when with the larger circle. fiddle
var time = 10;
var initialOffset = '440';
var i = 1
/* Need initial run as interval hasn't yet occured... */
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
var interval = setInterval(function() {
$('h2').text(i);
if (i == time) {
clearInterval(interval);
return;
}
$('.circle_animation').css('stroke-dashoffset', initialOffset-((i+1)*(initialOffset/time)));
i++;
}, 1000);
svg {
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440;
stroke-dashoffset: 440;
transition: all 1s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="item html">
<h2>0</h2>
<svg width="70" height="70" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" class="circle_animation" r="25" cy="35" cx="35" stroke-width="4" stroke="#6fdb6f" fill="#ccc"/>
</g>
</svg>
</div>
You have to reduce the offset, both in css and javascript, i updated your code there :
http://jsfiddle.net/3recj0s9/
CSS :
.circle_animation {
stroke-dasharray: 155;
stroke-dashoffset: 155;
transition: all 1s linear;
}
JS
var initialOffset = '155';

using multiple instances of popup

i am using a popup that perfectly suits my needs! what i want to do is add another instance of the same popup but its not working,basically i have two different popup i want to show depending on the scenario like one for register and one for login. the first instance named somedialog works fine but the instance somedialog2 doesn't work here's my code
<a data-dialog="somedialog" class="trigger icon icon-register">Register</a>
<a data-dialog="somedialog2" class="trigger icon icon-login">Login</a>
<div id="somedialog" class="dialog dialog--close">
<div class="dialog__overlay"></div>
<div class="dialog__content">
<div class="morph-shape">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 560 280" preserveAspectRatio="none">
<rect x="3" y="3" fill="none" width="556" height="276"></rect>
</svg>
</div>
<div class="dialog-inner">
<h2><strong>Wait!</strong>, Are you a teacher or a student?</h2>
<div>
<button style="display: none" class="action" data-dialog-close="a">Close</button>
<button class="action" onclick="window.location.href='<?=base_url()?>Home/student_register'">I'm A Student</button>
<button class="action" onclick="window.location.href='<?=base_url()?>Home/teacher_register'">I'm A Teacher</button>
</div>
</div>
</div>
</div>
<div id="somedialog2" class="dialog dialog--close">
<h1>here</h1>
<div class="dialog__overlay"></div>
<div class="dialog__content">
<div class="morph-shape">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 560 280" preserveAspectRatio="none">
<rect x="3" y="3" fill="none" width="556" height="276"></rect>
</svg>
</div>
<div class="dialog-inner">
<h2><strong>Wait!</strong>, Are you a teacher or a student?</h2>
<div>
<button style="display: none" class="action" data-dialog-close="a">Close</button>
<button class="action" onclick="window.location.href='<?=base_url()?>Student'">I'm A Student</button>
<button class="action" onclick="window.location.href='<?=base_url()?>Teacher'">I'm A Teacher</button>
</div>
</div>
</div>
</div>
js
( function( window ) {
'use strict';
var support = { animations : Modernizr.cssanimations },
animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' },
animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ],
onEndAnimation = function( el, callback ) {
var onEndCallbackFn = function( ev ) {
if( support.animations ) {
if( ev.target != this ) return;
this.removeEventListener( animEndEventName, onEndCallbackFn );
}
if( callback && typeof callback === 'function' ) { callback.call(); }
};
if( support.animations ) {
el.addEventListener( animEndEventName, onEndCallbackFn );
}
else {
onEndCallbackFn();
}
};
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function DialogFx( el, options ) {
this.el = el;
this.options = extend( {}, this.options );
extend( this.options, options );
this.ctrlClose = this.el.querySelector( '[data-dialog-close]' );
this.isOpen = false;
this._initEvents();
}
DialogFx.prototype.options = {
// callbacks
onOpenDialog : function() { return false; },
onCloseDialog : function() { return false; }
}
DialogFx.prototype._initEvents = function() {
var self = this;
// close action
this.ctrlClose.addEventListener( 'click', this.toggle.bind(this) );
// esc key closes dialog
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
if( keyCode === 27 && self.isOpen ) {
self.toggle();
}
} );
this.el.querySelector( '.dialog__overlay' ).addEventListener( 'click', this.toggle.bind(this) );
}
DialogFx.prototype.toggle = function() {
var self = this;
if( this.isOpen ) {
classie.remove( this.el, 'dialog--open' );
classie.add( self.el, 'dialog--close' );
onEndAnimation( this.el.querySelector( '.dialog__content' ), function() {
classie.remove( self.el, 'dialog--close' );
} );
// callback on close
this.options.onCloseDialog( this );
}
else {
classie.add( this.el, 'dialog--open' );
// callback on open
this.options.onOpenDialog( this );
}
this.isOpen = !this.isOpen;
};
// add to global namespace
window.DialogFx = DialogFx;
})( window );
(function() {
var dlgtrigger = document.querySelector( '[data-dialog]' );
console.log(dlgtrigger);
var dlgtrigger = document.querySelector( '[data-dialog]' ),
somedialog = document.getElementById( dlgtrigger.getAttribute( 'data-dialog' ) ),
dlg = new DialogFx( somedialog );
dlgtrigger.addEventListener( 'click', dlg.toggle.bind(dlg) );
})();
(function() {
var dlgtrigger = document.querySelector( '[data-dialog]' );
console.log(dlgtrigger);
var dlgtrigger = '<a class="trigger icon icon-register" data-dialog="somedialog">',
somedialog = "somedialog2",
dlg = new DialogFx( somedialog );
dlgtrigger.addEventListener( 'click', dlg.toggle.bind(dlg) );
})();
Codepen
This should do it,, the problem that you had was that your code only targeted one element id,, I only expanded it to account for all elements that have [data-dialog] property..
/**
* dialog box v0.1
* Ashwin Saxena
*/
;
(function(window) {
'use strict';
var support = {
animations: Modernizr.cssanimations
},
animEndEventNames = {
'WebkitAnimation': 'webkitAnimationEnd',
'OAnimation': 'oAnimationEnd',
'msAnimation': 'MSAnimationEnd',
'animation': 'animationend'
},
animEndEventName = animEndEventNames[Modernizr.prefixed('animation')],
onEndAnimation = function(el, callback) {
var onEndCallbackFn = function(ev) {
if (support.animations) {
if (ev.target != this) return;
this.removeEventListener(animEndEventName, onEndCallbackFn);
}
if (callback && typeof callback === 'function') {
callback.call();
}
};
if (support.animations) {
el.addEventListener(animEndEventName, onEndCallbackFn);
} else {
onEndCallbackFn();
}
};
function extend(a, b) {
for (var key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
return a;
}
function DialogFx(el, options) {
this.el = el;
this.options = extend({}, this.options);
extend(this.options, options);
this.ctrlClose = this.el.querySelector('[data-dialog-close]');
this.isOpen = false;
this._initEvents();
}
DialogFx.prototype.options = {
// callbacks
onOpenDialog: function() {
return false;
},
onCloseDialog: function() {
return false;
}
}
DialogFx.prototype._initEvents = function() {
var self = this;
// close action
this.ctrlClose.addEventListener('click', this.toggle.bind(this));
// esc key closes dialog
document.addEventListener('keydown', function(ev) {
var keyCode = ev.keyCode || ev.which;
if (keyCode === 27 && self.isOpen) {
self.toggle();
}
});
this.el.querySelector('.dialog__overlay').addEventListener('click', this.toggle.bind(this));
}
DialogFx.prototype.toggle = function() {
var self = this;
if (this.isOpen) {
classie.remove(this.el, 'dialog--open');
classie.add(self.el, 'dialog--close');
onEndAnimation(this.el.querySelector('.dialog__content'), function() {
classie.remove(self.el, 'dialog--close');
});
// callback on close
this.options.onCloseDialog(this);
} else {
classie.add(this.el, 'dialog--open');
// callback on open
this.options.onOpenDialog(this);
}
this.isOpen = !this.isOpen;
};
// add to global namespace
window.DialogFx = DialogFx;
})(window);
/* call */
(function() {
var dlgs = document.querySelectorAll('[data-dialog]');
for( var i = 0; i < dlgs.length; i++){
var dlgID = document.getElementById(dlgs[i].getAttribute('data-dialog'));
var dlg = new DialogFx( dlgID );
dlgs[i].addEventListener('click', dlg.toggle.bind(dlg));
}
})();
button {
padding: 1em 2em;
outline: none;
font-weight: 600;
border: none;
color: #fff;
background: #c94e50;
}
.dialog,
.dialog__overlay {
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.dialog {
position: fixed;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
pointer-events: none;
}
.dialog__overlay {
position: absolute;
z-index: 1;
background: rgba(55, 58, 71, 0.9);
opacity: 0;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
-webkit-backface-visibility: hidden;
}
.dialog--open .dialog__overlay {
opacity: 1;
pointer-events: auto;
}
.dialog__content {
width: 50%;
max-width: 560px;
min-width: 290px;
background: #fff;
padding: 4em;
text-align: center;
position: relative;
z-index: 5;
opacity: 0;
}
.dialog--open .dialog__content {
pointer-events: auto;
}
/* Content */
.dialog h2 {
margin: 0;
font-weight: 400;
font-size: 2em;
padding: 0 0 2em;
margin: 0;
}
.dialog--open .dialog__overlay {
-webkit-transition-duration: 0.8s;
transition-duration: 0.8s;
}
.dialog--close .dialog__overlay {
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
}
.dialog__content {
padding: 0;
background: transparent;
}
.dialog.dialog--open .dialog__content {
opacity: 1;
}
.morph-shape {
position: absolute;
width: calc(100% + 4px);
height: calc(100% + 4px);
top: -2px;
left: -2px;
z-index: -1;
}
.morph-shape svg rect {
stroke: #fff;
stroke-width: 2px;
stroke-dasharray: 1680;
}
.dialog--open .morph-shape svg rect {
-webkit-animation: anim-dash 0.6s forwards;
animation: anim-dash 0.6s forwards;
}
.dialog-inner {
opacity: 0;
background: #fff;
}
.dialog--open .dialog-inner {
padding: 4em;
opacity: 1;
-webkit-transition: opacity 0.85s 0.35s;
transition: opacity 0.85s 0.35s;
}
.dialog.dialog--open h2 {
-webkit-animation: anim-elem-1 0.7s ease-out both;
animation: anim-elem-1 0.7s ease-out both;
}
.dialog.dialog--open button {
-webkit-animation: anim-elem-2 0.7s ease-out both;
animation: anim-elem-2 0.7s ease-out both;
}
#keyframes anim-dash {
0% {
stroke-dashoffset: 1680;
}
100% {
stroke-dashoffset: 0;
}
}
#-webkit-keyframes anim-dash {
0% {
stroke-dashoffset: 1680;
}
100% {
stroke-dashoffset: 0;
}
}
/* Inner elements animations */
#-webkit-keyframes anim-elem-1 {
0% {
opacity: 0;
-webkit-transform: translate3d(-150px, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
}
}
#keyframes anim-elem-1 {
0% {
opacity: 0;
-webkit-transform: translate3d(-150px, 0, 0);
transform: translate3d(-150px, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
#-webkit-keyframes anim-elem-2 {
0% {
opacity: 0;
-webkit-transform: translate3d(150px, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
}
}
#keyframes anim-elem-2 {
0% {
opacity: 0;
-webkit-transform: translate3d(150px, 0, 0);
transform: translate3d(150px, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://tympanus.net/Development/DialogEffects/js/classie.js"></script>
<script src="https://tympanus.net/Development/DialogEffects/js/modernizr.custom.js"></script>
<div class="button-wrap">
<button data-dialog="somedialog" class="trigger">Open Dialog 1</button>
</div>
<div class="button-wrap">
<button data-dialog="somedialog1" class="trigger">Open Dialog 2</button>
</div>
<div id="somedialog" class="dialog dialog--close">
<div class="dialog__overlay"></div>
<div class="dialog__content">
<div class="morph-shape">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 560 280" preserveAspectRatio="none">
<rect x="3" y="3" fill="none" width="556" height="276"></rect>
</svg>
</div>
<div class="dialog-inner">
<h2><strong>Howdy</strong>, I'm a dialog box 1</h2>
<div><button class="action" data-dialog-close="a">Close</button></div>
</div>
</div>
</div>
<div id="somedialog1" class="dialog dialog--close">
<div class="dialog__overlay"></div>
<div class="dialog__content">
<div class="morph-shape">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 560 280" preserveAspectRatio="none">
<rect x="3" y="3" fill="none" width="556" height="276"></rect>
</svg>
</div>
<div class="dialog-inner">
<h2><strong>Howdy</strong>, I'm a dialog box 2</h2>
<div><button class="action" data-dialog-close="a">Close</button></div>
</div>
</div>
</div>

How would I display a dynamic de/scaling SVG transformation in Javascript to the page?

I have an SVG with two circles that are animated using #keyframes. I want to display the value of the circle's growth. Using javascript I want to display the value as they shrink and grow. getTotalLength() is probably not what I should be using but am not sure of the name of what I am looking for that does size/value check.
My codepen
an example of how it would look
// console.log("DOM Ready!");
// // setInterval, changes global var, translates the size of the val.
// // ever second or two
// // point 0.1 means 10px
// // displaying each circle's value as they grow and shrink
// var redValue = document.getElementById('red-grow').innerHTML = span;
// // var greenValue = document.getElementById('green-grow').innerHTML =
// var redCircle = document.getElementsByClassName('red');
// var greenCircle = document.getElementsByClassName('green');
// var current = 0;
// var width = 0;
// var destination = 700;
// var friction = 0.04;
// // scaling up both circles
// function scaleUp() {
// // console.log(scaleUp);
// current += (destination - current) * friction;
// redCircle[0].style.width = (current * 0.5 + 'px');
// greenCircle[0].style.width = (current * 0.5 + 'px');
// if (current >= destination - 0.1) {
// // clearInterval(redAnimate);
// }
// }
// var redAnimate = setInterval(scaleUp, 20);
// // scaling down both circles
// function scaleDown() {
// //console.log(redCircle[0].style, greenCircle[0].style);
// current += (destination - current) * friction;
// redCircle[0].style.width = (current * 0.5 + 'px');
// greenCircle[0].style.width = (current * 0.5 + 'px');
// if (current >= destination - 0.1) {
// // clearInterval(greenAnimate);
// }
// }
// var greenAnimate = setInterval(scaleDown, 20);
body {
background-color: #584E56;
}
h1 {
color: #fff;
text-align: center;
margin: 10px 0;
font-family: 'Roboto', sans-serif;
font-weight: bold;
font-size: 2.4rem;
text-transform: uppercase;
letter-spacing: .3rems;
}
svg {
position: absolute;
top: 0;
left: 0;
bottom: 0;
margin: auto;
}
.red, .green {
transform-origin: 60% 60%;
-webkit-animation: zoom 1s ease-in-out infinite alternate;
animation: zoom 1s ease-in-out infinite alternate;
}
#keyframes zoom {
from {
transform: scale(1, 1);
}
to {
transform: scale(0.7, 0.7);
}
}
.timeline {}
<h1>Animation SVG</h1>
<span class="" id="red-grow">
<!-- display here the red circle's value when growing and shrinking -->
Value:
</span>
<span class="" id="green-grow">
<!-- display here the green circle's value when growing and shrinking -->
Value:
</span>
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<path fill="none" d="M-1-1h502v302H-1z"/>
<g>
<path class="timeline" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="4.5" stroke="#000" fill="none" d="M39.5 31.5v239M463.5 269.5l-422-1"/>
<ellipse class="red" id="inner" stroke="#e5a3a3" ry="65.5" rx="67" cy="165" cx="158.5" stroke-width="4.5" fill="none"/>
<ellipse class="green" stroke="#98FB98" ry="65.5" rx="67" cy="165" cx="361.5" stroke-width="4.5" fill="none"/>
</g>
</svg>
The current value of the animated transform property can be extracted using the getComputedStyle() method. That returns a matrix in string form which then need to extract the scale value from.
Here's an example solution that displays the current scale as a percentage.
var redCircle = document.getElementsByClassName('red')[0];
var greenCircle = document.getElementsByClassName('green')[0];
var redLabel = document.getElementById('red-grow');
var greenLabel = document.getElementById('green-grow');
function updateLabels()
{
var transformValue = getComputedStyle(redCircle).getPropertyValue('transform');
// transformValue is a string of the form "matrix(1, 0, 0, 1, 0 ,0)".
// For this example the first number in the matrix corresponds to the scale.
// We'll use a regular expression to extract it.
var currentScale = transformValue.match(/matrix\(([\d.]+)\,/)[1];
redLabel.textContent = Math.round(currentScale * 100) + '%';
requestAnimationFrame(updateLabels);
}
requestAnimationFrame(updateLabels);
body {
background-color: #584E56;
}
h1 {
color: #fff;
text-align: center;
margin: 10px 0;
font-family: 'Roboto', sans-serif;
font-weight: bold;
font-size: 2.4rem;
text-transform: uppercase;
letter-spacing: .3rems;
}
svg {
position: absolute;
top: 0;
left: 0;
bottom: 0;
margin: auto;
}
.red, .green {
transform-origin: 60% 60%;
-webkit-animation: zoom 1s ease-in-out infinite alternate;
animation: zoom 1s ease-in-out infinite alternate;
}
#keyframes zoom {
from {
transform: scale(1, 1);
}
to {
transform: scale(0.7, 0.7);
}
}
.timeline {}
<h1>Animation SVG</h1>
<span class="" id="red-grow">
<!-- display here the red circle's value when growing and shrinking -->
Value:
</span>
<span class="" id="green-grow">
<!-- display here the green circle's value when growing and shrinking -->
</span>
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<path fill="none" d="M-1-1h502v302H-1z"/>
<g>
<path class="timeline" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="4.5" stroke="#000" fill="none" d="M39.5 31.5v239M463.5 269.5l-422-1"/>
<ellipse class="red" id="inner" stroke="#e5a3a3" ry="65.5" rx="67" cy="165" cx="158.5" stroke-width="4.5" fill="none"/>
<ellipse class="green" stroke="#98FB98" ry="65.5" rx="67" cy="165" cx="361.5" stroke-width="4.5" fill="none"/>
</g>
</svg>
However in my opinion, it might be more straightforward to avoid using CSS animation, and instead update the circle transform or radius yourself in the requestAnimationFrame() function.

Prevent random particles to appear where unnecessary scrollbars are triggered

How to prevent random particles to appear where unnecessary scrollbars are triggered?
The background element can't be fixed-sized.
I think the solution could be to only show particles on the visible part of the viewport and to leave a margin of a few pixels, but I don't know how to do it.
// https://github.com/maxspeicher/jquery-sparkle
(function($, window, document) {
const defaults = {
fill: "#fff",
stroke: "#000",
size: 20,
delay: 0,
duration: 1500,
pause: 1000
};
const optionsKeys = ["fill", "stroke", "size", "delay", "duration", "pause"];
const optionsStrToObj = function(optionsStr) {
const optionsArr = !!optionsStr ? optionsStr.split(" ") : [];
var optionsObj = {};
for (var i=0; i<optionsArr.length; ++i) {
optionsObj[optionsKeys[i]] = optionsArr[i];
}
return optionsObj;
};
$.fn.sparkle = function(options) {
$.destroySparkle = $.destroySparkle || {};
const $this = this;
const id = this.data("sparkle-id") || (new Date()).getTime() + Math.random();
if (options === "destroy" && this.find("svg").length > 0) {
$.destroySparkle[id] = true;
this.data("sparkle-id", null);
}
var settings;
if (options instanceof Array) {
for (var i=0; i<options.length; ++i) {
$this.sparkle(optionsStrToObj(options[i]));
}
return;
} else if (options instanceof Object) {
settings = $.extend({}, defaults, options);
} else {
settings = defaults;
}
const cssAnimationAttr = "my-sparkle " + settings.duration + "ms infinite linear";
const $star = $('<svg class="my-sparkle" version="1.1" viewBox="0.0 0.0 50.0 50.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0 0l50.0 0l0 50.0l-50.0 0l0 -50.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#p.0)"><path fill="' + settings.stroke + '" fill-opacity="0.0" d="m0 0l50.0 0l0 50.0l-50.0 0z" fill-rule="nonzero"></path><path fill="' + settings.fill + '" d="m0.62204725 25.0l20.068499 -4.323374l4.309454 -20.13332l4.309454 20.13332l20.068499 4.323374l-20.068499 4.323374l-4.309454 20.133318l-4.309454 -20.133318z" fill-rule="nonzero"></path><path stroke="' + settings.stroke + '" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m0.62204725 25.0l20.068499 -4.323374l4.309454 -20.13332l4.309454 20.13332l20.068499 4.323374l-20.068499 4.323374l-4.309454 20.133318l-4.309454 -20.133318z" fill-rule="nonzero"></path></g></svg>').css({
position: "absolute",
width: settings.size,
height: settings.size,
zIndex: 9999
});
const w = this.width();
const h = this.height();
const getCoordinates = function() {
return {
left: Math.random() * w,
top: Math.random() * h
};
};
const placeStar = function(init) {
const coords = getCoordinates();
if (init) {
$this.append($star);
}
$star.css({
"-moz-animation": cssAnimationAttr,
"-webkit-animation": cssAnimationAttr,
animation: cssAnimationAttr,
display: "block",
left: coords.left,
top: coords.top
});
window.setTimeout(function() {
$star.css({
"-moz-animation": null,
"-webkit-animation": null,
animation: null,
display: "none"
});
if (!$.destroySparkle[id]) {
window.setTimeout(function() {
placeStar(false);
}, settings.pause);
} else {
$star.remove();
}
}, settings.duration);
};
if (this.css("position") === "static") {
this.css("position", "relative");
}
if (!$.destroySparkle[id] && options !== "destroy") {
window.setTimeout(function() {
placeStar(true);
}, settings.delay);
this.data("sparkle-id", id);
}
return this;
};
$("#bg").sparkle({
size: 25,
}).sparkle({
delay: 1000,
pause: 750,
size: 25
}).sparkle({
delay: 1500,
pause: 750,
size: 25
}).sparkle({
delay: 2000,
pause: 750,
size: 25
}).sparkle({
delay: 2500,
pause: 750,
size: 25
}).sparkle({
delay: 3000,
pause: 750,
size: 25
});
})(Zepto, window, document);
#-moz-keyframes my-sparkle {
0% {
opacity: 0;
-moz-transform: rotate(0deg) scale(0);
}
50% {
opacity: 1;
-moz-transform: rotate(360deg) scale(1);
}
100% {
opacity: 0;
-moz-transform: rotate(720deg) scale(0);
}
}
#-webkit-keyframes my-sparkle {
0% {
opacity: 0;
-webkit-transform: rotate(0deg) scale(0);
}
50% {
opacity: 1;
-webkit-transform: rotate(360deg) scale(1);
}
100% {
opacity: 0;
-webkit-transform: rotate(720deg) scale(0);
}
}
#keyframes my-sparkle {
0% {
opacity: 0;
transform: rotate(0deg) scale(0);
}
50% {
opacity: 1;
transform: rotate(360deg) scale(1);
}
100% {
opacity: 0;
transform: rotate(720deg) scale(0);
}
}
body {
background-color: #1d1f20;
margin: 0;
padding: 20px;
}
#bg {
color: #666;
display: inline-block;
font-family: Verdana;
font-size: 200%;
font-weight: bold;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<html id="bg" style="width: 100%;
height: 100%;">
<head>
<meta charset="UTF-8">
<title>Sparkles</title>
</head>
<body>
<div>Sparkle, sparkle!</div>
</body>
</html>
https://codepen.io/tigre/pen/xRbZPZ
You are using it wrong. Dont directly use sparcle plugin on html tag instead add element to starting of body tag and style it
<body>
<div id="bg"></div>
<div>Sparkle, sparkle!</div>
</body>
where bg is your sparkle element you are pointing to.
and Style bg element as following
#bg {
position:fixed;
display:block;
width:100%;
height:100%;
top:0;
left:0;
background: #1d1f20;
overflow:hidden;
z-index:-1;
}
Here is updated Codepen link : https://codepen.io/anon/pen/LbELWa

Categories

Resources