基于SVG和Segment.js的Loading加载按钮特效

所属分类:其他-动画效果

 2593  46  查看评论 (2)
基于SVG和Segment.js的Loading加载按钮特效 ie兼容9

在这个特效中,为了制作线条动画,使用了Segment.js插件。Segment.js是一个可以制作SVG路径片段动画的js库。

要制作出这个特效,首先是要了解线条如何动画。所有的线条必须手动画出来。下面是第一个loading效果的SVG线条。

<svg width="120px" height="120px">
    <path class="outer-path" stroke="#fff" d="M 60 60 m 0 -50 a 50 50 0 1 1 0 100 a 50 50 0 1 1 0 -100"></path>
    <path class="inner-path" stroke="rgba(255, 255, 255, 0.5)" d="M 60 60 m 0 -30 a 30 30 0 1 1 0 60 a 30 30 0 1 1 0 -60"></path>
    <path class="success-path" stroke="#fff" d="M 60 10 A 50 50 0 0 1 91 21 L 75 45 L 55 75 L 45 65"></path>
    <path class="error-path" stroke="#fff" d="M 60 10 A 50 50 0 0 1 95 25 L 45 75"></path>
    <path class="error-path2" stroke="#fff" d="M 60 30 A 30 30 0 0 1 81 81 L 45 45"></path>
</svg>

为它添加一些CSS样式:

body {
	background:#354458;
}
svg path {
	stroke-linecap:round;
	stroke-linejoin:round;
	stroke-width:4;
	fill:none;
}

Segment.js 是创建这个loading动画的关键js代码。

var outer = document.querySelector('.outer-path'),
    inner = document.querySelector('.inner-path'),
    outerSegment = new Segment(outer, 0, 0.1),
    innerSegment = new Segment(inner, 0, 0.1);

function outerAnimation() {
    outerSegment.draw('15%', '25%', 0.2, {
        callback: function() {
            outerSegment.draw('75%', '150%', 0.3, {
                circular: true,
                callback: function() {
                    outerSegment.draw('70%', '75%', 0.3, {
                        circular: true,
                        callback: function() {
                            outerSegment.draw('100%', '100% + 0.1', 0.4, {
                                circular: true,
                                callback: function() {
                                    outerAnimation();
                                    innerAnimation();
                                }
                            });
                        }
                    });
                }
            });
        }
    });
}

function innerAnimation() {
    innerSegment.draw('20%', '80%', 0.6, {
        callback: function() {
            innerSegment.draw('100%', '100% + 0.1', 0.6, {
                circular: true
            });
        }
    });
}
outerAnimation();
innerAnimation();

通过上面的代码我们已经可以进行loading加载线条动画了。但是我们还没有处理loading成功和失败时的状态。另外如果想添加新的loader该如何做呢?最好的方法是创建一个通用的js库或插件来处理这些问题。下面就是这个通用库的js代码:

function LoadingButton(el, options) {
    this.el = el;
    this.options = options;
    this.init();
}
LoadingButton.prototype = {
    // Initialize everything    
    init: function() {
        this.infinite = true;
        this.succeed = false;
        this.initDOM();
        this.initSegments();
        this.initEvents();
    },
    // Create an span element with inner text of the button and insert the corresponding SVG beside it   
    initDOM: function() {
        this.el.innerHTML = '' + this.el.innerHTML + '';
        this.span = this.el.querySelector('span');
        var div = document.createElement('div');
        div.innerHTML = document.querySelector(this.options.svg).innerHTML;
        this.svg = div.querySelector('svg');
        this.el.appendChild(this.svg);
    },
    // Initialize the segments for all the paths of the loader itself, and for the success and error animations   
    initSegments: function() {
        for (var i = 0, paths = this.options.paths, len = paths.length; i < len; i++) {
            paths[i].el = this.svg.querySelector(paths[i].selector);
            paths[i].begin = paths[i].begin ? paths[i].begin : 0;
            paths[i].end = paths[i].end ? paths[i].end : 0.1;
            paths[i].segment = new Segment(paths[i].el, paths[i].begin, paths[i].end);
        }
        this.success = this.el.querySelector('.success-path');
        this.error = this.el.querySelector('.error-path');
        this.error2 = this.el.querySelector('.error-path2');
        this.successSegment = new Segment(this.success, 0, 0.1);
        this.errorSegment = new Segment(this.error, 0, 0.1);
        this.errorSegment2 = new Segment(this.error2, 0, 0.1);
    },
    // Initialize the click event in loading buttons, that trigger the animation   
    initEvents: function() {
        var self = this;
        self.el.addEventListener('click', function() {
            self.el.disabled = 'disabled';
            classie.add(self.el, 'open-loading');
            self.span.innerHTML = 'Sending';
            for (var i = 0, paths = self.options.paths, len = paths.length; i < len; i++) {
                paths[i].animation.call(self, paths[i].segment);
            }
        }, false);
    },
    // Make it fail    
    triggerFail: function() {
        this.infinite = false;
        this.succeed = false;
    },
    // Make it succeed  
    triggerSuccess: function() {
        this.infinite = false;
        this.succeed = true;
    },
    // When each animation cycle is completed, check whether any feedback has triggered and call the feedback    
    // handler, otherwise it restarts again    
    completed: function(reset) {
        if (this.infinite) {
            for (var i = 0, paths = this.options.paths, len = paths.length; i < len; i++) {
                if (reset) {
                    paths[i].segment.draw(0, 0.1);
                }
                paths[i].animation.call(this, paths[i].segment);
            }
        } else {
            this.handleResponse();
        }
    },
    // Handle the feedback request, and perform the success or error animation   
    handleResponse: function() {
        for (var i = 0, paths = this.options.paths, len = paths.length; i < len; i++) {
            paths[i].el.style.visibility = 'hidden';
        }
        if (this.succeed) {
            this.success.style.visibility = 'visible';
            this.successAnimation();
        } else {
            this.error.style.visibility = 'visible';
            this.error2.style.visibility = 'visible';
            this.errorAnimation();
        }
    },
    // Success animation   
    successAnimation: function() {
        var self = this;
        self.successSegment.draw('100% - 50', '100%', 0.4, {
            callback: function() {
                self.span.innerHTML = 'Succeed';
                classie.add(self.el, 'succeed');
                setTimeout(function() {
                    self.reset();
                }, 2000);
            }
        });
    },
    // Error animation  
    errorAnimation: function() {
        var self = this;
        self.errorSegment.draw('100% - 42.5', '100%', 0.4);
        self.errorSegment2.draw('100% - 42.5', '100%', 0.4, {
            callback: function() {
                self.span.innerHTML = 'Failed';
                classie.add(self.el, 'failed');
                setTimeout(function() {
                    self.reset();
                }, 2000);
            }
        });
    },
    // Reset the entire loading button to the initial state   
    reset: function() {
        this.el.removeAttribute('disabled');
        classie.remove(this.el, 'open-loading');
        this.span.innerHTML = 'Send';
        classie.remove(this.el, 'succeed');
        classie.remove(this.el, 'failed');
        this.resetSegments();
        this.infinite = true;
        for (var i = 0, paths = this.options.paths, len = paths.length; i < len; i++) {
            paths[i].el.style.visibility = 'visible';
        }
        this.success.style.visibility = 'hidden';
        this.error.style.visibility = 'hidden';
        this.error2.style.visibility = 'hidden';
    },
    // Reset the segments to the initial state   
    resetSegments: function() {
        for (var i = 0, paths = this.options.paths, len = paths.length; i < len; i++) {
            paths[i].segment.draw(paths[i].begin, paths[i].end);
        }
        this.successSegment.draw(0, 0.1);
        this.errorSegment.draw(0, 0.1);
        this.errorSegment2.draw(0, 0.1);
    }
};

当然还应该为动画添加一些必要的CSS样式,下面使用的是SCSS代码:

// Loading button
.loading-button{
  // When loading button is open
  &.open-loading{
    color: rgba(255, 255, 255, 0.8);
    &.infinity{
      padding-top: 80px;
    }
    svg{
      display: inline-block;
      visibility: visible;
      opacity: 1;
      transition: 1s opacity;
      transform: translateX(-50%);
    }
  }
  // Loading failed
  &.failed{
    background-color: #EB7260;
  }
  // Loading succeed
  &.succeed{
    background-color: #29ABA4;
  }
  // Remove transition when changing demo position
  &.no-transition{
    transition: 0s;
    *{
      transition: 0s;
    }
  }
  // SVG element, centered and hidden initially
  svg{
    visibility: hidden;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    opacity: 0;
    transition: 0s;
    path{
      stroke-linecap: round;
      stroke-linejoin: round;
      stroke-width: 4;
      fill: none;
      // To hide success and error paths
      &.success-path, &.error-path, &.error-path2{
        visibility: hidden;
      }
    }
  }
}

// Handle positions
.loading-button {
  &.top {
    svg{
      top: 10px;
    }
  }
  &.bottom {
    svg{
      bottom: 10px;
    }
  }
  &.left {
    svg {
      top: 50%;
      transform: scale(0.25) translateY(-50%);
      transform-origin: 0 0 0;
      left: 20px;
    }
  }
  &.right {
    svg {
      top: 50%;
      transform: scale(0.25) translateY(-50%);
      transform-origin: 100% 0 0;
      left: auto;
      right: 20px;
    }
  }
  &.open-loading {
    &.left {
      padding-left: 60px;
    }
    &.right {
      padding-right: 60px;
    }
    &.top, &.bottom {
      svg{
        transition-delay: 0.2s;
      }
    }
    &.circular-loading, &.circle-loading {
      &.top {
        padding-top: 140px;
      }
      &.bottom {
        padding-bottom: 140px;
      }
    }
    &.infinity-loading {
      &.top {
        padding-top: 80px;
      }
      &.bottom {
        padding-bottom: 80px;
      }
    }
  }
}

如果要使用新的SVG loading加载效果,可以像下面这样将SVG代码放置在一个模板中:

<script type="text/template" id="circle-loading">
	    <svg width="120px" height="120px">
	        <circle r="50" cx="60" cy="60" fill="none" stroke="rgba(255, 255, 255, 0.3)"></circle>
	        <circle r="30" cx="60" cy="60" fill="none" stroke="rgba(255, 255, 255, 0.3)"></circle>
	        <path class="outer-path" stroke="#fff" d="M 60 60 m 0 -50 a 50 50 0 1 1 0 100 a 50 50 0 1 1 0 -100"></path>
	        <path class="inner-path" stroke="#fff" d="M 60 60 m 0 -30 a 30 30 0 1 1 0 60 a 30 30 0 1 1 0 -60"></path>
	        <path class="success-path" stroke="#fff" d="M 60 10 A 50 50 0 0 0 16 36  L 45 65 L 55 75 L 75 45"></path>
	        <path class="error-path" stroke="#fff" d="M 60 10 A 50 50 0 0 0 25 95 L 75 45"></path>
	        <path class="error-path2" stroke="#fff" d="M 60 30 A 30 30 0 0 1 81 81 L 45 45"></path>
	    </svg>
	</script>

然后使用下面的代码来驱动新的loading动画。

function circularLoading() {
    var button = document.querySelector('.loading-button'),
        options = {
            svg: '#circular-loading',
            paths: [{
                selector: '.outer-path',
                animation: outerAnimation
            }, {
                selector: '.inner-path',
                animation: innerAnimation
            }]
        },
        loading = new LoadingButton(button, options);

    function outerAnimation(segment) {
        var self = this;
        segment.draw('15%', '25%', 0.2, {
            callback: function() {
                segment.draw('75%', '150%', 0.3, {
                    circular: true,
                    callback: function() {
                        segment.draw('70%', '75%', 0.3, {
                            circular: true,
                            callback: function() {
                                segment.draw('100%', '100% + 0.1', 0.4, {
                                    circular: true,
                                    callback: function() {
                                        self.completed(true);
                                    }
                                });
                            }
                        });
                    }
                });
            }
        });
    }

    function innerAnimation(segment) {
        segment.draw('20%', '80%', 0.6, {
            callback: function() {
                segment.draw('100%', '100% + 0.1', 0.6, {
                    circular: true
                });
            }
        });
    }
    return loading;
}
相关插件-动画效果

Google Doodle粘土动画

Google Doodle粘土动画
  动画效果
 8051  59

CSS3提交按钮动画特效

这个提交按钮效果有两组动画:当屏幕大于800px的时候是一个动画效果,当屏幕小于800像素的时候是另一种动画效果。缩放你的浏览器看看不同的提交按钮动画效果吧!
  动画效果
 12449  89

html5 水波扩散效果

HTML5实现点击水波扩散效果
  动画效果
 13898  133

SVG文字变形动画特效

这是一款使用效果非常炫酷的HTML5 SVG文字变形动画特效。该特效使用SVG和anime.js,通过SVG描边动画来完成各种漂亮的字母动画特效。
  动画效果
 9884  118

讨论这个项目(2)回答他人问题或分享插件使用方法奖励jQ币

    a'╃ ?? 0
    2017/8/29 14:51:28
    `莫欺少年穷 0
    2017/8/29 10:17:38
取消回复