SVG进度条与图像

3

我正在尝试使用SVG创建进度条(弧形)。目前,进度条已经可以根据数据属性存储的值移动到期望的位置,并且看起来非常好。但是我希望能够在弧形上面移动一个图像和进度条一起更新。这个图像应该从0开始,随着进度条的完成点,例如50%,向上移动。

<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
<svg class='progress_bar' viewBox="0 0 100 50" >
 <g fill-opacity="0" stroke-width="4">
  <path d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
  <path class="progress" d="M5 50a45 45 0 1 1 90 0" stroke="#f00" stroke-dasharray="142" stroke-dashoffset="142"></path>
 </g>
 <circle fill="url(#image)" id='case_progress__prog_fill' class="case_progress__prog" cx="0" cy="0" r="8" fill="#999" stroke="#fff" stroke-width="1" />
 <defs>
  <pattern id="image" x="0%" y="0%" height="100%" width="100%" viewBox="0 0 60 60">
   <image x="0%" y="0%" width="60" height="60" xlink:href="https://via.placeholder.com/150x150"></image>
  </pattern>
 </defs>
</svg>
</div>

(function(){

    var $wrapper = $('.case-progress-bar'),
        $bar = $wrapper.find('.progress_bar'),
        $progress = $bar.find('.progress'),
        $percentage = $wrapper.data('percentage');

    $progress.css({
        'stroke-dashoffset': 'calc(142 - (0 * 142 / 100))',
        'transition': 'all 1s'
    });

    var to = setTimeout(function(){
        $progress.css('stroke-dashoffset', 'calc(142 - (' + $percentage + ' * 142 / 100))');
        clearTimeout(to);
    }, 500);

})();

这是我目前拥有的 这是我想要实现的

4个回答

3

为了解决问题,您需要结合两种动画:

  1. 从开始到中间(顶部)绘制一半弧形
  2. 带有图像的圆形移动动画

将两种动画的时间设置为相同。

<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
<svg class='progress_bar' viewBox="0 0 100 50" >

 <g fill-opacity="0" stroke-width="4">
  <path id="pfad"  d="M5 50C5 44.1 6.1 38.5 8.2 33.4 10.8 26.8 14.9 20.9 20.2 16.3 28.1 9.3 38.6 5 50 5" stroke="#EBEDF8"></path> 
  
   <path   d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
  
    <!-- Animation to fill half an arc -->
  <path class="progress" d="M5 50a45 45 0 1 1 90 0" stroke="#f00" stroke-dasharray="142" stroke-dashoffset="142">
    <animate attributeName="stroke-dashoffset" from="142" to="71" dur="4s" fill="freeze" />
  </path>
 </g>
 
 <defs>
  <pattern id="image" x="0%" y="0%" height="100%" width="100%" viewBox="0 0 60 60">
   <image x="0%" y="0%" width="60" height="60"  xlink:href="https://via.placeholder.com/150x150"></image>
  </pattern>
 </defs>

  
   <circle fill="url(#image)" id='case_progress__prog_fill' class="case_progress__prog" cx="0" cy="0" r="8" fill="#999" stroke="#fff" stroke-width="1" >
     <!-- Animation of movement of a circle with an image    -->
   <animateMotion begin="0s" dur="4s" fill="freeze"> 
    <mpath xlink:href="#pfad" />
</animateMotion>
   </circle>

</svg>
</div>  


1
这个很好地解决了问题,但是由于规格变更,我不得不编辑初始代码使其更加圆滑。但是有了提供的知识和示例,让我在更新时有了一个良好的起点。我喜欢这个因为它是纯SVG,并帮助我更好地理解SVG。谢谢! - Bryan88

2
这是一个例子,其中不使用stroke-dasharray技巧有优势。
SVG可以在路径末尾绘制标记。该标记可以是任何类型的图形,其语法与<symbol>相同。标记的位置由路径d属性定义,并且不受虚线笔画的影响。
一般策略是计算路径的端点。
endpoint_x = center_x - cos(percentage / 100 * 180°) * radius
endpoint_y = center_y - sin(percentage / 100 * 180°) * radius

由于你决定只使用半圆来表示100%,所以可以相对无缝地完成。为了实现这个目标,我已经改变了路径数据的书写方式:
`M5 50 A 45 45 0 ${large} 1 ${x} ${y}`
  • A 的意思是:绘制一个弧线,并使用绝对坐标。
  • 45 45 0 使用 rx 和 ry 都为 45,不旋转弧线的轴。
  • ${large} 是重要的部分。它区分小于 180° 的弧和大于 180° 的弧。一旦超过该值,标志必须从 0 更改为 1。但由于您永远不会期望超过 180° 的值,因此您不需要它。
  • 1 表示在路径方向上看,应向左侧绘制弧线。
  • ${x} ${y} 是最终坐标,以 绝对 而不是 相对 坐标表示。

<marker> 元素有许多属性必须考虑:

  • orient="0" 表示标记不会随着路径在其末端的方向而改变其方向。 orient="auto" 会使其转向,就像您希望看到的箭头一样。
  • markerUnits="userSpaceOnUse" 表示其他属性中的数字以路径的坐标系的 px 单位表示。默认值为 markerUnits="strokeWidth",这意味着相对于描边的宽度的大小。
  • viewBox="-8 -8 16 16" 是因为使用的圆是以坐标系原点为中心的。
  • markerWidth="16" markerHeight="16" 表示应绘制标记的大小。
  • refX="0" refY="-10" 描述了标记的定位方式:在标记本身的坐标系中取一个点(略高于其最高点并且在中间),并将其与路径的末端完全对齐。

最后,请注意路径的 marker-end="url(#image)" 演示属性。这就是设置标记的方式,并定义它将在路径的末端

(function(){

    var $wrapper = $('.case-progress-bar'),
        $bar = $wrapper.find('.progress_bar'),
        $progress = $bar.find('.progress'),
        $percentage = $wrapper.data('percentage');

   function computePath (percentage) {
       var x = 50 - Math.cos(percentage / 100 * Math.PI) * 45,
           y = 50 - Math.sin(percentage / 100 * Math.PI) * 45,
           large = percentage > 100 ? 1 : 0;
           
       return `M5 50 A 45 45 0 ${large} 1 ${x} ${y}`;
   }
    

    var to = setTimeout(function(){
        $progress.attr('d', computePath($percentage));
        clearTimeout(to);
    }, 500);

})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
  <svg class='progress_bar' viewBox="0 0 100 70" >
    <g fill-opacity="0" stroke-width="4">
      <path d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
      <path class="progress" d="M5 50 A 45 45 0 0 1 95 50`" stroke="#f00"
            marker-end="url(#image)"></path>
    </g>
    <marker id="image" orient="0" markerUnits="userSpaceOnUse"
            viewBox="-8 -8 16 16"
            markerWidth="16" markerHeight="16" 
            refX="0" refY="-10">
       <circle r="8" fill="#aaf" />
       <path d="M-6 0h12" stroke="#000" stroke-width="2" />
    </marker>
  </svg>
</div>


1

由于在CSS中难以轻松进行三角函数运算,因此只需使用少量JavaScript即可。

Codepen: https://codepen.io/mullany/pen/02cd0773588b3d975c8443ab6a87f670

(function(){

    var $wrapper = $('.case-progress-bar'),
        $bar = $wrapper.find('.progress_bar'),
        $progress = $bar.find('.progress'),
        $percentage = $wrapper.data('percentage');
   var  $circpos = $('.case_progress__prog');

    $progress.css({
        'stroke-dashoffset': 'calc(142 - (0 * 142 / 100))',
        'transition': 'all 1s'
    });

    var to = setTimeout(function(){
        $progress.css('stroke-dashoffset', 'calc(142 - (' + $percentage + ' * 142 / 100))');
        var angleInRadians = 180*(1-$percentage/100) * 0.01745329251;

        var xPos = 5 + 45 * (1 + Math.cos(angleInRadians) ); 
        var yPos = 5 + 45 * (1 - Math.sin(angleInRadians) );        


        $circpos.css('cx', xPos);
        $circpos.css('cy', yPos);
        clearTimeout(to);


    }, 500);

})();

0

我有一个非常类似形状的加载SVG。你可以在这里看到它的效果。它是主横幅底部的绿色图标。

我使用的是ProgressBar.js库。它让事情变得非常简单。我只需要专注于根据我的网站设计调整SVG的形状,然后使用以下代码:

<svg id="progress-bar" xmlns="http://www.w3.org/2000/svg" width="650" height="526" viewBox="0 0 650 526">
   <path opacity=".55" id="progress-bar-base" fill="none" stroke="#fefefe" stroke-width="20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M84.591 512.622S20 441.232 20 324.941 79.903 131.327 137.4 84.476 269.116 20 324.94 20s124.217 15.743 184.57 62.183 89.159 101.578 103.475 142.419c14.316 40.84 25.203 105.21 8.788 170.477-16.415 65.268-43.628 101.409-56.482 117.543"></path>
   <linearGradient id="grade" gradientUnits="userSpaceOnUse" x1="592.08" y1="157.665" x2="46.149" y2="472.859">
       <stop offset="0" stop-color="#2cab9a"></stop>
       <stop offset="1" stop-color="#8ed1c3"></stop>
   </linearGradient>
   <path fill-opacity="0" id="progress-bar-indicator" stroke="url(#grade)" stroke-width="24" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M84.591 512.622S20 441.232 20 324.941 79.903 131.327 137.4 84.476 269.116 20 324.94 20s124.217 15.743 184.57 62.183 89.159 101.578 103.475 142.419c14.316 40.84 25.203 105.21 8.788 170.477-16.415 65.268-43.628 101.409-56.482 117.543" style="stroke-dasharray: 1362.73, 1362.73; stroke-dashoffset: 1140.45;"></path>
</svg>

var progressBar = new ProgressBar.Path('#progress-bar-indicator', {
    easing: 'easeInOut',
    duration: 2500
});
progressBar.set(0); // Initiate it at zero.
progressBar.animate(0.33); // this is your percentage of load

这只是一个非常基础的例子,但我认为你可以根据自己的需求进行调整。

至于图片,也许你可以将其添加到自己的SVG中,在加载曲线的顶端,并且它应该会随之移动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接