다음 스 니펫을 실행 한 다음 상자를 클릭하십시오.
const box = document.querySelector('.box')
box.addEventListener('click', e => {
if (!box.style.transform) {
box.style.transform = 'translateX(100px)'
new Promise(resolve => {
setTimeout(() => {
box.style.transition = 'none'
box.style.transform = ''
resolve('Transition complete')
}, 2000)
}).then(() => {
box.style.transition = ''
})
}
})
.box {
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #121212;
transition: all 2s ease;
}
<div class = "box"></div>
내가 일어날 것으로 예상되는 것 :
- 클릭이 발생합니다
- 상자가 가로로 100px 번역을 시작합니다 (2 초 소요)
- 클릭하면 새로운
Promise
것도 생성됩니다. 내부Promise
에서setTimeout
기능이 2 초로 설정되었습니다. - 조치가 완료된 후 (2 초가 경과 한 후)
setTimeout
콜백 기능을 실행하고transition
none으로 설정하십시오 . 그런 다음 원래 값으로setTimeout
되돌려transform
서 상자를 원래 위치에 표시합니다. - 상자는 전환 효과 문제 없이 원래 위치에 나타납니다.
- 모든 작업이 끝나면
transition
상자 값을 원래 값으로 다시 설정하십시오.
그러나 알 수 있듯이 transition
값을 실행할 none
때 보이지 않는 것 같습니다 . 위와 같은 다른 방법, 예를 들어 keyframe 및를 사용하는 방법 transitionend
이 있지만 왜 이런 일이 발생합니까? 콜백 이 완료된 후에transition
만 명시 적으로 원래 값으로 되돌려 서 약속을 해결합니다.setTimeout
편집하다
요청에 따라 문제가되는 동작을 표시하는 코드는 다음과 같습니다.
답변
이벤트 루프 배치 스타일이 변경됩니다. 한 줄에서 요소의 스타일을 변경하면 브라우저에 해당 변경 사항이 즉시 표시되지 않습니다. 다음 애니메이션 프레임까지 기다립니다. 이것이 예를 들어
elm.style.width = '10px';
elm.style.width = '100px';
깜박 거리지 않습니다. 브라우저는 모든 Javascript가 완료된 후에 설정 한 스타일 값만 고려합니다.
렌더링은 마이크로 태스크를 포함하여 모든 Javascript가 완료된 후에 발생합니다 . 약속의는 microtask 발생 (다른 모든 자바 스크립트가 끝난 효과적으로 빨리으로 실행되지만 다른 어떤 전에 – 실행에 기회가있다 – 같은 렌더링 등)..then
브라우저가에 의한 변경 사항을 렌더링하기 전에 마이크로 작업에서 transition
속성을 설정하고 있습니다 .''
style.transform = ''
a requestAnimationFrame
(다음 다시 그리기 직전에 실행 됨) 다음에 빈 문자열로의 전환을 재설정 한 다음 (다음 다시 그리기 직후에 setTimeout
실행 됨) 후에 예상대로 작동합니다.
const box = document.querySelector('.box')
box.addEventListener('click', e => {
if (!box.style.transform) {
box.style.transform = 'translateX(100px)'
setTimeout(() => {
box.style.transition = 'none'
box.style.transform = ''
// resolve('Transition complete')
requestAnimationFrame(() => {
setTimeout(() => {
box.style.transition = ''
});
});
}, 2000)
}
})
.box {
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #121212;
transition: all 2s ease;
}
<div class="box"></div>
답변
요소가 숨겨진 문제를 시작 하지만 transition
속성에서 직접 시작하면 변형의 변형이 작동하지 않습니다 .
CSSOM과 DOM이 “다시 그리기”프로세스를 위해 어떻게 연결되는지 이해 하려면 이 답변 을 참조하십시오 .
기본적으로 브라우저는 일반적으로 다음 페인팅 프레임 이 모든 새 상자 위치를 다시 계산하고 CSSOM에 CSS 규칙을 적용 할 때까지 기다립니다 .
당신의 약속 처리기 그래서, 당신이 다시 때 transition
하는 ""
의는 transform: ""
여전히 아직 계산되지 않았습니다. 계산이 완료되면이 값 transition
은 이미 재설정되었으며 ""
CSSOM은 변환 업데이트를위한 전환을 트리거합니다.
그러나 브라우저가 “리플 로우”를 트리거하도록 할 수 있으므로 전환을로 재설정하기 전에 요소의 위치를 다시 계산할 수 있습니다 ""
.
약속을 사용하지 않아도됩니다.
const box = document.querySelector('.box')
box.addEventListener('click', e => {
if (!box.style.transform) {
box.style.transform = 'translateX(100px)'
setTimeout(() => {
box.style.transition = 'none'
box.style.transform = ''
box.offsetWidth; // this triggers a reflow
// even synchronously
box.style.transition = ''
}, 2000)
}
})
.box {
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #121212;
transition: all 2s ease;
}
<div class = "box"></div>
그리고 마이크로 작업 등에 대한 설명 Promise.resolve()
또는 MutationEvents , 또는 queueMicrotask()
, 당신은 그들이 곧 현재 작업이 완료 될 때로 달아거야 이해할 필요가 이벤트 루프 처리 모델의 7 단계를 하기 전에, 렌더링 단계 .
따라서 귀하의 경우 동 기적으로 실행되는 것과 매우 유사합니다.
그건 그렇고, 마이크로 작업은 while 루프만큼 차단 될 수 있습니다.
// this will freeze your page just like a while(1) loop
const makeProm = ()=> Promise.resolve().then( makeProm );
답변
나는 이것이 당신이 찾고있는 것이 아니라고 생각하지만 호기심과 완전성을 위해이 효과에 대한 CSS 전용 접근법을 작성할 수 있는지 알고 싶었습니다.
거의 …하지만 여전히 한 줄의 자바 스크립트를 포함해야한다는 것이 밝혀졌습니다.
작업 예 :
document.querySelector('.box').addEventListener('animationend', (e) => e.target.blur());
.box {
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #121212;
cursor: pointer;
}
.box:focus {
animation: boxAnimation 2s ease;
}
@keyframes boxAnimation {
100% {transform: translateX(100px);}
}
<div class="box" tabindex="0"></div>
답변
나는 당신의 문제는 그냥 믿는 당신이에서 .then
당신이 설정하는 transition
에을 ''
은으로 설정되어야 할 때, none
당신은 타이머 콜백에서 그랬던 것처럼.
const box = document.querySelector('.box');
box.addEventListener('click', e => {
if (!box.style.transform) {
box.style.transform = 'translateX(100px)';
new Promise(resolve => {
setTimeout(() => {
box.style.transition = 'none';
box.style.transform = '';
resolve('Transition complete');
}, 2000)
}).then(() => {
box.style.transition = 'none'; // <<----
})
}
})
.box {
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #121212;
transition: all 2s ease;
}
<div class = "box"></div>