<p class="note">See <a href="https://codepen.io/vidhill/pen/XJrxdrQ" target="_blank">2024 re-implementation</a> using SVG 2 <code>getPathData()</code> and <code>setPathData()</code> methods
</pre>
<p class="note">
This version uses <code>PathSegList</code> which was deprecated and was removed in Chrome 48<br>
Using the polyfill for <a href="https://github.com/progers/pathseg" target="_blank"><code>PathSegList</code></a> in this example.
</p>
<div>
<svg id="my-svg" viewbox="0 0 36 36">
<path class="hide" id="play" d="M11,10 L18,13.74 18,22.28 11,26 M18,13.74 L26,18 26,18 18,22.28" />
<path class="hide" id="pause" d="M11,10 L17,10 17,26 11,26 M20,10 L26,10 26,26 20,26" />
</svg>
<button id="reset-btn">Reset SVG</button>
</div>
svg {
width: 15%;
background: #A4A6BF;
display: block;
margin: 2em auto;
cursor: pointer;
}
svg path {
fill: #000;
stroke: none;
stroke-width: 2;
}
svg path.hide {
display: none;
}
body {
background: #6A6D95;
}
h1 {
color: #FFF;
opacity: 0.8;
font-family: sans-serif;
text-align: center;
margin: 1em 0;
}
button {
border: none;
display: block;
background: #A4A6BF;
color: #555;
font-size: 1em;
padding: 0.5em 1em;
margin: 1em auto;
}
(function(document){
var svg = document.getElementById("my-svg"),
paths = svg.getElementsByTagName('path'),
fromPathPoints = paths[0].pathSegList,
toPathPoints = paths[1].pathSegList,
numPoints = fromPathPoints.numberOfItems,
clonedPath = paths[0].cloneNode()
;
console.log(fromPathPoints);
clonedPath.setAttribute('class', '');
clonedPath.id = 'myClone';
svg.appendChild(clonedPath);
var getPathDiff = function(){
var diffPoints = [];
for( var i = 0; i < numPoints; i++ ){
/*
M { x,y
L { x,y
T { x,y
Q { x,y, x1, y1,
C { x,y, x1, y1, x2, y2
vV { y
hH { x
*/
var point = {},
fromPoint = fromPathPoints.getItem(i),
toPoint = toPathPoints.getItem(i),
typeLetter = fromPoint.pathSegTypeAsLetter,
pointX = fromPoint.x - toPoint.x,
pointY = fromPoint.y - toPoint.y
;
switch(typeLetter.toUpperCase()) {
case 'V':
point.y = pointY;
break;
case 'H':
point.x = pointX;
break;
case 'C':
point.x2 = fromPoint.x2 - toPoint.x2;
point.y2 = fromPoint.y2 - toPoint.y2;
case 'Q':
point.x1 = fromPoint.x1 - toPoint.x1;
point.y1 = fromPoint.y1 - toPoint.y1;
case 'T':
case 'M':
case 'L':
point.x = pointX;
point.y = pointY;
}
diffPoints.push(point);
}
return diffPoints;
}
var diffPoints = getPathDiff();
// Set Points
var morphPath = function(path, fromPath, diffObj, multiplier){
var sourcePathPoints = fromPath.pathSegList,
destPathPoints = path.pathSegList;
for( var k = 0; k < numPoints; k++ ){
var sourcePoint = sourcePathPoints.getItem(k),
segTypeLetter = sourcePoint.pathSegTypeAsLetter,
toX = sourcePoint.x - (diffObj[k].x)*multiplier,
toY = sourcePoint.y - (diffObj[k].y)*multiplier;
switch(segTypeLetter.toUpperCase()) { // ignore case
case 'V':
destPathPoints.getItem(k).y = toY;;
break;
case 'H':
destPathPoints.getItem(k).x = toX;
break;
case 'C': // cubic
destPathPoints.getItem(k).x2 = sourcePoint.x2 - (diffObj[k].x2)*multiplier;
destPathPoints.getItem(k).y2 = sourcePoint.y2 - (diffObj[k].y2)*multiplier;
case 'Q': // quadratic
destPathPoints.getItem(k).x1 = sourcePoint.x1 - (diffObj[k].x1)*multiplier;
destPathPoints.getItem(k).y1 = sourcePoint.y1 - (diffObj[k].y1)*multiplier;
case 'T':
case 'M': // move
case 'L': // line
destPathPoints.getItem(k).x = toX;
destPathPoints.getItem(k).y = toY;
}
}
}
function done(){
console.log('Animation Done')
}
var letsAnimate = function(duration, morphFunc, doneCallback){
var animTime = 0,
position = 0
startTime = performance.now()
;
function frame(time){
animTime = time - startTime;
// Are we done?
if (animTime >= duration) {
// last rendered value wasn't final position, set it here.
morphFunc(1);
doneCallback();
}
else {
// What position should the animation be in?
position = (animTime / duration).toPrecision(3);
//console.log(position)
morphFunc(position);
// Request the next frame
requestAnimationFrame(frame);
}
}
// reqest the first frame
requestAnimationFrame(frame);
};
svg.addEventListener("click", function(){
letsAnimate(500, function(pos){
morphPath(clonedPath, paths[0], diffPoints, pos);
}, done);
});
var resetBtn = document.getElementById("reset-btn");
resetBtn.addEventListener("click", function(){
clonedPath.setAttribute('d', paths[0].getAttribute('d') );
});
})(document);
This Pen doesn't use any external CSS resources.