>웹 프론트엔드 >JS 튜토리얼 >보이는 요소와 숨겨진 요소 사이에서 요소를 전환하는 전환 효과를 얻는 방법

보이는 요소와 숨겨진 요소 사이에서 요소를 전환하는 전환 효과를 얻는 방법

一个新手
一个新手원래의
2017-10-06 10:41:042151검색

최근 Ele.me 앱과 h5 웹사이트에서 판매자 세부정보 페이지에서 음식을 주문한 후 하단에 클릭 후 주문 세부정보를 볼 수 있는 모달 상자를 팝업할 수 있는 요소가 있는 것을 봤습니다. 점차 드러나고 숨겨지는 효과.

,

제 경험을 바탕으로 처음 생각한 것은 이 마스크 레이어가 숨김과 표시를 제어하기 위해 display:none;을 사용해야 한다는 것이었습니다. 하지만 이 속성은 전환 애니메이션을 파괴할 것입니다. 이 속성은 표시 및 숨기기를 제어하므로 점진적인 표시 및 숨기기 효과는 순간적인 표시 및 숨기기 효과를 얻기 어려울 것 같습니다.

Chrome을 사용하여 모바일 단말을 시뮬레이션하여 Ele.me의 구현을 확인한 결과 Ele.me가 vue를 사용하고 있다는 것을 깨달았습니다. 이 애니메이션 효과는 실제로 vue에 포함된 전환 애니메이션과 Hook 기능을 사용하여 구현됩니다.


프레임워크 구현

Vue 기반 애니메이션 페이드 구현

프레임워크를 사용하여 이 효과를 얻는 것은 코딩에 대해 걱정할 필요 없이 정말 쉽습니다.

// HTML<p id="app">
  <button class="btn" @click="show = !show">click</button>
  <transition name=&#39;fade&#39;>
    <p class="box1" v-if="show"></p>
  </transition>
</p>// CSS.box1 {
 width: 200px;
 height: 200px;
 background-color: green;
}

.fade-enter-active, .fade-leave-active {
 transition: opacity .5s
}
.fade-enter, .fade-leave-to{
 opacity: 0;
}

그림도 없고 진실도 없습니다. 효과를 보고 힘내세요:

이보다 더 간단할 수는 없습니다.

React를 기반으로 한 애니메이션 페이드 구현

React 자체의 별도 라이브러리에는 자체 전환 애니메이션이 없습니다. 하지만 애니메이션 추가 기능이 있습니다.

네이티브 구현

위는 모두 프레임워크 구현이지만 프로젝트의 역사가 길고 이러한 눈부신 프레임워크를 전혀 사용하지 않고 jquery 버전 1.2를 사용하는 경우 위 방법은 사용되지 않습니다. 나는 보편적인 네이티브 방법을 찾고 싶습니다.


visibility가 디스플레이를 대체합니다

해결책 중 하나는 제목에 표시된 것과 같습니다. 가시성 속성은 요소의 가시성을 제어할 수도 있고 가시성 속성은 표시와 숨김 사이를 앞뒤로 전환할 때 요소의 전환 애니메이션을 파괴하지 않기 때문입니다. .

그러나 가시성과 표시 사이에서 요소의 모양을 제어하는 ​​최종 효과에는 여전히 약간의 차이가 있습니다.

Visibility: Hidden; 요소는 실제로 시각적으로 보이지 않지만 해당 요소는 여전히 해당 위치를 차지하고 문서 흐름에 계속 존재하여 페이지 레이아웃에 영향을 미칩니다. 시각적으로 표시되지 않으며 단순히 페이지의 원래 위치에 빈 공간을 남겨 둡니다(요소에 너비와 높이가 있고 기본 위치 지정을 사용하는 경우).

display:none;이 있는 요소는 시각적으로 보이지 않으며 공간을 차지하지 않습니다. 이는 문서 흐름에서 사라졌음을 의미합니다.

가시성 제어 요소의 가시성도 순간적으로 발생하지만 이 순간 발생은 표시의 순간 발생과 다릅니다. 디스플레이는 설정된 전환 전환 속성을 전혀 고려하지 않습니다. 설정하는 것은 설정하지 않는 것과 같습니다. 같은.

그러나 가시성은 이 값에 관심을 가질 수 있지만 전환 기간 속성만 중요합니다.


예를 들어 visible:hidden에서 visible:visible로 변경하는 경우 전환 시간이 3초로 설정된 경우 이벤트가 발생한 후 요소는 숨김에서 표시로 효과를 즉시 표시하지 않지만 그림과 같이 표시됩니다. 아래 그런 식으로 먼저 3초 정도 기다렸다가 즉시 숨기면 표시부터 최종 시야에서 사라지기까지의 시간은 실제로 3초이지만 점차적으로 나타나지는 않습니다.

위 사진에는 문제가 있는 것 같습니다. 디스플레이에서 숨기기까지 3초가 걸리는데 숨기기에서 디스플레이까지 바로 완료되는 것 같고, 거기에 있습니다. 3초를 기다린다는 말은 아닙니다.

시각적으로는 그렇겠지만 이는 시각적인 느낌일 뿐입니다. 사실 이 기다림의 시간은 실제로 존재하고 눈에 보이지 않을 뿐입니다. 게다가 여기서의 기다림은 아무것도 하지 않고 순수한 기다림이 아닙니다.

之所以 display会破坏 transition动画,有个说法是,因为 transition对元素在整个过渡过程中的状态控制,是根据元素过渡前后起始的状态来计算得出的,例如从 opacity:0 到 opacity:1的变化,用时 3s,那么 transition会计算在这 3s内的每一帧画面中元素该有的 opacity值,从而完成过渡效果,其他的一些属性,例如 width、 scale、 color等都可以转化为数字进行计算 (说明文档参见), 但 display是个尴尬的属性,从 display:none到 display:block 该怎么计算值呢?

计算不了,所以就只能 破坏了, visibility同样如此,只不过 visibility比 display稍好一点,因为最起码 visibility不会破罐子破摔,不会搞破坏。

从 visibility:hidden到 visibility:visible的过程中。因为没办法计算过渡阶段没帧的值,所以元素就直接显示出来了,但内在的过渡操作依旧在元素显示出来后显示了 3s,而从 visibility:visible 到 visibility:hidden,元素在视觉上看起来等待的 3s内,实际在内部已经在进行 transition过渡操作,只不过还是因为没办法计算值,所以到了过渡阶段的最后一刻时,就直接将元素设置为结束状态,也就是隐藏了。

想要验证这种说法,还需要配合另外一个属性: opacity,此属性也是配合 visibility完成过渡效果的搭配属性。

实现代码如下


// HTML<button class="btn">click</button>
<p class="box1"></p>


// CSS.box1 {
 width: 200px;
 height: 200px;
 background-color: green;

 opacity: 0;
 visibility: hidden;
 transition: all 2s linear;
}
.show {
 opacity: .6;
 visibility: visible;
}

 js控制显隐效果代码如下:

let box1 = document.querySelector('.box1')
let btn = document.querySelector('button')
btn.addEventListener('click', ()=>{
let boxClassName = box1.className
 boxClassName.includes('show')? box1.className = boxClassName.slice(0, boxClassName.length-5)
: box1.className += ' show'})

 

效果依旧没问题:

 


 

因为虽然 visibility没办法计算值,但 opacity可以,过渡的效果实际上是 opacity在起作用。

其实 opacity本身就能控制元素的显隐,把上面代码中的所有 visibility 全部删除,效果依旧不变,并且和 visibility 一样,设置了 opacity:0; 的元素依旧存在于文档流中, but,相比于 visibility:hidden, opacity:0 的元素并不会出现点透。

而 visibility:hidden的元素就会出现点透,点击事件会穿透 visibility:hidden的元素,被下面的元素接收到,元素在隐藏的时候,就不会干扰到其他元素的点击事件。

关于这个说法,似乎网上有些争论,但是我用迄今最新版的 Chrome Firefox 以及 360浏览器 进行测试, 都是上面的结果。

如果你只是想让元素简单的渐进显隐,不用管显隐元素会不会遮挡什么点击事件之类的,那么完全可以不用加 visibility 属性,加了反而是自找麻烦,但是如果需要考虑到这一点,那么最好加上。


setTimeOut

如果不使用 visibility的话还好,但是如果使用了此属性,那么上述的解决方案其实还有点小瑕疵,因为 visibility从 IE10以及 Android4.4才开始支持,如果你需要支持这种版本的浏览器,那么 visibility 就派不上用场了。

 

哎呦呦,公司网站最低要求都是 IE9,用不了了诶。

怎么办?再回到 display 这个属性上。

为什么 display 这个属性会影响到 transition 动画的原因上面已经大致说了下,既然问题是出在了 display上,那么我可以同样参考上面 visibility的做法,加个 opocity属性进行辅助,又因为考虑到 display 比起 visibility 来说破坏性较大,所以再让 opocity 与 display 分开执行不就行了吗?

你如果写成这种形式:

box1.style.display=&#39;block&#39;box1.style.opacity=1

其实还是没用的,尽管 display值的设定在代码上看起来好像是在 opacity前面,但是执行的时候却是几乎同时发生的。

我的理解是应该是浏览器对代码进行了优化,浏览器看到你分两步为同一个元素设置 CSS属性,感觉有点浪费,为了更快地完成这两步,它帮你合并了一下,放在一个 tick(参见 [ ( ] )内执行,变成一步到位了,也就是同步执行了这两句代码。

那么如何明确地让浏览器不要合并到一个 tick内执行呢? setTimeOut就派上了用场。

setTimeOut 一个重要功能就是延迟执行,只要将 opacity属性的设置延迟到 display后面执行就行了。


// CSS.box1 {
 width: 200px;
 height: 200px;
 background-color: green;

 display: none;
 opacity: 0;
 transition: all 2s linear;
}

下面是控制元素渐进显示的代码:


// JSlet box1 = document.querySelector(&#39;.box1&#39;)
let btn = document.querySelector(&#39;.btn&#39;)
btn.addEventListener(&#39;click&#39;, ()=>{
let boxDisplay = box1.style.displayif(boxDisplay === &#39;none&#39;) {
   box1.style.display=&#39;block&#39;
   setTimeout(()=> {
     box1.style.opacity = 0.4})
}
})

 上述代码中,最关键的就是 setTimeOut 这一句,延迟元素 opacity属性的设定。

setTiomeOut的第二个可选的时间 delay参数,我在最新版的 Chrome和 360 浏览器上测试,此参数可以不写,也可以写成 0或者其他数值,但是在 firefox上,此参数必须写,不然渐进效果时灵时不灵,而且不能为 0,也不能太小,我测出来的最小数值是 14,这样才能保证渐进效果,所以为了兼容考虑,最好还是都统一加上时间。

至于为什么是 14,我就不清楚了,不过记得以前看过一篇文章,其中说 CPU能够反应过来的最低时间就是 14ms,我猜可能与这个有关吧。

显示的效果有了,那么要隐藏怎么办? setTimeOut 当然也可以,在 JS代码的 if(boxDisplay==='none')后面再加个 else

else {
  box1.style.opacity = 0
  setTimeout(()=>{
    box1.style.display = &#39;none&#39; }, 2000)
}

 

隐藏时先设置 opacity,等 opacity过渡完了,再设置 display:none;。

但是这里有点不太合理,因为虽然 setTimeOut的 delay参数 2000ms和 transition 时间 2s一样大,但因为 JS是单线程,遵循时间轮询,所以并不能保证 display属性的设置刚好是在 opacity过渡完了的同时执行,可能会有更多一点的延迟,这取决于过渡动画完成之刻, JS主线程是否繁忙。

当然,就算是延迟,一般也不会延迟多长时间的,人眼不太可能感觉得到,如果不那么计较的话其实完全可以无视,但是如果我就吹毛求疵,要想做到更完美,那怎么办?


transitionend

transition 动画结束的时候,对应着一个事件: transitionend,MDN [  ] 上关于此事件的详细如下:

transitionend 事件会在 CSS transition 结束后触发. 当 transition完成前移除 transition时,比如移除 css的 transition-property 属性,事件将不会被触发,如在 transition完成前设置 display:none,事件同样不会被触发。

如果你能够使用 transition,那么基本上也就能够使用这个事件了,只不过此事件需要加前缀的浏览器比较多(现在最新版的所有 主流浏览器,都已经不用写前缀了),大致有如下写法:


transitionend
webkitTransitionEnd
mozTransitionEnd
oTransitionEnd

使用此属性,就可以避免上面 setTimeOut可能出现的问题了 ,使用示例如下:

// ...else {
 box1.style.opacity = 0
 box1.addEventListener(&#39;transitionend&#39;, function(e) {
   box1.style.display = &#39;none&#39;});
}
需要注意的是, transitionend 事件监听的对象是所有 CSS中transition属性指定的值,例如,如果你为元素设置了 transition:all3s;的 样式,那么元素可能无论是 left top还是 opacity 的改变,都会触发该事件,也就是说此事件可能会被触发多次,并且并不一定每次都是你想要触发的,针对这种情况,最好加一个判断。

既然是 涉及到了 JS实现的动画,那么其实可以考虑一下 把 setTimeout换成 requestAnimationFrame。


btn.addEventListener(&#39;click&#39;, ()=>{
let boxDisplay = box1.style.displayif(boxDisplay === &#39;none&#39;) {
   box1.style.display=&#39;block&#39;// setTimeOut 换成 requestAnimationFrame
   requestAnimationFrame(()=> {
     box1.style.opacity = 0.6})
} else {
  box1.style.opacity = 0
  box1.addEventListener(&#39;transitionend&#39;, function(e) {
    box1.style.display = &#39;none&#39;});
}
})

文章最开始说过的 vue 和 react这两个框架实现示例动画的方法,也利用到了这个 API,,监听动画过渡的状态,为元素添加和删除一系列过渡类名的操作,当然,并不是全部,此事件只能监听动画结束的这个时刻,其他时间点是无法监听的。

  • 以下为 transitionEnd 在 react-addons-css-transition-group源码里面出现的形式:

react-addons-css-transition-group对 transitionend做了兼容,如果浏览器支持此属性,则使用,如果不支持,就使用 setTimeOut这种形式。

  • 以下为 transitionEnd 在 vue源码里面出现的形式:

 

그런데, Transitionend 이벤트 외에도 animationend도 있습니다 [] event, this 이벤트는 애니메이션 애니메이션에 해당하고, React-addons-css-transition-group 및 vue도 Transitionend에 해당합니다. 이 속성은 여기서 확장되지 않습니다.

위 내용은 보이는 요소와 숨겨진 요소 사이에서 요소를 전환하는 전환 효과를 얻는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.