最近看到了AngularJS-material的網(wǎng)站,看到里面有一個點擊出現(xiàn)水紋的效果考蕾,非常漂亮』嵯埽可以看到點擊上面的元素的時候肖卧,元素會出現(xiàn)水波蕩漾的效果,我想了一下實現(xiàn)原理掸鹅,主要借助CSS3的<code>transform:scale()</code>來實現(xiàn)的塞帐。<p>如下圖
首先介紹一下原理拦赠,第一步是在點擊元素中動態(tài)添加一個<code>span</code>標(biāo)簽,然后為這個標(biāo)簽設(shè)置初始類名和相對于父節(jié)點的位置葵姥。這里需要有兩點注意:(1)初始類名荷鼠,要讓添加<code>span</code>標(biāo)簽縮放為0,代碼如下:
-webkit-transform: scale(0);
-moz-transform: scale(0);
-ms-transform: scale(0);
-o-transform: scale(0);
transform: scale(0)
(2)要通過監(jiān)聽事件來實現(xiàn)動態(tài)改變添加<code>span</code>標(biāo)簽的位置牌里,讓其永遠位于鼠標(biāo)點擊的中點颊咬。代碼如下
x=event.pageX-this.offsetLeft-CSS(span,'width')/2;
y=event.pageY-this.offsetTop-CSS(span,'height')/2;
第二步是每次在動態(tài)添加這個<code>span</code>標(biāo)簽的時候,都要把上一個添加的刪除牡辽,讓其新添加的時候,可以繼續(xù)添加類名敞临。<p>
第三步是通過CSS3的方式實現(xiàn)水紋的效果态辛,代碼如下:
.animate{
-webkit-animation: ripple .5s linear;
-moz-animation: ripple .5s linear;
-ms-animation: ripple .5s linear;
-o-animation: ripple .5s linear;
animation: ripple .5s linear
}
@-webkit-keyframes ripple {
100% {
opacity: 0;
-webkit-transform: scale(1.5)
}
}
@-moz-keyframes ripple {
100% {
opacity: 0;
-moz-transform: scale(0.5)
}
}
@-o-keyframes ripple {
100% {
opacity: 0;
-o-transform: scale(0.5)
}
}
@keyframes ripple {
100% {
opacity: 0;
transform: scale(0.5)
}
}
下面是兩種實現(xiàn)方式,第一種是原生js實現(xiàn)的挺尿,第二種是通過angularJS實現(xiàn)的奏黑。下面是效果
原生JS實現(xiàn)代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<style>
#content{
width: 200px;
height: 50px;
border: 1px solid gray;
background: slateblue;
position: absolute;
left: 123px;
top: 321px;
overflow: hidden;
}
.animate{
-webkit-animation: ripple .5s linear;
-moz-animation: ripple .5s linear;
-ms-animation: ripple .5s linear;
-o-animation: ripple .5s linear;
animation: ripple .5s linear
}
#content #spanStyle{
border-radius: 75px;
position: absolute;
background: salmon;
-webkit-transform: scale(0);
-moz-transform: scale(0);
-ms-transform: scale(0);
-o-transform: scale(0);
transform: scale(0)
}
@-webkit-keyframes ripple {
100% {
opacity: 0;
-webkit-transform: scale(1.5)
}
}
@-moz-keyframes ripple {
100% {
opacity: 0;
-moz-transform: scale(0.5)
}
}
@-o-keyframes ripple {
100% {
opacity: 0;
-o-transform: scale(0.5)
}
}
@keyframes ripple {
100% {
opacity: 0;
transform: scale(0.5)
}
}
</style>
<body>
<div id="content">
</div>
</body>
<script>
window.onload= function () {
var content=document.getElementById("content");
var eventHandle= function (obj,type,fn) {
if(obj.addEventListener){
obj.addEventListener(type,fn,false)
}else if(obj.attachEvent){
obj.attachEvent('on'+type,fn)
}
};
var setCSS3=function(obj,attr,value){
var sTr="";
var sVal="";
var arr=["Webkit","Moz","O","ms",""];
if(! obj["$Transform"])
{
obj["$Transform"]={};
}
obj["$Transform"][attr]=parseInt(value);
for( sTr in obj["$Transform"])
{
switch(sTr)
{
case 'scale':
case 'scaleX':
case 'scaleY':
sVal+=sTr+"("+(obj["$Transform"][sTr]/100)+") ";
break;
case 'rotate':
case 'rotateX':
case 'rotateY':
sVal+=sTr+"("+(obj["$Transform"][sTr])+"deg) ";
break;
case 'translateX':
case 'translateY':
case 'translateZ':
sVal+=sTr+"("+(obj["$Transform"][sTr])+"px) ";
break;
}
}
for(var i=0;i<arr.length;i++)
{
obj.style[arr[i]+"Transform"]=sVal;
}
};
var CSS= function (obj,attr,value) {
if(arguments.length==2){//查看有有幾個參數(shù),如果有兩個參數(shù)就執(zhí)行编矾,此時為獲取css屬性值
if(attr=='scale'|| attr=='rotate'|| attr=='rotateX'||attr=='rotateY'||attr=='scaleX'||attr=='scaleY'||attr=='translateY'||attr=='translateX')//如果是css3中的屬性
{
if(! obj.$Transform)
{
obj.$Transform={};
}
switch(attr)
{
case 'scale':
case 'scaleX':
case 'scaleY':
return typeof(obj.$Transform[attr])=='number'?obj.$Transform[attr]:100;//判斷obj.$Transform[attr]是否屬于number類型熟史,如果是就返回相應(yīng)值
break;
default:
return obj.$Transform[attr]?obj.$Transform[attr]:0;
}
}//如果不是CSS3樣式
var sCur=obj.currentStyle?obj.currentStyle[attr]:document.defaultView.getComputedStyle(obj, false)[attr];//傳統(tǒng)css屬性返回
if(attr=='opacity'){
return Math.round((parseFloat(sCur)*100));
}
else{
return parseInt(sCur);
}
}
else if(arguments.length==3)//如果有3個參數(shù),此時為設(shè)置css屬性
{
switch(attr){ //如果為css3屬性窄俏,就用css3設(shè)置函數(shù)蹂匹。
case 'scale':
case 'scaleX':
case 'scaleY':
case 'rotate':
case 'rotateX':
case 'rotateY':
case 'translateZ':
case 'translateX':
case 'translateY':
setCss3(obj, attr, value);
break;
case 'width':
case 'height':
case 'paddingLeft':
case 'paddingTop':
case 'paddingRight':
case 'paddingBottom':
value=Math.max(value,0);
case 'left':
case 'top':
case 'marginLeft':
case 'marginTop':
case 'marginRight':
case 'marginBottom':
if(typeof value=="string")
{
obj.style[attr]=value;
}
else
{
obj.style[attr]=value+'px';
}
break;
case 'opacity':
obj.style.filter="alpha(opacity:"+value+")";
obj.style.opacity=value/100;
break;
default:
obj.style[attr]=value;
}
}
return function (attr_in, value_in){css(obj, attr_in, value_in)};
};
var hasClassName=function(obj,className){
var isSpourtClassList='classList' in document.documentElement;
if(isSpourtClassList){
return obj.classList.contains(className)
}else{
var isContain=obj.className.indexOf(className)>-1;
return isContain;
}
};
var addClass= function (obj,className) {
var isSpourtClassList='classList' in document.documentElement;
if(isSpourtClassList){
if(hasClassName(obj,className)!=true){
obj.classList.add(className)
}
}else{
if(hasClassName(obj,className)!=true){
obj.className+=obj.className
}
}
};
var removeClass= function (obj, className) {
var isSpourtClassList='classList' in document.documentElement;
if(isSpourtClassList){
if(hasClassName(obj,className)){
obj.classList.remove(className)
}
}else{
if(hasClassName(obj,className)){
obj.className-=obj.className
}
}
}
var animate= function (target) {
this.obj=target;
};
animate.prototype={
addEffect: function () {
var that=this;
var span=document.createElement("span");
span.setAttribute("id","spanStyle");
//var spanRemove=that.obj.
eventHandle(that.obj,'click', function (e) {
var event=e ||window.event;
var x;
var y;
var spanS=document.getElementById("spanStyle");
if(spanS){
that.obj.removeChild(spanS);
}
that.obj.appendChild(span);
CSS(span,"width",150);
CSS(span,"height",150);
x=event.pageX-this.offsetLeft-CSS(span,'width')/2;
y=event.pageY-this.offsetTop-CSS(span,'height')/2;
CSS(span,"left",x);
CSS(span,"top",y);
addClass(span,"animate");
},false)
},
removeEffect: function () {
}
};
state.changeFront(new animate(content));//調(diào)用一次只產(chǎn)生一個span
};
var state= function () {//狀態(tài)控制函數(shù)
var currentState=null;
return{
changeFront: function (handle) {
currentState=handle;
currentState.addEffect();
},
changeBack: function (handle) {
currentState=handle;
currentState.removeEffect();
}
}
}()
</script>
</html>
AngularJS實現(xiàn)方法:
<!DOCTYPE html>
<html lang="en" ng-app="animateDemo">
<head>
<meta charset="UTF-8">
<title></title>
<script src="lib/angular.min.js" type="text/javascript"></script>
<script src="lib/angular-animate.js" type="text/javascript"></script>
<script src="lib/jquery-2.1.4.min.js" type="text/javascript"></script>
<style>
.spanStyle{
width: 200px;
height: 50px;
position: absolute;
left: 200px;
top: 60px;
background: slateblue;
overflow: hidden;
}
.spanStyle .animateSpan{
position: absolute;
background: saddlebrown;
display: block;
width: 50px;
height: 50px;
border-radius: 100%;
-webkit-transform: scale(0);
-moz-transform: scale(0);
-ms-transform: scale(0);
-o-transform: scale(0);
transform: scale(0);
}
.animate{
-webkit-animation: ripple .5s linear;
-moz-animation: ripple .5s linear;
-ms-animation: ripple .5s linear;
-o-animation: ripple .5s linear;
animation: ripple .5s linear
}
@-webkit-keyframes ripple {
100%{
opacity: 0;
-webkit-transform: scale(1.5);
}
}
@-moz-keyframes ripple {
100%{
opacity: 0;
-moz-transform: scale(1.5);
}
}
@-o-keyframes ripple {
100%{
opacity: 0;
-o-transform: scale(1.5);
}
}
@keyframes ripple {
100%{
opacity: 0;
transform: scale(1.5);
}
}
</style>
</head>
<body ng-controller="animateController">
<div>
<span-click></span-click>
</div>
</body>
<script>
var app=angular.module('animateDemo',["templateCache","serviceModule","directiveModule"])
.controller("animateController",['$scope','createEffect', function ($scope,createEffect) {
}])
var service=angular.module("serviceModule",[])
.factory("createEffect",["$animate","$compile","$rootScope", function ($animate,$compile,$rootScope) {
var x;
var y;
var span=angular.element("<span class='animateSpan'></span>");
var scopeNew=$rootScope.$new(true);
var spanEffect=$compile(span)(scopeNew);
return{
addEffect: function (obj) {
obj.on("click", function (e) {
var e=e||event;
obj.empty();
$animate.enter(spanEffect,obj);
x= e.pageX-this.offsetLeft-parseInt($(obj).find("span").css("width"))/2;
y= e.pageY-this.offsetTop-parseInt($(obj).find("span").css("width"))/2;
$(obj).find("span").css("left",x);
$(obj).find("span").css("top",y);
obj.find("span").addClass("animate");
})
}
}
}])
var directive=angular.module("directiveModule",["serviceModule"])
.directive("spanClick",["$animate","$compile","createEffect", function ($animate,$compile,createEffect) {
return{
restrict:'EA',
replace:true,
templateUrl:'spanClick.html',
link: function (scope, ele, attr) {
/* var x;
var y;
var span=angular.element("<span class='animateSpan'></span>");
//span.attr("class","animateSpan");
var scopeNew=scope.$new(true);
var spanEffect=$compile(span)(scopeNew);
ele.bind("click", function (e) {
ele.empty();
$animate.enter(spanEffect,ele);
x= e.pageX-this.offsetLeft-parseInt($(ele).find("span").css("width"))/2;
y= e.pageY-this.offsetTop-parseInt($(ele).find("span").css("width"))/2;
$(ele).find("span").css("left",x);
$(ele).find("span").css("top",y);
ele.find("span").addClass("animate");
})*/
createEffect.addEffect(ele)
}
}
}])
var templateCache=angular.module('templateCache',[])
.run(function ($templateCache) {
$templateCache.put("spanClick.html","<div class='spanStyle'></div>")
})
</script>
</html>