【问题标题】:SVG progress bar with image带有图像的 SVG 进度条
【发布时间】:2021-02-19 07:56:54
【问题描述】:

我正在尝试使用 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);

})();

this is what I currently have this is what i'm trying to achieve

【问题讨论】:

    标签: javascript html jquery animation svg


    【解决方案1】:

    在这种情况下,使用stroke-dasharray 技巧具有优势。

    SVG 可以在路径末尾绘制marker。该标记可以是任何类型的图形,其语法就像&lt;symbol&gt; 的语法一样。标记的位置为defined by the path d attribute,不受虚线影响。

    一般的策略是计算路径的端点

    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 使用 45 的 rx,45 的 ry,不要旋转圆弧的轴。
    • ${large} 是重要的一点。它可以将小于 180° 的弧与大于 180° 的弧区分开来。一旦超过该值,标志必须从0 更改为1。但由于您从不期望超过 180° 的值,因此您不需要它。
    • 1 表示顺着路径的方向看,弧线应该画到左边。
    • ${x} ${y} 是以绝对 表示的最终坐标,而不是相对 坐标。

    &lt;marker&gt; 元素有许多必须考虑的属性:

    • 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)" 表示属性。这是设置标记的内容,并定义它将位于路径的end

    (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>

    【讨论】:

      【解决方案2】:

      我有一个形状非常相似的加载 SVG。你可以看到它在行动here。主横幅底部的绿色。

      我使用的是ProgressBar.js library。这很容易。由于我网站的设计,我只是专注于调整我的 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 中,我认为它应该会随之移动。

      【讨论】:

        【解决方案3】:

        只需使用一点 JavaScript,因为您还不能轻松地在 CSS 中做三角函数。

        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);
        
        })();
        

        【讨论】:

          【解决方案4】:

          要解决,需要结合两个动画:

          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>  

          【讨论】:

          • 这很好用,但我不得不将初始代码编辑为更循环(规范已更改),但凭借所提供的知识和示例,我在更新方面有了一个良好的开端。我喜欢它,因为它是纯 SVG,并帮助我更多地了解 SVG。谢谢!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-21
          • 2013-05-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多