Skip to content

Instantly share code, notes, and snippets.

@srajagop
Created June 19, 2015 03:09
Show Gist options
  • Save srajagop/5912c59ffb208ebaee57 to your computer and use it in GitHub Desktop.
Save srajagop/5912c59ffb208ebaee57 to your computer and use it in GitHub Desktop.
Polygon spinner
<div id='container'>
<svg class='spinner' version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px" viewBox="0 0 100 100">
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 100 100" width="100%" height="100%">
<polygon id='polygon' />
</svg>
</svg>
</div>

Polygon spinner

A loading spinner that draws an SVG triangle and then animates it to a dodecagon and back.

A Pen by neil tron on CodePen.

License.

var Spinner = function (el) {
this.svg = el,
this.timing = .3,
this.direction = 0,
this.timelineEnd = 0, // can be used to end the spinner.
this.timeline = new TimelineMax({ // initiate the timeline object
paused: true, // paused at start so the tween steps can be added. it gets started at the very end
ease: Bounce.easeOut,
yoyo: true,
repeat: -1,
onStart: this.startRepeat.bind(this),
onRepeat: this.startRepeat.bind(this)
});
this.polygon = new Polygon({ el: this.svg.querySelector('#polygon'), parent: this.svg });
this.setupTimeline();
this.timeline.play();
};
Spinner.prototype.startRepeat = function (el) {
if (this.direction) {
TweenMax.to(this.polygon.el, this.timing * this.polygon.maxSides * 1.05, { rotation: '10deg', ease: Cubic.easeInOut });
} else {
TweenMax.to(this.polygon.el, this.timing * this.polygon.maxSides, { rotation: '370deg', ease: Cubic.easeInOut });
}
this.direction = !this.direction;
};
Spinner.prototype.applyUpdates = function (tween, sides) {
var points = [];
for (var j = 0; j <= sides; j++) {
points.push(tween.target['x' + j] + ',' + tween.target['y' + j]);
}
this.polygon.el.setAttribute('points', points.join(' '));
};
Spinner.prototype.setupTimeline = function () {
for (var i = this.polygon.sides - 1; i < this.polygon.maxSides; i++) {
var finish = this.polygon.setSides(i + 1),
_sides = i;
this.timeline.to(this.polygon.points, this.timing, _.extend(finish, {
ease: Cubic.easeOut,
onUpdateParams: ["{self}", _sides],
onUpdate: this.applyUpdates.bind(this)
}));
}
// enables the spinner to hide itself at the end of a cycle
this.timeline.call(this.fadeOut.bind(this));
};
Spinner.prototype.fadeOut = function () {
if (this.timelineEnd) {
this.timeline.pause();
TweenMax.to(this.svg, .4, {
bezier: {
curviness: 1.25,
values: [
{ scale: 1.2, opacity: 1 },
{ scale: .8, opacity: .5 },
{ scale: .2, opacity: .1 },
{ scale: 0, opacity: 0 }
]
}
})
}
};
// used to kill the spinner when something finished loading.
// `timelineEnd` is checked at the end of every animation cycle
// via the `fadeOut` method above
Spinner.prototype.finish = function () {
this.timelineEnd = 1;
};
var Polygon = function (opts) {
this.el = opts.el;
this.parent = opts.parent;
this.setupPolygon();
};
Polygon.prototype.setupPolygon = function () {
// setup size properties
this.size = this.parent.getBoundingClientRect().width,
this.center = this.size / 6,
this.radius = this.center
this.sides = 1, //beginning number of sides. we start with a triangle
this.maxSides = 12, // end with a dodecagon
this.points = { // defines the initial triangle area
x0: 0, y0: 60, x1: 51, y1: -31, x2: -52, y2: -32, x3: -52,y3: -32
};
TweenMax.set(this.el, {
transformOrigin: 'center center',
x: this.size / 2,
y: this.size / 2
});
this.points = this.fillPoints(this.points, this.sides);
};
Polygon.prototype.setSides = function (sides) {
var points = {},
angle = 2 * Math.PI / sides,
i = 0; // keep track of `i` outside of for loop so we can easily pass it to `fillPoints`
for (i; i < sides; i++) {
var _angle = i * angle,
_radius = this.center + this.radius,
x = _radius * Math.sin(_angle),
y = _radius * Math.cos(_angle);
points['x' + i] = Math.floor(x);
points['y' + i] = Math.floor(y);
}
return this.fillPoints(points, i);
};
Polygon.prototype.fillPoints = function (points, i) {
// fill in remaining values to be the same as the last point
// so they dont appear on screen
var x = points['x' + (i - 1)],
y = points['y' + (i - 1)];
for (i; i < this.maxSides; i++) {
points['x' + i] = x;
points['y' + i] = y;
}
return points;
};
var spinner = new Spinner(document.querySelector('.spinner'));
// uncomment to demo the fading. in real life this would be
// called on some event, e.g. something finished loading
/*
setTimeout( function () {
spinner.finish();
}, 6000)
*/
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
html,body,#container {
background: #333;
color: #fafafa;
height: 100%;
}
#container {
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
text-align: center;
}
svg polygon {
stroke: #fafafa;
fill: transparent;
stroke-width: 2;
}
svg {
margin: 0 auto;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment