gsap rotation - how to avoid sharp transitions - javascript

Could someone help me make this swing more fluently, without this sharp transitions from one direction to another? The animation should be like a swing on wind.
const stepDuration = 0.5, wing = '#wing';
gsap.set(wing, {transformOrigin: "50% 0%", rotation: 15})
const walk = () => {gsap.timeline({repeat: -1, defaults: { ease: "circ.inOut", duration: stepDuration }})
.add('start')
.to(wing, { rotation: -15 })
.to(wing, { rotation: 15 })
}
window.onload = () => {walk()};
svg {
position: fixed;
left: 50vw;
top: 25px;
width: 70%;
height: 70%;
animation: a01 9s;
background: #0099cc;
}
#wing {
fill: gold;
transform-origin: top center;
animation: awinga 25s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.0.4/gsap.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/MotionPathPlugin.min.js?v=15"></script>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1" viewBox="0 0 1280 720">
<path id='wing' d="M100 0 L150 0 L150 300 L100 300 Z" />
</svg>
<script>
</script>

What you want is basically a tweening/easing function that tapers out at the start and at the end, without a sharp gradient in between. The reason of your jerking is because for the circ.inOut easing, there is basically an infinitesimally small period of time where the gradient is infinite (right at the 50% mark), which causes the sharp jerking you see when it's mid-tween (right when your element rotates past the vertical midline):
Therefore, you should pick an easing function that doesn't have that abrupt change in the middle, e.g. power1.inOut or the likes of it:
[
const stepDuration = 0.5, wing = '#wing';
gsap.set(wing, {transformOrigin: "50% 0%", rotation: 15})
const walk = () => {gsap.timeline({repeat: -1, defaults: { ease: "power1.inOut", duration: stepDuration }})
.add('start')
.to(wing, { rotation: -15 })
.to(wing, { rotation: 15 })
}
window.onload = () => {walk()};
svg {
position: fixed;
left: 50vw;
top: 25px;
width: 70%;
height: 70%;
animation: a01 9s;
background: #0099cc;
}
#wing {
fill: gold;
transform-origin: top center;
animation: awinga 25s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.0.4/gsap.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/MotionPathPlugin.min.js?v=15"></script>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1" viewBox="0 0 1280 720">
<path id='wing' d="M100 0 L150 0 L150 300 L100 300 Z" />
</svg>
<script>
</script>

Related

cannot make 3/4 circle and nest it with progress bar, far now only full circle and progress bar

i am going to do some circular charts and now i have full circle with progress bar but dont know why i cannot make it 3/4circle and progress bar.
code:
<!-- The HTML structure for the progress bar -->
<div class="progress-container">
<svg class="progress-bar" width="100%" height="100%" viewBox="0 0 42 42">
<circle class="progress-ring" cx="21" cy="21" r="15.91549430918954" fill="transparent"/>
<circle class="progress-value" cx="21" cy="21" r="15.91549430918954" fill="transparent"/>
</svg>
<div class="progress-text">0%</div>
</div>
/* The CSS styles for the progress bar */
.progress-container {
position: relative;
width: 200px;
height: 200px;
}
.progress-bar {
position: absolute;
top: 0;
left: 0;
}
.progress-ring {
stroke: #ddd;
stroke-width: 2;
fill: transparent;
}
.progress-value {
stroke: #3498db;
stroke-width: 2;
fill: transparent;
stroke-dasharray: 0 100;
transition: stroke-dasharray 0.3s;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 18px;
font-weight: bold;
text-align: center;
color: #3498db;
}
// The JavaScript code to update the progress bar
const progressContainer = document.querySelector('.progress-container');
const progressBar = progressContainer.querySelector('.progress-bar');
const progressValue = progressBar.querySelector('.progress-value');
const progressText = progressContainer.querySelector('.progress-text');
const updateProgress = (value) => {
// Update the value of the progress bar
progressValue.style.strokeDasharray = `${value} 100`;
// Update the text inside the progress bar
progressText.textContent = `${value}%`;
}
updateProgress(50); // Set the progress bar to 50%
This is how it looks like:
but i want it to looks some similar to:
If someone know how to rebuild my code, please help me out.
You would need to draw the 3/4 arc segment with svg path,
<svg class="progress-bar" width="100%" height="100%" viewBox="0 0 42 42">
<!--
<circle class="progress-ring" cx="21" cy="21" r="15.91549430918954" fill="transparent"/>
<circle class="progress-value" cx="21" cy="21" r="15.91549430918954" fill="transparent"/>
-->
<path class="progress-ring" d="M21 21 m-11.254 11.254 a 15.91549430918954 15.91549430918954 135 1 1 22.508 0" fill="transparent" stroke-linecap="round"/>
<path class="progress-value" d="M21 21 m-11.254 11.254 a 15.91549430918954 15.91549430918954 135 1 1 22.508 0" fill="transparent" stroke-linecap="round"/>
</svg>
and adjust the length accordingly.
//progressValue.style.strokeDasharray = `${value} 100`;
progressValue.style.strokeDasharray = `${value * 3 / 4} 75`;

All circle counter showing same data (how to make each circle show it's own counter data value)

hwllo i want my circle counters to display counter value as i mention in their class and data-percent
but currently all 4 counters are shwoing the data of 1st counter only however i have mentioned the data-percent value of each counter in its html code.
please help me figure out what's wrong and what should i do to make it correct?
any help is appreciated and code is provided for clear understanding:
the code used below is showing me this result as shown in the picture:
currently i'm using this js code snippet
<script type="text/javascript">
(function () {
var bar, c, percent, r, range;
percent = $('.pie').data('percent');
bar = $('.pie').find('.bar');
r = bar.attr('r');
c = Math.PI * (r * 2);
range = (100 - percent) / 100 * c;
bar.css({
'stroke-dashoffset': c,
'stroke-dasharray': c });
bar.animate({
strokeDashoffset: range },
1000, 'linear');
$('.text').prop('Counter', 0).animate({
Counter: percent },
{
duration: 1000,
step: function (now) {
return $(this).text(Math.ceil(now) + '%');
} });
}).call(this);
</script>
and html is this:
<!--counter new -->
<div class="container-fluid counter">
<div class="row">
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="99">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="32">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="44">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="22">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
</div>
</div>
<!--counter new end -->
the css used is also shown below:
.svg-box {
position: relative;
}
.text {
position: absolute;
top: 50%;
left: 40%;
transform: translate(-50%, -50%);
z-index: 1;
font-size: 3em;
letter-spacing: 3px;
font-family: "Raleway", Calibri, sans-serif;
color: #fff
}
svg {
transform: rotate(-90deg);
width: 200px;
height: 200px;
background: transparent;
fill: none;
}
svg circle {
stroke-width: 1;
stroke: #999;
}
svg .bar {
stroke-width: 3;
stroke: #ffffff;
stroke-linecap: round;
}
i want to display the counters by showing the
"data-percent" vale
whihc i've mentioned in the html code. but all the counters are showing the data value of 1st counter.
how ever what i want to display data is like this:
I've redesigned a little your code, and I think it should work correctly.
(function () {
$('.pie').each(function(){
let percent = $(this).data('percent');
let bar = $(this).find('.bar');
let r = bar.attr('r');
let c = Math.PI * (r * 2);
let range = (100 - percent) / 100 * c;
bar.css({
'stroke-dashoffset': c,
'stroke-dasharray': c
});
bar.animate({ strokeDashoffset: range }, 1000, 'linear');
$(this).prev().prop('Counter', 0).animate({
Counter: percent
}, {
duration: 1000,
step: function (now) {
return $(this).text(Math.ceil(now) + '%');
}
});
})
}).call(this);
And here is a working fiddle: https://jsfiddle.net/w2o7rt1L/

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;
}

Create a button out of a javascript canvas object

function draw() {
var canvas = document.getElementById('navigation');
if(canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath();
//shape 1
ctx.moveTo(300, 1120);
ctx.lineTo(230, 1070);
ctx.lineTo(160, 880);
ctx.lineTo(170, 770);
ctx.lineTo(260, 640);
ctx.lineTo(350, 710);
ctx.lineTo(360, 820);
//shape 2
ctx.moveTo(340, 1050);
ctx.lineTo(390, 820);
ctx.lineTo(450, 710);
ctx.lineTo(450, 810);
ctx.fill();
}
}
<html>
<head>
</head>
<body onload="draw();">
<canvas id="navigation" width="1500" height="1500"></canvas>
</body>
</html>
I've got this snippet of code, and I was wondering if it's possible to make Shape 1 and Shape 2 independently clickable buttons, each redirecting to a different webpage.
Here is another way of achieving it... instead of using canvas here the svg object gets used... though using an experimental feature (clip-path, css property)
<style type="text/css">
body { margin: 0; }
</style>
<style type="text/css">
svg {
display: block;
height: 0;
}
</style>
<svg>
<defs>
<clipPath id="c1">
<path
d="
M 300, 1120
L 230, 1070
L 160, 880
L 170, 770
L 260, 640
L 350, 710
L 360, 820
Z
M 340, 1050
L 390, 820
L 450, 710
L 450, 810
Z
"/>
</clipPath>
</defs>
</svg>
<style type="text/css">
a {
display: block;
width: 1500px;
height: 1500px;
background: black;
transition: all .2s;
font-size: 0;
clip-path: url(#c1);
}
a:hover {
background: lime;
}
</style>
Click me...

Animate the Matrix.translate(x,y) in snap.svg

So I have this bit of code for two SVG arrows that squint when clicked on (via Snap.svg) and I want to have one of them move up and down along the y axis in a loop to make it look like it's hovering while the other one is active (clicked upon).
The Snap documentation has a bit about translating matrices but has nothing on animating them. I'm assuming this is possible with Snap (some way or other), so how is it done?
var suspend = true;
Array.prototype.slice.call(document.querySelectorAll('.arrow_button')).forEach(function(el) {
var s = Snap(el.querySelector('svg')),
path = s.select('path'),
squint = el.getAttribute('data-path-squint'),
resetPath = path.attr('d'),
callback = function() {
path.animate({
'path': resetPath
}, 1300, mina.elastic);
setTimeout(function() {
suspend = true;
}, 150)
};
el.addEventListener('click', function() {
if (suspend) {
path.animate({
'path': squint
}, 100, mina.linear, callback);
suspend = false;
};
});
});
.arrow_button {
width: 55px;
height: 79.8px;
position: fixed;
top: 50%;
margin-top: -40.5px;
cursor: pointer;
}
.arrow_styles {
fill: none;
stroke: #000;
stroke-width: 10;
stroke-linecap: round;
}
.left {
left: 7px;
}
.right {
right: 7px;
}
<script src="https://cdn.jsdelivr.net/snap.svg/0.4.1/snap.svg-min.js"></script>
<div class="left arrow_button" onclick="" preserveAspectRatio="none" data-path-squint="M46.2,16.1L14,35.5C8.4,40,8.6,47,14,51.1l32.2,19.6">
<svg id="left_arrow" class="arrow_styles" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 60 86.8">
<path d="M43.7,8.1L20.2,33.2c-5.1,5.5-5.1,14.6,0,20.1l23.5,25.3" />
</svg>
</div>
<div class="right arrow_button" onclick="" preserveAspectRatio="none" data-path-squint="M9.9,16.1l32.2,19.4c5.6,4.5,5.5,11.5,0,15.5L9.9,70.6">
<svg id="right_arrow" class="arrow_styles" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 60 86.8">
<path d="M16.3,8.1l23.5,25.1c5.1,5.5,5.1,14.6,0,20.1L16.3,78.6" />
</svg>
</div>

Categories

Resources