Skip to content

Instantly share code, notes, and snippets.

@rikkertkoppes
Last active November 23, 2015 09:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rikkertkoppes/a8d961daa8e877de6dd2 to your computer and use it in GitHub Desktop.
Save rikkertkoppes/a8d961daa8e877de6dd2 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">
</head>
<body>
<div class="card">
<div class="card-content">
<h2>Title</h2>
<p class="spinnerline">
<span class="pull-left">Something something</span>
<span class="pull-right" spinner>8</span>
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<h2>Title</h2>
<p class="spinnerline">
<span class="pull-left">Something something</span>
<span class="pull-right" spinner min="0" max="10">8</span>
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<h2>Title</h2>
<p class="spinnerline">
<span class="pull-left">Something something</span>
<span class="pull-right" spinner min="0" max="10">8</span>
</p>
</div>
</div>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="spinner.js"></script>
</body>
</html>
/* math */
function getDifference(from,to) {
return [
to[0] - from[0],
to[1] - from[1]
];
}
function getAngle(from,to) {
var d = getDifference(from,to);
return 180 * Math.atan2(d[1],d[0]) / Math.PI;
}
function getDistance(from,to) {
var d = getDifference(from,to);
return Math.sqrt(d[0]*d[0]+d[1]*d[1]);
}
/* widget */
function Widget() {
this.el = document.createElement('div');
this.el.classList.add('spinnerwidget');
this.mousemoveListener = this.handleMove.bind(this);
this.mouseupListener = this.handleUp.bind(this);
this.centerListener = this.hide.bind(this);
this.el.innerHTML = [
'<div class="line"></div>',
'<div class="value"></div>'
].join('');
this.lineEl = this.el.querySelector('.line');
this.valueEl = this.el.querySelector('.value');
}
Widget.prototype.getCenter = function() {
var r = this.el.getBoundingClientRect();
this.center = [
(r.left + r.right) / 2,
(r.top + r.bottom) /2
];
}
Widget.prototype.update = function(mouse) {
var newAngle = getAngle(this.center,mouse);
while (Math.abs(newAngle-this.angle) > 180) {
//angle rollover
newAngle += (newAngle < this.angle)?360:-360;
}
this.angle = newAngle;
console.log(newAngle);
this.radius = getDistance(this.center,mouse);
this.lineEl.style.transform = 'rotate('+this.angle+'deg)';
this.lineEl.style.width = (Math.max(100,this.radius))+'px';
this.value = this.startValue + Math.round((this.angle - this.startAngle) / 36);
this.valueEl.innerHTML = this.value;
}
Widget.prototype.show = function(value, start, cb) {
document.body.appendChild(this.el);
document.body.addEventListener('mousemove',this.mousemoveListener);
document.body.addEventListener('mouseup',this.mouseupListener);
this.valueEl.addEventListener('click',this.centerListener);
this.start = start;
this.getCenter();
this.startAngle = getAngle(this.center,this.start);
this.startValue = value;
this.angle = this.startAngle;
this.update(start);
this.onDone = cb;
}
Widget.prototype.hide = function() {
document.body.removeChild(this.el);
document.body.removeEventListener('mousemove',this.mousemoveListener);
document.body.removeEventListener('mouseup',this.mouseupListener);
this.valueEl.removeEventListener('click',this.centerListener);
if (this.onDone) {
this.onDone(this.value);
}
}
Widget.prototype.handleMove = function(e) {
this.update([e.pageX,e.pageY]);
e.preventDefault();
return false;
}
Widget.prototype.handleUp = function() {
console.log(this.value,this.startValue);
if (this.value !== this.startValue) {
this.hide();
}
}
/* singleton for all spinners */
var widget = new Widget();
/* spinner */
function Spinner(el,widget) {
this.el = el;
this.widget = widget;
this.value = 0;
console.log('create');
el.classList.add('spinner');
el.addEventListener('mousedown',this.activate.bind(this));
}
Spinner.prototype.activate = function(e) {
this.widget.show(this.value, [e.pageX,e.pageY], this.update.bind(this));
}
Spinner.prototype.update = function(value) {
this.value = value;
console.log(value);
this.el.innerHTML = this.value;
}
Spinner.create = function(el) {
return new Spinner(el,widget);
}
Array.prototype.slice.call(document.querySelectorAll('[spinner]')).forEach(Spinner.create);
html, body {
margin: 0;
font-family: RobotoDraft,Roboto,'Helvetica Neue',sans-serif;
}
/* generic material design stuff */
.card {
box-sizing: border-box;
margin: 8px;
box-shadow: 0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084);
background-color: rgb(255,255,255);
border-radius: 2px;
overflow: hidden;
}
.card-content {
padding: 16px;
}
.pull-left {
float: left;
}
.pull-right {
float: right;
}
h2 {
font-size: 20px;
}
p {
overflow: hidden;
}
p.spinnerline {
line-height: 48px;
}
/* spinner styling */
.spinner {
border-radius: 50%;
background-color: whitesmoke;
display: inline-block;
width: 48px;
height: 48px;
line-height: 48px;
text-align: center;
}
.spinner:hover {
background-color: darkblue;
color: white;
}
.spinnerwidget {
width: 200px;
height: 200px;
border: 2px solid silver;
background-color: rgba(0,0,0,0.1);
border-radius: 100%;
position: absolute;
left: 50%;
margin-left: -100px;
top: 100px;
}
.spinnerwidget .line {
border: 1px solid silver;
position: absolute;
left: 100px;
top: 100px;
width: 100%;
height: 0;
box-sizing: border-box;
transform-origin: 0 50%;
}
.spinnerwidget .value {
position: absolute;
left: 50%;
top: 50%;
height: 48px;
width: 48px;
margin-top: -24px;
margin-left: -24px;
line-height: 48px;
text-align: center;
font-size: 20px;
border-radius: 100%;
background-color: white;
box-shadow: 0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment