Created
April 1, 2020 18:48
-
-
Save mscalora/2383f710dfc276f4372048d4be0c35d5 to your computer and use it in GitHub Desktop.
Work towards a spin the needle animation using SVG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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