Yesvinkumar
Yesvinkumar

Reputation: 559

SVG Animation: Object along a path on a scroll

I have created an SVG animation: Object along a path on a scroll.

Please check the below code and Codepen demo

HTML

    <circle r="2" cy="18.591" cx="169.887" fill="red" fill-opacity=".96" fill-rule="evenodd" stroke="red" stroke-width=".55"/>
    <circle r="2" id="dot" cy="-5" cx="0"  fill="red" fill-opacity=".96" fill-rule="evenodd" stroke="red" stroke-width=".55"/>

    <path id="c" d="M174.093 22.89a.384.384 0 0 0-.148.037l-.921.437-.002-.009a.113.113 0 0 0-.151-.054l-.978.463a.113.113 0 0 0-.054.151l.005.007-1.049.497a.384.384 0 0 0-.183.512l2.25 4.747c.066.14.205.22.35.219-.056.058-.101.15-.04.28l1.395 2.942-.498-.03a.134.134 0 0 0-.016.267l.644.04.602 1.27c.25.527.874.75 1.4.5l.346-.164a.113.113 0 0 0 .155.14l.978-.463a.113.113 0 0 0-.01-.209l.346-.164c.527-.249.75-.874.5-1.4l-.68-1.437.439-.45a.133.133 0 0 0-.003-.189.133.133 0 0 0-.19.002l-.369.379-1.306-2.757c-.069-.155-.164-.195-.25-.193a.382.382 0 0 0 .048-.404l-2.248-4.747a.384.384 0 0 0-.363-.22z" fill="#1a1a1a" fill-rule="evenodd"/>
</svg>

CSS

#route {
  margin-top: 200px;
}

.code {
  height:150px;
  width:250px;
  background:#000;
  color:#fff;
  margin:20px;
  width: 40%;
  clear: both;
  height: 200px;
  background: #000;
  border-radius: 2px;
  margin: 100vh 0;
  padding: 10px;
}

JS

$(document).ready(function(){           
    $(window).scroll(function() {
        drawLine( $('#bx_a'),document.getElementById('path') );
        positionTheDot();
        //positionCar();            
    });

    // init the line length
    drawLine( $('#bx_a'),document.getElementById('path') );
    positionTheDot();       
    //positionCar();

    function positionTheDot() {
    // What percentage down the page are we? 
    var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
    // Get path length
    var path = document.getElementById("path");
    var pathLen = path.getTotalLength();
    // Get the position of a point at <scrollPercentage> along the path.
    var pt = path.getPointAtLength(scrollPercentage * pathLen);
    var  scrollY = window.scrollY || window.pageYOffset;
    var  maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
   var  path = document.getElementById("path");
   // Calculate distance along the path the car should be for the current scroll amount
   var  pathLen = path.getTotalLength();
   var  dist = pathLen * scrollY / maxScrollY;
   var  pos = path.getPointAtLength(dist);
   // Calculate position a little ahead of the car (or behind if we are at the end), so we can calculate car angle
   if (dist + 1 <= pathLen) {
    var  posAhead = path.getPointAtLength(dist + 1);
    var  angle = Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
   } else {
    var  posBehind = path.getPointAtLength(dist - 1);
    var  angle = Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
   }

   // Position the red dot at this point
   var dot = document.getElementById("dot");          
   dot.setAttribute("transform", "translate("+ pt.x + "," + (pt.y+5) + ")");        
   var  car = document.getElementById("c");
   car.setAttribute("transform", "translate(" + (pt.x-171) + "," + (pt.y-21) + ")");
   //car.setAttribute("transform", "translate(" + (pt.x-171) + "," + (pt.y) + ") rotate(" + (rad2deg(angle)) + ")");
   };

   //draw the line
   function drawLine(container, line) {
    var pathLength = line.getTotalLength(),
    maxScrollTop = $(document).height() - $(window).height(),
    percentDone = $(window).scrollTop() / maxScrollTop,
    length = percentDone * pathLength;
    line.style.strokeDasharray = [ length,pathLength].join(' ');
   }

   function positionCar() {
    var  scrollY = window.scrollY || window.pageYOffset;
    var  maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
    var  path = document.getElementById("path");
    // Calculate distance along the path the car should be for the current scroll amount
    var  pathLen = path.getTotalLength();
    var  dist = pathLen * scrollY / maxScrollY;
    var  pos = path.getPointAtLength(dist);
    // Calculate position a little ahead of the car (or behind if we are at the end), so we can calculate car angle
    if (dist + 1 <= pathLen) {
        var  posAhead = path.getPointAtLength(dist + 1);
        var  angle = Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
    } else {
        var  posBehind = path.getPointAtLength(dist - 1);
        var  angle = Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
    }
    // Position the car at "pos" totated by "angle"
    var  car = document.getElementById("c");
    car.setAttribute("transform", "translate(" + (pos.x) + "," + (pos.y) + ") rotate(" + (rad2deg(angle)) + ")");
    }

    function rad2deg(rad) {
        return 180 * rad / Math.PI;
    }    
});

Please check the Codepen demo: https://codepen.io/yesvin/pen/XymwvX

The problem is that the object (ex: Car) inside the SVG is not rotating itself along the path. I tried with the transform rotation but it is not working. Please check the commented line of the SVG set attribute function.

So, How to achieve, that the object has to rotate itself along a path on a scroll? Is there any other calculations to calculate the objects angle and path's length?

Thanks in advance.

Upvotes: 2

Views: 2858

Answers (1)

ccprog
ccprog

Reputation: 21921

The issue is the center of rotation, and the initial angle. Currently, the initial position of the car is such that its rotation center should be at (171, 21). You could start out with translating that point to (0, 0), and then rotate it by -65deg so that it points to the right. In this case, it is easier to rewrite the path data to incorporate these transformations:

d="M 3.02008,-2.00446 A 0.384,0.384 0 0 0 2.99106,-1.85469 L 2.99789,-0.835296 2.98889,-0.837286 A 0.113,0.113 0 0 0 2.87613,-0.723255 L 2.88243,0.358786 A 0.113,0.113 0 0 0 2.99646,0.471542 L 3.00492,0.469969 3.01203,1.63073 A 0.384,0.384 0 0 0 3.39872,2.01296 L 8.65185,1.97994 C 8.80663,1.97929 8.93788,1.88712 8.99825,1.75528 9.02715,1.83055 9.09151,1.91021 9.23511,1.90987 L 12.491,1.88891 12.2534,2.32758 A 0.134,0.134 0 0 0 12.4886,2.45492 L 12.797,1.88816 14.2024,1.87929 C 14.7857,1.87543 15.2515,1.40414 15.2473,0.821764 L 15.2448,0.438872 A 0.113,0.113 0 0 0 15.4372,0.357561 L 15.4309,-0.72448 A 0.113,0.113 0 0 0 15.2373,-0.803744 L 15.2349,-1.18664 C 15.2319,-1.76949 14.7597,-2.23574 14.1774,-2.23146 L 12.5876,-2.22247 12.3653,-2.81052 A 0.133,0.133 0 0 0 12.1927,-2.88767 0.133,0.133 0 0 0 12.1143,-2.71463 L 12.3018,-2.22003 9.25118,-2.20155 C 9.08154,-2.20452 9.00514,-2.13533 8.9706,-2.05654 A 0.382,0.382 45 0 0 8.62474,-2.27078 L 3.37245,-2.23957 A 0.384,0.384 0 0 0 3.01965,-2.00355 Z"

$(document).ready(function(){

  $(window).scroll(function() {
    drawLine( $('#bx_a'),document.getElementById('path') );
    positionTheDot();
    positionCar();			
  });

  // init the line length
  drawLine( $('#bx_a'),document.getElementById('path') );
  positionTheDot();		
  positionCar();

  function positionTheDot() {
    // What percentage down the page are we? 
    var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
    // Get path length
    var path = document.getElementById("path");
    var pathLen = path.getTotalLength();
    // Get the position of a point at <scrollPercentage> along the path.
    var pt = path.getPointAtLength(scrollPercentage * pathLen);

    var  scrollY = window.scrollY || window.pageYOffset;
    var  maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
    var  path = document.getElementById("path");
    // Calculate distance along the path the car should be for the current scroll amount
    var  pathLen = path.getTotalLength();
    var  dist = pathLen * scrollY / maxScrollY;
    var  pos = path.getPointAtLength(dist);
    // Calculate position a little ahead of the car (or behind if we are at the end), so we can calculate car angle
    if (dist + 1 <= pathLen) {
      var  posAhead = path.getPointAtLength(dist + 1);
      var  angle = Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
    } else {
      var  posBehind = path.getPointAtLength(dist - 1);
      var  angle = Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
    }

    // Position the red dot at this point
    var dot = document.getElementById("dot");		  
    dot.setAttribute("transform", "translate("+ pt.x + "," + (pt.y+5) + ")");
  };

  //draw the line
  function drawLine(container, line) {
    var pathLength = line.getTotalLength(),
    maxScrollTop = $(document).height() - $(window).height(),
    percentDone = $(window).scrollTop() / maxScrollTop,
    length = percentDone * pathLength;
    line.style.strokeDasharray = [ length,pathLength].join(' ');
  }

  function positionCar() {
    var  scrollY = window.scrollY || window.pageYOffset;
    var  maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
    var  path = document.getElementById("path");
    // Calculate distance along the path the car should be for the current scroll amount
    var  pathLen = path.getTotalLength();
    var  dist = pathLen * scrollY / maxScrollY;
    var  pos = path.getPointAtLength(dist);
    // Calculate position a little ahead of the car (or behind if we are at the end), so we can calculate car angle
    if (dist + 1 <= pathLen) {
    var  posAhead = path.getPointAtLength(dist + 1);
    var  angle = Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
    } else {
    var  posBehind = path.getPointAtLength(dist - 1);
    var  angle = Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
    }
    // Position the car at "pos" totated by "angle"
    var  car = document.getElementById("c");
    car.setAttribute("transform", "translate(" + (pos.x) + "," + (pos.y) + ") rotate(" + (rad2deg(angle)) + ")");
  }

  function rad2deg(rad) {
    return 180 * rad / Math.PI;
  }

});
#route {
  margin-top: 200px;
}
.code {
	height:150px;
	width:250px;
	background:#000;
	color:#fff;
	margin:20px;
	width: 40%;
    clear: both;
    height: 200px;
    background: #000;
    border-radius: 2px;
    margin: 100vh 0;
    padding: 10px;
}
svg {
	/*width:100%;
	height:auto;*/
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="position:absolute; top:0; right:0; width:100%; height:auto; background:url('background-map.jpg') no-repeat;" id="route">
	<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 334 1426" id="svgRoute">
		<path id="path" d="M170 19s18 32 18 57c0 48-40 51-40 72 0 22 43 31 43 44 0 10-9.4 10-10 23-.7 14 2.3 16 2.2 28-.17 12-14 24-21 32-6.5 7.2-17 15-20 23-2.4 6.8-3.5 23 12 29 16 5.7 32 10 37 24 5.2 13-7.4 26-17 33-9.2 6.6-30 20-26 30 3.5 9.9 27 6.2 27 24 0 18 8.8 13 8.8 24-11 24 16 50 13 71-.46 17-15 23-8.2 42-32 37 1.3 83 26 106 28 20-33 5.7-33 39 2.4 51-28 113-34 139-4.1 18-3.5 41 18 41 8.1-.2 14-6.5 15-14 .37-6.3-6.1-14-16-12-11 1.8-16 11-19 24-1.3 6.6-2.1 17-2.3 28-.43 29 14 36 19 39 2.6 1.8 19 8.8 19 17 10 18-41 7.8-35 28 .61 10 37 22 36 35 8.4 94-128 42-89 130 8.8 36-63 33-60 68-2.1 25 35 32 38 44" stroke="red" fill="none"/>
		
		<circle r="2" cy="18.591" cx="169.887" fill="red" fill-opacity=".96" fill-rule="evenodd" stroke="red" stroke-width=".55"/>
		<circle r="2" id="dot" cy="-5" cx="0"  fill="red" fill-opacity=".96" fill-rule="evenodd" stroke="red" stroke-width=".55"/>
		
		<path id="c" d="M 3.02008,-2.00446 A 0.384,0.384 0 0 0 2.99106,-1.85469 L 2.99789,-0.835296 2.98889,-0.837286 A 0.113,0.113 0 0 0 2.87613,-0.723255 L 2.88243,0.358786 A 0.113,0.113 0 0 0 2.99646,0.471542 L 3.00492,0.469969 3.01203,1.63073 A 0.384,0.384 0 0 0 3.39872,2.01296 L 8.65185,1.97994 C 8.80663,1.97929 8.93788,1.88712 8.99825,1.75528 9.02715,1.83055 9.09151,1.91021 9.23511,1.90987 L 12.491,1.88891 12.2534,2.32758 A 0.134,0.134 0 0 0 12.4886,2.45492 L 12.797,1.88816 14.2024,1.87929 C 14.7857,1.87543 15.2515,1.40414 15.2473,0.821764 L 15.2448,0.438872 A 0.113,0.113 0 0 0 15.4372,0.357561 L 15.4309,-0.72448 A 0.113,0.113 0 0 0 15.2373,-0.803744 L 15.2349,-1.18664 C 15.2319,-1.76949 14.7597,-2.23574 14.1774,-2.23146 L 12.5876,-2.22247 12.3653,-2.81052 A 0.133,0.133 0 0 0 12.1927,-2.88767 0.133,0.133 0 0 0 12.1143,-2.71463 L 12.3018,-2.22003 9.25118,-2.20155 C 9.08154,-2.20452 9.00514,-2.13533 8.9706,-2.05654 A 0.382,0.382 45 0 0 8.62474,-2.27078 L 3.37245,-2.23957 A 0.384,0.384 0 0 0 3.01965,-2.00355 Z" fill="#1a1a1a" fill-rule="evenodd"/>
	</svg>
</div>

Upvotes: 5

Related Questions