提交按钮动画效果(基于SVGi)

2 篇文章 0 订阅
1 篇文章 0 订阅

翻译整理一篇外文前端效果,基于SVG的按钮动画。

效果:

1、hover:按钮颜色过渡;
2、click:按钮转换为圆环,开始环形描边动画;
3、完成环形描边动画,显示成功或错误状态 (对勾和差);
4、恢复初始按钮状态

虽然存在其他可选方案,但本文将主要通过基于SVG和CSS transition技术实现这一效果。基于SVG的动画可能不会被所有浏览器支持,可视本文为前端实验性案例。

按钮状态:


Html代码段:

<div id="progress-button" class="progress-button">
	<!-- button元素,显示button初始状态-->
	<button><span>Submit</span></button>
	<!-- svg 圆形图案,通过path元素进行描边动画 -->
	<svg class="progress-circle" width="70" height="70">
		<path d="m35,2.5c17.955803,0 32.5,14.544199 32.5,32.5c0,17.955803 -14.544197,32.5 -32.5,32.5c-17.955803,0 -32.5,-14.544197 -32.5,-32.5c0,-17.955801 14.544197,-32.5 32.5,-32.5z"/>
	</svg>

	<!-- 提交成功的显示状态 -->
	<svg class="checkmark" width="70" height="70">
		<path d="m31.5,46.5l15.3,-23.2"/>
		<path d="m31.5,46.5l-8.5,-7.1"/>
	</svg>

	<!-- 提交失败的显示状态-->
  <svg class="cross" width="70" height="70">
		<path d="m35,35l-9.3,-9.3"/>
		<path d="m35,35l9.3,9.3"/>
		<path d="m35,35l-9.3,9.3"/>
		<path d="m35,35l9.3,-9.3"/>
  </svg>
</div>


CSS代码段:


.progress-button { 
 	position: relative; 
        display: inline-block; 
	text-align: center;
	width: 45%;
	min-width: 250px;
	margin: 10px;}

.progress-button button {
    display: block;
    margin: 0 auto;
    padding: 0;
    width: 250px;
    height: 70px;
    border: 2px solid #1ECD97;
    border-radius: 35px;
    background: transparent;
    color: #1ECD97;
    letter-spacing: 1px;
    font-size: 18px;
    font-family: 'Montserrat', sans-serif;
    /* button元素过渡效果应用属性:背景色,前景色,宽度,边框宽,边框颜色 */
    -webkit-transition: background-color 0.3s, color 0.3s, width 0.3s, border-width 0.3s, border-color 0.3s;
    -moz-transition: background-color 0.3s, color 0.3s, width 0.3s, border-width 0.3s, border-color 0.3s;
    -o-transition: background-color 0.3s, color 0.3s, width 0.3s, border-width 0.3s, border-color 0.3s;
    transition: background-color 0.3s, color 0.3s, width 0.3s, border-width 0.3s, border-color 0.3s;}

.progress-button button:hover { /*鼠标移入改变背景色和前景色,实现淡入淡出效果*/
     background-color: #1ECD97;
     color: #fff;
     cursor:pointer;
    }

.progress-button svg {
    position: absolute;
    top: 0;
    left: 50%;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    pointer-events: none;
}
.progress-button svg path {/*隐藏svg path元素*/    
    opacity:0;
    fill:none;
}

.progress-button svg.progress-circle path {
    stroke: #1ECD97;
    stroke-width: 5;
}

.progress-button svg.checkmark path, .progress-button svg.cross path {
    stroke:#fff;
    stroke-linecap:round;
    stroke-width:4px;
    -webkit-transition:opacity 0.1s;
    transition:opacity 0.1s;
    }

.loading.progress-button button {
    width: 70px; /* make a circle */
    border-width: 5px;
    border-color: #ddd;
    background-color: transparent;
    color: #fff;
}

.loading.progress-button span {
    -webkit-transition: opacity 0.15s;
    transition: opacity 0.15s;
}

.loading.progress-button span,
.success.progress-button span,
.error.progress-button span {
    opacity: 0; /*在整个动画过程中,隐藏button span元素*/
}

.progress-button button span {
    -webkit-transition: opacity 0.3s 0.1s;
    transition: opacity 0.3s 0.1s;
}

.success.progress-button button,
.error.progress-button button {
    -webkit-transition: background-color 0.3s, width 0.3s, border-width 0.3s;
    transition: background-color 0.3s, width 0.3s, border-width 0.3s;
}

.loading.progress-button svg.progress-circle path,
.success.progress-button svg.checkmark path,
.error.progress-button svg.cross path {
    opacity: 1;
    -webkit-transition: stroke-dashoffset 0.3s;/*改变svg path元素的stroke-dashoffeset的属性实现动画*/
    transition: stroke-dashoffset 0.3s;
}


.success.progress-button button {
    border-color: #1ECD97;
    background-color: #1ECD97;
}
 
.error.progress-button button {
    border-color: #FB797E;
    background-color: #FB797E;
}

.elastic.progress-button button {
    -webkit-transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.25, 0.25, 0.4, 1.6), border-width 0.3s, border-color 0.3s;
    -webkit-transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.25, 0.25, 0.4, 1.6), border-width 0.3s, border-color 0.3s;
    transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.25, 0.25, 0.4, 1.6), border-width 0.3s, border-color 0.3s;
}
 
.loading.elastic.progress-button button {
    -webkit-transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.6, 0, 0.75, 0.75), border-width 0.3s, border-color 0.3s;
    -webkit-transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.6, -0.6, 0.75, 0.75), border-width 0.3s, border-color 0.3s;
    transition: background-color 0.3s, color 0.3s, width 0.3s cubic-bezier(0.6, -0.6, 0.75, 0.75), border-width 0.3s, border-color 0.3s;
}

javascript代码段:

依赖库:Modernizr.js(自定义下载,主要用于判断浏览器对transition的支持);classie.js(判断元素应用了样式,为元素添加、删除样式,或进行样式切换)。

html内嵌javascript代码:

<script>
  [].slice.call(document.querySelectorAll('.progress-button')).forEach(function(btn,pos){
	  new ProgressButton(btn,{
		  callback:function(instance){
			 var progress=0;
			 var interval=setInterval(function(){
				 progress=Math.min(progress+Math.random()*0.1,1);
				 instance.setProgress(progress);
				 if(progress===1){
					 instance.stop(pos===1 || pos ===3?-1:1);
					 clearInterval(interval);
					 }
				 
				 },150);
			  }
		  });
	  });
  
  </script>


外部js:

(function(window){
	var transEndEventNames={'WebkitTransition': 'webkitTransitionEnd','MozTransition': 'transitionend','OTransition': 'oTransitionEnd','msTransition': 'MSTransitionEnd','transition': 'transitionend'};
	var transEndEventName=transEndEventNames[Modernizr.prefixed( 'transition' ) ];
	var support={transitions:Modernizr.csstransitions};
	
	function extend( a, b ) {
		for( var key in b ) { 
			if( b.hasOwnProperty( key ) ) {
				a[key] = b[key];
			}
		}
		return a;
	}
	
	function SVGEL(el){//SVG 元素的构造函数
		//alert(el);
		this.el = el;
		this.paths = [].slice.call( this.el.querySelectorAll( 'path' ) );
		this.pathsArr = new Array();
		this.lengthsArr = new Array();
		this._init();
		}
		
	SVGEL.prototype._init=function(){
		var self = this;
		this.paths.forEach( function( path, i ) {
			self.pathsArr[i] = path;
			path.style.strokeDasharray = self.lengthsArr[i] = path.getTotalLength();
		} );
		this.draw(0);		
		};
		
	SVGEL.prototype.draw=function(value){
		for( var i = 0, len = this.pathsArr.length; i < len; ++i ){
		this.pathsArr[i].style.strokeDashoffset = this.lengthsArr[i] * ( 1 - value );
		}
};
	
	
	function ProgressButton(element,options){//ProgressButton类的构造函数入口
		this.element=element;
		this.options = extend( {}, this.options );
		extend( this.options, options );
		this._init();
		}
		
	ProgressButton.prototype._init=function(){//ProgessButton类的_init实例方法,用于初始化。
		this.button = this.element.querySelector( 'button' );
		this.progressElement = new SVGEL( this.element.querySelector( 'svg.progress-circle' ) );
		this.successElement= new SVGEL( this.element.querySelector( 'svg.checkmark' ) );
		this.errorElement = new SVGEL( this.element.querySelector( 'svg.cross' ) );
		this._initEvents();
		this._enable();
		
		};
	ProgressButton.prototype.options={statusTime : 1500};
		
	ProgressButton.prototype._initEvents=function(){
		var self=this;
		this.button.addEventListener("click",function(){ 
			self._submit();
			});
		};
		
	ProgressButton.prototype._submit=function(){
		classie.addClass( this.element, 'loading' );
		var self=this;
		var onEndBtnTransitionFn=function(event){
			if(support.transitions){
				if(event.propertyName!=='width'){return false;}
				this.removeEventListener(transEndEventName,onEndBtnTransitionFn);
				}
				this.setAttribute('disabled','');
				if(typeof self.options.callback==='function'){
					self.options.callback(self);
					}else{
					self.setProgress(1);
					self.stop();
						}
			};
		if(support.transitions){
			this.button.addEventListener(transEndEventName,onEndBtnTransitionFn);
			}else{
				onEndBtnTransitionFn();
			}
		};
	ProgressButton.prototype.stop=function(status){
		var self=this;
		var endloading=function(){
			self.progressElement.draw(0);
			if(typeof status==='number'){
				var statusClass=status>=0?'success':'error';
				var statusElement=status>=0?self.successElement:self.errorElement;
				statusElement.draw(1);
				classie.addClass( self.element, statusClass );
				setTimeout(function(){
					classie.removeClass(self.element,statusClass);
					statusElement.draw(0);
					self._enable();
					},self.options.statusTime);
				}else{
				self._enable();
					}
				classie.removeClass(self.element,'loading');
			};
			setTimeout(endloading,300);
		};	
		
	ProgressButton.prototype.setProgress=function(val){
		this.progressElement.draw(val);
		};
			
	ProgressButton.prototype._enable=function(){
		this.button.removeAttribute('disabled');
		};
	
	window.ProgressButton=ProgressButton;//将ProgressButton类附加到window对象上,便于外部调用
})(window);


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值