我尝试制作Google材料"涟漪"效果,没有任何库。为此,我使用了Sitepoint's script for capturing CSS3 Animation和一个脚本来模拟" jQuery的偏移方法:
function offset(elt) {
var rect = elt.getBoundingClientRect(), bodyElt = document.body;
return {
top: rect.top + bodyElt.scrollTop,
left: rect.left + bodyElt.scrollLeft
}
}
我从Hai Nguyen react touch-ripple file获取动画的例子但是我甚至不知道反应的基础知识,所以我做了类似他的代码主要用于动画。我的问题是,为了效果,我添加了一个新的<span>
,在完成所有修改并在1秒延迟后移除旧的,只需点击一下它就能正常工作,但如果我点击几次新的<span>
元素仍在此处。我远不是一个JS大师。因此,如果您发现一个重大错误,请告诉我,我将改进我的代码。 HTML,SCSS和JS的所有代码都可见here。
我的HTML:
<button>
<span class="ripples">
<span class="ripples-circle"></span>
</span>Test
</button>
<button>
<span class="ripples">
<span class="ripples-circle"></span>
</span>Test
</button>
<button>
<span class="ripples">
<span class="ripples-circle"></span>
</span>Test
</button>
<button>
<span class="ripples">
<span class="ripples-circle"></span>
</span>Test
</button>
<button class="round">
<span class="ripples">
<span class="ripples-circle"></span>
</span>Test
</button>
我的SCSS(我使用Autoprefixer):
body { /*just for remove inline-block lateral margins*/
font-size: 0;
}
button {
font-size: 12px;
height: 4em;
width: 25%;
background-color: gray;
border-style:solid;
color: #fff;
cursor: pointer;
position: relative;
display: inline-block;
overflow: hidden;
padding: 12px 24px;
margin-top: 0;
margin-bottom: 0;
vertical-align: middle;
transition: all 250ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
&.round {
border-radius: 50%;
height: 100px;
width: 100px;
}
&:focus {
outline: 0;
}
&:hover {
background-color: darken(gray, 5%);
}
}
.ripples {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: transparent;
opacity: 0.7;
transition: opacity 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
&.is-active {
opacity: 1;
.ripples-circle {
-webkit-transform: scale(1);
transform: scale(1);
}
}
.ripples-circle {
background-color: rgba(255,255,255,.5);
position: absolute;
left: 0;
right: 0;
height: 100%;
width: 100%;
border-radius: 50%;
-webkit-transform: scale(0);
transform: scale(0);
transition: -webkit-transform 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
transition: transform 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
}
}
JavaScript的:
;(function(window, document, undefined){
'use strict';
//http://www.debray-jerome.fr/js-performance-la-fonction-offset-de-jquery-vs-vanilla-javascript-23.html -- french
function offset(elt) {
var rect = elt.getBoundingClientRect(), bodyElt = document.body;
return {
top: rect.top + bodyElt.scrollTop,
left: rect.left + bodyElt.scrollLeft
}
}
//http://www.sitepoint.com/css3-animation-javascript-event-handlers/
var pfx = ["webkit", "moz", "MS", "o", ""];
function prefixedEvent(element, type, callback) {
for (var p = 0; p < pfx.length; p++) {
if (!pfx[p]) type = type.toLowerCase();
element.addEventListener(pfx[p]+type, callback, false);
}
}
function calcDiag(a, b) {
return Math.sqrt((a * a) + (b * b));
}
var buttons = document.querySelectorAll('button');
for(var i=0; i < buttons.length;i++ ){
buttons[i].addEventListener('click', function(e){
var rippleThis = this;
var parentOffset = offset(rippleThis);
var elHeight = rippleThis.offsetHeight;
var elWidth = rippleThis.offsetWidth;
var pageX = e.pageX == undefined ? e.nativeEvent.pageX : e.pageX;
var pageY = e.pageY == undefined ? e.nativeEvent.pageY : e.pageY;
var x = pageX - parentOffset.left;
var y = pageY - parentOffset.top;
var topLeftDiag = calcDiag(x, y);
var topRightDiag = calcDiag(elWidth - x, y);
var botRightDiag = calcDiag(elWidth - x, elHeight - y);
var botLeftDiag = calcDiag(x, elHeight - y);
var rippleRadius = Math.max(topLeftDiag, topRightDiag, botRightDiag, botLeftDiag);
var rippleSize = rippleRadius * 2;
var j;
var childrenLength = rippleThis.childNodes.length;
var targetParent;
var targetChild;
for(j = 0; j < childrenLength; j++){
if(rippleThis.childNodes[j].className == 'ripples') {
targetParent = rippleThis.childNodes[j];
targetParent.style.top = (y - rippleRadius) + 'px';
targetParent.style.left = (x -rippleRadius) + 'px';
targetParent.style.height = rippleSize +'px';
targetParent.style.width = rippleSize +'px';
targetParent.className = 'ripples is-active';
}
}
//New span element
var rippleSpan = document.createElement('span');
var rippleCircleSpan = document.createElement('span');
rippleSpan.className = "ripples";
rippleCircleSpan.className = "ripples-circle";
//Add new span
rippleSpan.appendChild(rippleCircleSpan);
rippleThis.insertBefore(rippleSpan, rippleThis.childNodes[0]);
}, false);
buttons[i].addEventListener('mouseup', function(e){
prefixedEvent(e.currentTarget, "TransitionEnd", function(e){
var button = e.currentTarget;
for(var j = 0; j < button.childNodes.length; j++){
if(button.childNodes[j].className == 'ripples is-active') {
var targetParent = button.childNodes[j];
targetParent.style.opacity = 0;
setTimeout(function(){
if(targetParent.parentNode){ targetParent.parentNode.removeChild(targetParent);
}
}, 1000);
}
}
});
}, false);
}
})(window, document);
我想要的最终效果是here。
答案 0 :(得分:4)
我已经为我的问题找到了解决方案。工作脚本可见here。
我在代码中将我的前缀事件置于最高位置,之后它是eventListener
mouseup
事件的单独span
,更重要的是我在父button
元素上调用它之前setTimeOut
元素1}}本身,这就是事件不起作用的原因。我又为if(targetParent.parentNode){
条件增加了一个条件:
在
if(targetParent && targetParent.parentNode){
在
span
现在我的代码正常运行,DOM
从{{1}}
答案 1 :(得分:2)
从链接网站上我可以看出,视觉效果非常好,问题是你的剩余跨度。我相信你的问题实际上是closure
并且与反应没有任何关系。我最近有一个类似的问题(在完全不同的环境中,但基本相同),我通过删除超时并使用检查超时条件的间隔来解决它。这几乎是它的工作原理:
有一个javascript变量,用于保存所有活动元素的数组及其创建时间。每1秒循环所有元素,看看他们的计时器是否已经过期。如果是这样,请删除它们。
这显然不是最有效的解决方案,因为它需要(几乎)不断检查元素,但它肯定会起作用。如果在性能密集型环境中未实现此功能,则应该可以很好地执行此操作。
另外,如果您找到另一种解决方案,我很高兴看到我是否也可以实施。