天天看點

svg動畫實作彈性側欄菜單

前端開發whqet,csdn,王海慶,whqet,前端開發專家

效果預覽

衆所周知,flash動畫類型可以分為補間動畫和逐幀動畫,補間動畫又可以分為屬性改變(大小、位置、顔色等)和形狀改變(直線變弧線等)。網頁動畫裡,我們可以使用css3、javascript(jquery)等實作屬性改變,卻對形狀改變無能為力,那麼如何實作網頁動畫裡的形狀改變呢?今天提供一種解決方案——通過snap.svg動态改變svg形狀實作,案例效果如下圖所示,案例靈感來自codrops。

svg動畫實作彈性側欄菜單

----------------

----------------------------------------

線上研究點選這裡,下載下傳收藏點選這裡。

----------------------------------------

---------------

關鍵技術

制作這個案例,你需要

1. snap.svg的基本使用

2. classie.js的基本使用

3. snap.svg操作改變svg的内容

4. 沖動與激情

實作步驟

ok,我們html内容如下,.menu為整個容器,.menu__handle為單擊按鈕,.menu__inner為彈出菜單内容,.morph-shape為形狀改變的線條。我們把需要改變的線條的path資料實作放到data-morph-open和data-morph-close兩個屬性裡。

<div class="container">
<nav id="menu" class="menu">
	<button class="menu__handle"><span>Menu</span></button>
	<div class="menu__inner">
		<ul>
			<li><a href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >Home</a></li>
			<li><a href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >Favs</a></li>
			<li><a href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >Files</a></li>
			<li><a href="#" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >Stats</li>
		</ul>
	</div>
	<div class="morph-shape" data-morph-open="M300-10c0,0,295,164,295,410c0,232-295,410-295,410" data-morph-close="M300-10C300-10,5,154,5,400c0,232,295,410,295,410">
		<svg width="100%" height="100%" viewBox="0 0 600 800" preserveAspectRatio="none">
			<path fill="none" d="M300-10c0,0,0,164,0,410c0,232,0,410,0,410"/>
		</svg>
	</div>
</nav>
</div>
           

然後是css,css裡面我們實作基本布局與css3動畫。css裡面我們使用了Normalize和prefixfree。按鈕(三條線變成x)由.menu__handle的before、after兩個僞對象和span實作。

/*統一化設定*/
*, *:after, *:before { box-sizing: border-box; }
.clearfix:before, .clearfix:after { content: ''; display: table; }
.clearfix:after { clear: both; }

body {
	color: #fff;
	background: #FF7F50;
	font-weight: 400;
	font-size: 1em;
}

a {
	color: #fff;
	text-decoration: none;
}

a:hover,
a:focus {
	color: #393;
	outline: none;
}

.container {
	text-align: center;
	padding: 5.25em 0.5em 0;
}
/*菜單設定*/
.menu {
	position: fixed;
	width: 240px;
   top: 2px;
	bottom: 2px;
	left: 0;
	z-index: 100;
	overflow: hidden;
	transform: translate3d(-150px, 0, 0);
	transition: transform 0.6s;
}

.menu.menu--open {
	transform: translate3d(0, 0, 0);
}

.menu__inner {
	width: calc(100% + 15px);
	padding: 0 100px 2em 0;
	overflow-y: auto;
	height: 100%;
	position: relative;
	z-index: 100;
}

.menu ul {
	list-style: none;
	padding: 0;
	margin: 0;
}

.menu ul li {
	transform: translate3d(-150px, 50px, 0);
	transition: transform 0.6s;
   height:24px;
   line-height:24px;
}

.menu.menu--anim ul li {
	transform: translate3d(0, 0, 0);
}

.menu ul li:first-child {
	transition-delay: 0.3s;
}

.menu ul li:nth-child(2) {
	transition-delay: 0.2s;
}

.menu ul li:nth-child(3) {
	transition-delay: 0.1s;
}

.menu ul li a {
	display: block;
	outline: none;
}
/*按鈕及單擊之後變換*/
.menu__handle {
	background: #FF7F50;
	border: none;
	width: 30px;
	height: 24px;
	padding: 0;
	outline: none;
	position: absolute;
	top: 3px;
	right: 45px;
	z-index: 2000;
}

.menu__handle::before,
.menu__handle::after,
.menu__handle span {
	background: #fff;
}

.menu__handle::before,
.menu__handle::after {
	content: '';
	position: absolute;
	height: 2px;
	width: 100%;
	left: 0;
	top: 50%;
	transform-origin: 50% 50%;
	transition: transform 0.25s;
}

.menu__handle span {
	position: absolute;
	width: 100%;
	height: 2px;
	left: 0;
	overflow: hidden;
	text-indent: 200%;
	transition: opacity 0.25s;
}

.menu__handle::before {
	transform: translate3d(0, -10px, 0);
}

.menu__handle::after {
	transform: translate3d(0, 10px, 0);
}

.menu--open .menu__handle span {
	opacity: 0;
}

.menu--open .menu__handle::before {
	transform: rotate3d(0, 0, 1, 45deg);
}

.menu--open .menu__handle::after {
	transform: rotate3d(0, 0, 1, -45deg);
}
/* 線條及改變 */
.morph-shape {
	position: absolute;
	width: 160px;
	height: 100%;
	top: 0;
	right: 0px;
}

.morph-shape svg path {
	stroke: #fff;
	stroke-width: 2px;
}
.menu--open .morph-shape svg path {
	stroke: #fff;
	stroke-width: 4px;
}
           

然後是javascript,也是我們的核心。這裡用到了classie.js和snap.svg(可以參考張鑫旭大俠翻譯的snap.svg中文版)。

function SVGMenu( el, options ) {
	this.el = el;
	this.init();
}

SVGMenu.prototype.init = function() {
	this.trigger = this.el.querySelector( 'button.menu__handle' );
	this.shapeEl = this.el.querySelector( 'div.morph-shape' );

	var s = Snap( this.shapeEl.querySelector( 'svg' ) );
	this.pathEl = s.select( 'path' );
	this.paths = {
		reset : this.pathEl.attr( 'd' ),
		open : this.shapeEl.getAttribute( 'data-morph-open' ),
		close : this.shapeEl.getAttribute( 'data-morph-close' )
	};

	this.isOpen = false;

	this.trigger.addEventListener( 'click', this.toggle.bind(this) );
};


SVGMenu.prototype.toggle = function() {
	var self = this;

	if( this.isOpen ) {
		classie.remove( self.el, 'menu--anim' );
		setTimeout( function() { classie.remove( self.el, 'menu--open' );	}, 250 );
	}
	else {
		classie.add( self.el, 'menu--anim' );
		setTimeout( function() { classie.add( self.el, 'menu--open' );	}, 250 );
	}
	self.pathEl.stop().animate( { 'path' : self.isOpen ? self.paths.close : this.paths.open }, 350, mina.easeout, function() {
		self.pathEl.stop().animate( { 'path' : self.paths.reset }, 800, mina.elastic );
	} );
	
	self.isOpen = !self.isOpen;
};

new SVGMenu( document.getElementById( 'menu' ) );
           

ok,請大家仔細揣摩。當然,這個方面也出了一些插件可以使用例如,SVG Morpheus,也可看看《SVG-Morpheus實作SVG圖示圖形間的補形動畫》這篇教程。

----------------------------------------------------------

前端開發whqet,關注web前端開發,分享相關資源,歡迎點贊,歡迎拍磚。

---------------------------------------------------------------------------------------------------------

繼續閱讀