te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>html - SVG `<path>` javascript animation not working as expected - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - SVG `<path>` javascript animation not working as expected - Stack Overflow

programmeradmin3浏览0评论

What I have achieved:

// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;

// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);

function myFunction() {
  // What % down is it?
  var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
  // Length to offset the dashes
  var draw = length * scrollpercent;

  // Reverse the drawing (when scrolling upwards)
  myline.style.strokeDashoffset = length - draw;

  //get point at length
  endPoint = myline.getPointAtLength(draw);
  circle.setAttribute("cx", endPoint.x);
  circle.setAttribute("cy", endPoint.y);

}
body {
  height: 2000px;
  background: #f1f1f1;
}

#circle {
  fill: red;
}

#mySVG {
  position: absolute;
  top: 15%;
  width: 100%;
  height: 1000px;
  
}

.st1 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: grey;
  stroke-width: 4;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
.st0 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 5;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <path  class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <circle id="circle" cx="10" cy="10" r="10"/>
  <path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

What I have achieved:

// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;

// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);

function myFunction() {
  // What % down is it?
  var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
  // Length to offset the dashes
  var draw = length * scrollpercent;

  // Reverse the drawing (when scrolling upwards)
  myline.style.strokeDashoffset = length - draw;

  //get point at length
  endPoint = myline.getPointAtLength(draw);
  circle.setAttribute("cx", endPoint.x);
  circle.setAttribute("cy", endPoint.y);

}
body {
  height: 2000px;
  background: #f1f1f1;
}

#circle {
  fill: red;
}

#mySVG {
  position: absolute;
  top: 15%;
  width: 100%;
  height: 1000px;
  
}

.st1 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: grey;
  stroke-width: 4;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
.st0 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 5;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <path  class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <circle id="circle" cx="10" cy="10" r="10"/>
  <path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

What I want is no matter which size or shape the SVG <path> is the growing line should be in the middle of the screen.

I tried changing the values of myline.style.strokeDashoffset = length //+newvalue - draw; and all but all it did was just ruins the consistency. so is there anyone who can help me solve this issue.?

Any help would be highly appreciatable.

Share Improve this question edited Jul 12, 2018 at 6:59 Jithin Raj P R asked Jul 3, 2018 at 9:19 Jithin Raj P RJithin Raj P R 6,7978 gold badges40 silver badges71 bronze badges 2
  • I know using fixed solves this issue to an extent but I need to go with the position absolute as I need this to relative to some other element on my page – Jithin Raj P R Commented Jul 3, 2018 at 9:24
  • Guys you can see in this link - stackoverflow./questions/45540161/… , I know that will work but I have many other UI's in whcih I want the path to se relative to another element. – Jithin Raj P R Commented Jul 3, 2018 at 9:28
Add a ment  | 

4 Answers 4

Reset to default 8 +50

(Update / New answer)

I think this is exactly what you want...

Update (added easing as OP wanted):

Made increase in length of #body smooth by adding transition: stroke-dashoffset ease-in-out 0.2s; to #body

Movement of #head can't be made smooth by adding transition: cx ease-in-out 0.2s, cy ease-in-out 0.2s; on #head because that will make it jump instead of moving along the path.

If you want #head to move smoothly along the track you'll have to do it by manually implementing an easing with js, which is a lot of work. So skipped that part.

let roadmapSvg = document.getElementById("roadmap-svg");
let track = document.getElementById("track");
let body = document.getElementById("body");
let head = document.getElementById("head");

let totalLength = track.getTotalLength();
let trackPoints = [];
let getTrackBounds = () => track.getBoundingClientRect();
let scaleFactor;

body.style.strokeDasharray = totalLength;
body.style.strokeDashoffset = totalLength;

function setScaleFactor(){
  scaleFactor = roadmapSvg.getBoundingClientRect().width / roadmapSvg.viewBox.baseVal.width;
}   
setScaleFactor();

function setTrackPoints(){
  let divisions = 1000;
  let unitLength = totalLength / divisions;
  trackPoints = [];
  for(let i=0; i < divisions; i++){
    let length = unitLength * i;
    let {x,y} = track.getPointAtLength(length);
    trackPoints.push({x: x*scaleFactor, y: y*scaleFactor, length});
  }
}
setTrackPoints();


function draw(){
  let currentLength = getCurrentLength();
  body.style.strokeDashoffset = totalLength - currentLength;
  headPos = track.getPointAtLength(currentLength);
  head.setAttribute("cx", headPos.x);
  head.setAttribute("cy", headPos.y);
}

function getCurrentLength(){
  let centreY = window.innerHeight / 2;
  let trackBounds = getTrackBounds();
  let currentY = centreY - trackBounds.y;
  if(currentY < 0) return 0;
  
  // if currentY is greater that track height, that means the user has scrolled pass the track (and the whole svg) in such case the animation should be pleted i.e. the head should be at the final position i.e. at totalLength 
  if(currentY > trackBounds.height) return totalLength;
  
  for(let point of trackPoints){
    if(point.y >= currentY){
      return point.length;
    }
  }
  
  // (For safety) Sometimes none of the conditions match bcoz of low precision... Such situation only occurs a point very close to total length... Thus...
  return totalLength;
}

document.addEventListener("scroll", draw);

window.addEventListener("resize", () => {
  setScaleFactor();
  setTrackPoints();
  draw();
});
body {
  background: #f1f1f1;
  margin: 0;
  padding: 0 20%;
  font-family: sans-serif;
}

#roadmap-svg{
  display: block;
  max-width: 600px;
  margin: 20px auto;
  overflow: visible;
}

#roadmap-svg #head{
  fill: red;
}

#roadmap-svg #track{
  fill: none;
  stroke-dashoffset: 3px;
  stroke: grey;
  stroke-width: 4;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
#roadmap-svg #body{
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 5;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
  transition: stroke-dashoffset ease-in-out 0.2s;
}

.center-line{
  position: fixed;
  left: 0;
  right: 0;
  top: 50%;
  border-top: 1px solid red;
  background-color: rgba(255,255,255,0.9);
}
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora in eaque rem eligendi corrupti voluptate, maxime cum cumque, eius delectus minus neque, dolorem optio cupiditate ratione! Excepturi fugit culpa quo?
Cum optio error ex voluptatem rerum eius sunt, nemo necessitatibus, exercitationem voluptatum illum, rem quibusdam accusamus deserunt sed. Iste odio obcaecati enim voluptate temporibus ab illo maxime et sit minima.
Odio ut dignissimos sed dicta recusandae esse, at molestiae quibusdam, consequatur aspernatur facilis, perferendis voluptatum adipisci. Dolores molestiae quos, doloribus excepturi officiis laborum ex officia reprehenderit esse perspiciatis alias itaque.
Delectus illum, asperiores at a ab quibusdam corporis necessitatibus. Libero eos vero blanditiis modi cum rem maxime delectus quisquam, facilis saepe sed eius corrupti nobis sunt, unde obcaecati modi velit.
Saepe adipisci consectetur blanditiis quos enim praesentium, at magnam quibusdam nisi! Dolore, esse beatae! Enim, quam cum, qui voluptates fugiat, nihil mollitia possimus doloremque porro aspernatur nesciunt velit. Cum, adipisci?
Dolores doloribus nihil delectus consequuntur id assumenda tempora, illum, earum ab quasi quaerat sequi et hic veniam excepturi eligendi quod perspiciatis voluptatem ratione reprehenderit! Corrupti minima facilis soluta adipisci animi!
Iure, sed exercitationem. Quidem assumenda omnis dicta ducimus sunt, quibusdam excepturi molestias cumque! Illum ipsum perferendis dicta optio eum consequuntur soluta, corrupti nostrum est sed quaerat voluptates dolores perspiciatis? Ex!
Consequatur corporis ratione beatae. Magni amet doloribus deserunt, accusamus suscipit earum accusantium perferendis adipisci inventore, ab modi odio necessitatibus aut omnis. Et quisquam esse deleniti, reprehenderit nihil optio aperiam fugit.
Aliquid error voluptatibus, quis quo eveniet nulla corrupti veniam culpa voluptas possimus tenetur nisi recusandae quae modi, animi dolores. Provident saepe nobis quos tenetur, veritatis laborum cupiditate molestias fugit consectetur.
A, perspiciatis illo sequi non eos facere temporibus dignissimos blanditiis ipsum harum eius culpa adipisci est ab nobis saepe mollitia quis laboriosam tenetur, repellat molestias. Quos ipsa magni dolores rerum.</div>
<svg id="roadmap-svg" viewBox="0 0 760 300">
  <path  id="track" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" />
  <path id="body" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" />
  <circle id="head" cx="10" cy="10" r="10"/>
</svg>
<div class="center-line">Center Line</div>
<div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente officia saepe facilis? Cupiditate rem vel, quaerat ratione ipsam magnam fugiat praesentium incidunt! Eveniet cum officia impedit obcaecati id animi rerum?
Non beatae inventore quos optio temporibus ratione doloremque ullam animi dolore reiciendis sint, esse consequatur asperiores assumenda repudiandae obcaecati ab quas molestias harum eveniet amet natus ea? Ipsum, dolore suscipit.
Explicabo assumenda minus, reprehenderit modi, laboriosam placeat saepe at repudiandae perferendis fugit asperiores itaque. Vero fugiat voluptas asperiores dolores dolorum quis ipsa sapiente deleniti odio, deserunt, iure voluptates. Error, tempore.
Doloribus nesciunt praesentium ad aut minus aliquam aspernatur quas qui incidunt sunt, maxime tempora facilis, cum assumenda. Dolorum a tempore itaque impedit, ad, corporis tenetur enim nulla quas, harum fuga!
Quae repellat, obcaecati voluptate inventore quidem, labore quo corporis repudiandae, vel doloremque perferendis numquam aliquam nisi? Vel architecto ullam fugiat error corrupti? Cumque amet illo, possimus assumenda eos unde deleniti.
Enim tenetur possimus a neque, voluptatum reprehenderit, cum magni blanditiis quam atque dolorum veniam eveniet repellendus. Modi quibusdam maxime corrupti harum! Ullam vitae assumenda laboriosam nam officia eaque. Totam, dolorem.
Ad sapiente itaque blanditiis, sint iusto nemo laborum corrupti cupiditate obcaecati quam ipsa quis perferendis vitae enim atque ex a ratione. Doloribus aspernatur id ipsa recusandae labore aliquid, totam aperiam?
Recusandae delectus quidem, aspernatur nulla expedita accusantium quod praesentium inventore qui, pariatur ullam maxime! Numquam, sed sequi rem voluptates asperiores qui, culpa nesciunt magnam, quas doloribus praesentium et adipisci tempora.
Veniam, placeat vel nesciunt recusandae voluptates laboriosam totam doloremque saepe. Nam quo similique vero esse possimus architecto officiis harum ratione perspiciatis dolor ut, molestias odit consequatur quam asperiores? Id, quasi!
Ex expedita impedit aliquam et modi voluptatibus, consequatur voluptate ea explicabo deserunt. Sapiente quo consequuntur enim dolores ea officia. Inventore ipsa dignissimos iste qui magnam reiciendis eveniet optio laudantium fugiat!</div>

I tried several ways to get point moving

  • inside the box
  • not sliding away until end
  • with minimal changes to original.

I made the following changes (working example below):

  • divider for percent I put 2000 (equal to 2000px, the containing body height)
  • I multiplied the scroll amount from top by 18 (suitable value was a promise whether the top or bottom bees behaving wierdly)
  • then finally I checked the percent value is not greater to one (it started to eat the 'worm' from the other end).

That's it! Maybe not the fanciest, but works.

Problematic here is the svg line is not linear from top to down, so direct element related values could not be chosen, or I at least did not find some. Thus, I ended up to simple solution and playing with parameters.

// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;

// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);

function myFunction() {
  // What % down is it?
  var scrollpercent = (document.documentElement.scrollTop * 18) / 2000;
  if (scrollpercent > 1) scrollpercent = 1;
  var draw = length * scrollpercent;

  // Reverse the drawing (when scrolling upwards)
  myline.style.strokeDashoffset = length - draw;

  //get point at length
  endPoint = myline.getPointAtLength(draw);
  circle.setAttribute("cx", endPoint.x);
  circle.setAttribute("cy", endPoint.y);
}
body {
  height: 2000px;
  background: #f1f1f1;
}

#circle {
  fill: red;
}

#mySVG {
  position: absolute;
  top: 15%;
  width: 100%;
  height: 1000px;
  
}

.st1 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: grey;
  stroke-width: 4;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
.st0 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 5;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <path  class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <circle id="circle" cx="10" cy="10" r="10"/>
  <path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

As per the ments and few modification to css and svg code, i was able to get the line to the center of the page, please check the working example below:

// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;

// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);

function myFunction() {
  // What % down is it?
  var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
  // Length to offset the dashes
  var draw = length * scrollpercent;

  // Reverse the drawing (when scrolling upwards)
  myline.style.strokeDashoffset = length - draw;

  //get point at length
  endPoint = myline.getPointAtLength(draw);
  circle.setAttribute("cx", endPoint.x);
  circle.setAttribute("cy", endPoint.y);
}
body {
  margin: 0;
  height: 1000px;
  background: #f1f1f1;
}
#circle {
  fill: red;
}
#mySVG {
  top: 15%;
  position: absolute;
  width: 100%;
}
.st1 {
  fill: none;
  stroke-dashoffset: 1;
  stroke: grey;
  stroke-width: .5;
  stroke-miterlimit: 1;
  stroke-dasharray: 2;
}
.st0 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 1;
  stroke-miterlimit: 1;
  stroke-dasharray: 2;
}
.grid {
  position: fixed;
  width: 1px;
  height: 100%;
  background: blue;
  left: 50%;
  top: 0;
}
<div class="grid"></div>
<svg id="mySVG" viewBox="0 0 200 72" preserveAspectRatio="xMidYMin slice">
  <path class="st1" stroke-dasharray="10,9" d="m 0,5 0,4 c 0,3 2,6 5,6 l 108,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -25,0 c -3,0 -6,3 -6,6 0,3 3,6 6,6 l 35,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -18,0 c -3,0 -5,3 -5,6 l 0,4" />
</svg>

<svg id="mySVG" viewBox="0 0 200 72" preserveAspectRatio="xMidYMin slice">
  <circle id="circle" cx="0" cy="3" r="2" />
  <path id="myline" class="st0" stroke-dasharray="10,9" d="m 0,5 0,4 c 0,3 2,6 5,6 l 108,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -25,0 c -3,0 -6,3 -6,6 0,3 3,6 6,6 l 35,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -18,0 c -3,0 -5,3 -5,6 l 0,4" />
</svg>

I'd choose a different approach. I understand you want to find the point on the path that is closest to the middle of the screen. Let's do that:

  1. Find the coordinates of the middle of the screen and convert them to the coordinate system of the path. The SVG API has two functions for that: .getScreenCTM() and SVGPoint.matrixTransform().
  2. Find the point on the path (and its distance along the path) that is nearest to these coordinates. There is a bit of math and a search algorithm involved to do that. Mike Bostock has shown such an algorithm, and it's used here. Note that his function is open to a bit of tweaking (the precision parameter).
  3. Use these data to draw the circle and the dashoffset.

It is probably a good idea to refine this by introducing a throttle for the scroll events (second variant) and then set CSS transitions to avoid visible jumps.

Transitions for the circle positioning only work with a CSS transform property. (My solution does not neccessarily move the circle along the path while transitioning. It's possible to achieve that, but goes far beyond the scope of this answer.)

var mySVG = document.getElementById("mySVG");
// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var pathLength = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = pathLength;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = pathLength;

// throttled scroll event listener
function throttle(ms, callback) {
    var timer, lastCall=0;

    return function() {
        var now = new Date().getTime(),
            diff = now - lastCall;
        if (diff >= ms) {
            lastCall = now;
            callback();
        }
    };
}

window.addEventListener("scroll", throttle(100, myFunction));

// one initial execution
myFunction();

function myFunction() {
  var center = mySVG.createSVGPoint();
  // middle of browser viewport
  center.x = window.innerWidth / 2;
  center.y = window.innerHeight / 2;
  // transform to path coordinate system
  var matrix = myline.getScreenCTM().inverse();
  center = center.matrixTransform(matrix);

  //find nearest length on path
  var draw = getNearestLength(center);

  // Reverse the drawing (when scrolling upwards)
  myline.style.strokeDashoffset = -draw - pathLength;

  //get point at length
  endPoint = myline.getPointAtLength(draw);
  circle.style.transform = "translate(" + endPoint.x + "px, " + endPoint.y + "px)";
}

function getNearestLength(point) {
  var precision = 8,
      best,
      bestLength,
      bestDistance = Infinity;
  // linear scan for coarse approximation
  for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
    if ((scanDistance = distance2(scan = myline.getPointAtLength(scanLength))) < bestDistance) {
      best = scan, bestLength = scanLength, bestDistance = scanDistance;
    }
  }
  // binary search for precise estimate
  precision /= 2;
  while (precision > 0.5) {
    var before,
        after,
        beforeLength,
        afterLength,
        beforeDistance,
        afterDistance;
    if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = myline.getPointAtLength(beforeLength))) < bestDistance) {
      best = before, bestLength = beforeLength, bestDistance = beforeDistance;
    } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = myline.getPointAtLength(afterLength))) < bestDistance) {
      best = after, bestLength = afterLength, bestDistance = afterDistance;
    } else {
      precision /= 2;
    }
  }
  return bestLength;

  function distance2(p) {
    var dx = p.x - point.x,
        dy = p.y - point.y;
    return dx * dx + dy * dy;
  }
}
body {
  height: 2000px;
  background: #f1f1f1;
}

#circle {
  fill: red;
}

#mySVG {
  position: absolute;
  top: 15%;
  width: 100%;
  height: 1000px;
  
}

.st1 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: grey;
  stroke-width: 4;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
}
.st0 {
  fill: none;
  stroke-dashoffset: 3px;
  stroke: red;
  stroke-width: 5;
  stroke-miterlimit: 10;
  stroke-dasharray: 20;
  transition: stroke-dashoffset 0.2s;
}

#circle {
   transform: translate(10px, 10px);
   transition: transform 0.2s;
   }
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <path  class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
  <circle id="circle" cx="0" cy="0" r="10"/>
  <path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>

发布评论

评论列表(0)

  1. 暂无评论