Skip to content

Instantly share code, notes, and snippets.

@mscalora
Created April 1, 2020 18:48
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 mscalora/2383f710dfc276f4372048d4be0c35d5 to your computer and use it in GitHub Desktop.
Save mscalora/2383f710dfc276f4372048d4be0c35d5 to your computer and use it in GitHub Desktop.
Work towards a spin the needle animation using SVG
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Make</title>
<style>
</style>
</head>
<body>
<div id="outer">
<div id="inner">
<p id="p">
</p>
<svg width="500" height="500" style="border:1px gray solid">
<defs>
<marker id='head' orient='auto' markerWidth='2' markerHeight='2'
refX='0.1' refY='1'>
<path d='M0,0 V2 L2,1 Z' fill='black' />
</marker>
</defs>
<g id="ring">
</g>
<filter id="saturate">
<feColorMatrix in="SourceGraphic"
type="saturate"
values="3" />
</filter>
<style>
#ring .ring-segment {
transition: f;
}
#ring .ring-segment-outline {
}
#ring .ring-segment-outline[show-outline="1"],
#ring .ring-segment-outline:hover {
stroke: #444;
stroke-width: 4px;
}
</style>
<g id="arrow">
<path
marker-end='url(#head)'
stroke-width='20' fill='none' stroke='black'
d='M225,250 l65,0'
/>
</g>
<animateTransform
xlink:href="#arrow"
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 250 250"
to="360 250 250"
dur="2s"
begin="0s"
repeatCount="indefinite"
fill="freeze"
/>
</svg>
</div>
</div>
<script>
let svg = document.documentElement,
svgNS = svg.namespaceURI;
function describeArc(x, y, radius, spread, startAngle, endAngle){
let innerStart = polarToCartesian(x, y, radius, endAngle),
innerEnd = polarToCartesian(x, y, radius, startAngle),
outerStart = polarToCartesian(x, y, radius + spread, endAngle),
outerEnd = polarToCartesian(x, y, radius + spread, startAngle),
largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1",
d = [
"M", outerStart.x, outerStart.y,
"A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
"L", innerEnd.x, innerEnd.y,
"A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y,
"L", outerStart.x, outerStart.y, "Z"
].join(" ");
return d;
}
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
let angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function createSVGNode(n, v) {
let node = document.createElementNS("http://www.w3.org/2000/svg", n);
for (let prop of Object.keys(v)) {
node.setAttributeNS(null, prop.replace(/[A-Z]/g, function (propName) {
return "-" + propName.toLowerCase();
}), v[prop]);
}
return node
}
const colorPallet = ['#3498DB', '#E74C3C', '#2ECC71', 'blueviolet', 'aqua',
'#F2CA27', 'darkcyan', 'darkorange', 'darkolivegreen', 'lime',
'darksalmon', 'yellow', 'wheat'];
function buildRing (segments, ringColors) {
let start = 0,
ring = document.querySelector('#ring'),
pallet = ringColors || colorPallet,
total = segments.reduce((a,b) => a + b, 0),
segs = segments.map(a => a * 360/total),
oPaths = [];
for (let i = 0; i < segs.length; i++) {
let node,
span = segs[i],
color = pallet[i % pallet.length],
fillPath = describeArc(250, 250, 50, 150, start, start + span),
outlinePath = describeArc(250, 250, 52, 146, start, start += span);
node = createSVGNode('path', {
fill: color,
d: fillPath,
class: 'ring-segment'
});
oPaths.push(createSVGNode('path', {
fill: 'transparent',
d: outlinePath,
class: 'ring-segment-outline',
segNum: `${i}`
}));
if (i === 0) {
ring.innerHTML = '';
}
ring.appendChild(node);
}
oPaths.forEach(node => ring.appendChild(node))
let z = 0;
window.segmentCount = segs.length;
}
let segs = colorPallet.map((x, i, a) => 360/a.length);
let segments = [10, 20, 30, 60, 120, 360 - 240];
buildRing(colorPallet.map((x, i, a) => 360/a.length));
function doReset () {
buildRing(colorPallet.map((x, i, a) => 360/a.length));
}
function doSet0 () {
buildRing([90,180,20,70]);
}
function doSet1 () {
buildRing([1,2,3]);
}
function doSet2 () {
buildRing([1,2,3,4,5,6,7,8,9]);
}
setInterval(function () {
window.segNum = ((window.segNum || 0) + 1) % window.segmentCount;
for (let node of Array.from(document.querySelectorAll('[show-outline]'))) {
node.removeAttribute('show-outline');
}
let node = document.querySelector(`[seg-num="${window.segNum}"]`);
if (node) {
node.setAttribute('show-outline', '1');
}
}, 33);
</script>
<button type="button" onclick="doReset()">Reset</button>
<button type="button" onclick="doSet0()">Set 0</button>
<button type="button" onclick="doSet1()">Set 1</button>
<button type="button" onclick="doSet2()">Set 2</button>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment