자바스크립트 - 서로 다른 시간에 시작/재시작된 CSS 애니메이션을 동기화하는 방법
저는 같은 애니메이션 클래스를 받는 여러 페이지 요소의 CSS 애니메이션을 동기화하려고 하고 있습니다.
이 주제에 대한 글이 많은 것 같지만 통일된 해결책은 없고, 제가 찾은 해결책 중 어느 것도 이 경우에 적용되는 것 같지 않습니다.
문제를 보여주기 위해 여기 jsfiddle을 준비했습니다:
https://jsfiddle.net/gdf7szo5/1/
어떤 글자를 클릭하면 해당 글자의 애니메이션이 시작됩니다. 다시 클릭하면 다른 애니메이션으로 전환되고, 세 번째로 클릭하면 글자에 애니메이션이 전혀 없도록 설정됩니다.
원하는 효과는 한 애니메이션에서 깜빡이는 모든 글자가 서로 동기화되는 것이고, 다른 애니메이션의 글자들도 서로 동기화되는 것입니다. 명확히 말하자면, 저는 두 애니메이션을 서로 동기화하려는 것이 아니라, 단지 주어진 애니메이션을 가진 모든 요소가 서로 동기화되기를 원합니다.
하지만 현재 한 글자가 애니메이션을 보여주고 다른 글자도 같은 애니메이션으로 설정하면, 타이밍이 완벽하지 않다면 두 글자의 애니메이션은 같은 애니메이션임에도 불구하고 동기화되지 않습니다.
다음은 사용 중인 코드입니다:
HTML :
<div>
<span id="spA" onclick="toggleFx('spA')">A</span>
<span id="spB" onclick="toggleFx('spB')">B</span>
<span id="spC" onclick="toggleFx('spC')">C</span>
<span id="spD" onclick="toggleFx('spD')">D</span>
</div>
CSS :
div {
display: flex;
flex-flow: column;
}
span {
animation-name: pulse_active;
}
span.pulse {
color: #f00;
animation: pulse_active 1.5s ease-in infinite;
}
span.pulse_invert {
color: #00f;
animation: pulse_inverted 3s ease-in infinite;
}
@keyframes pulse_active {
0% {
opacity: 0;
}
50% {
opacity: 0.66;
}
100% {
opacity: 0;
}
}
@keyframes pulse_inverted {
0% {
opacity: 1;
}
50% {
opacity: 0.33;
}
100% {
opacity: 1;
}
}
JS :
let pulseNormal = {};
let pulseInvert = {};
function toggleFx(spanID) {
let spanTarget = document.getElementById(spanID);
// shift setting
if (spanID in pulseInvert) {
console.log('y');
console.log(Object.keys(pulseInvert));
delete pulseInvert[spanID]
console.log(Object.keys(pulseInvert));
} else if (spanID in pulseNormal) {
pulseInvert[spanID] = true;
delete pulseNormal[spanID];
} else {
pulseNormal[spanID] = true
}
// update display
for (let sp of 'ABCD') {
let span = document.getElementById(`sp${sp}`);
// clear any prev animation
span.classList.remove('pulse');
span.classList.remove('pulse_invert');
// add/re-add animation as needed
if (`sp${sp}` in pulseNormal) {
span.classList.add('pulse');
} else if (`sp${sp}` in pulseInvert) {
span.classList.add('pulse_invert');
}
}
}
답변 #1
이를 위해 document.getAnimations() 메서드는 유용합니다. 이 메서드는 현재 적용 중인 모든 애니메이션 객체의 배열을 반환하며, 그 대상 요소는 문서의 하위 요소입니다. 자세한 정보: https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations
document.getAnimations()의 항목에는 애니메이션 동기화에 사용할 수 있는 currentTime 속성이 포함됩니다. 자세한 정보: https://drafts.csswg.org/web-animations/#dom-documentorshadowroot-getanimations
toggleFx 함수가 수정되었으며 이제 세 부분으로 나뉩니다.
첫 번째 부분에서 document.getAnimations()를 검색하고, animationName이 pulse_active 또는 pulse_inverted 중 하나인 currentTime 속성을 찾으세요; 그런 다음 이를 관련 변수(예: pulseActiveStart 및 pulseInvertedStart)에 저장합니다.
두 번째 부분에서, spanTarget의 className이 당신이 말한 대로 변경되었습니다.
3부에서는 pulseActiveStart와 pulseInvertedStart가 관련 애니메이션의 currentTime 속성에 적용되었습니다(1부를 역순으로).
function toggleFx(spanID) {
let spanTarget = document.getElementById(spanID);
let pulseActiveStart;
let pulseInvertedStart;
let anims = document.getAnimations()
for(let i = 0; i < anims.length; i++) {
if(anims[i].animationName == "pulse_active") pulseActiveStart = anims[i].currentTime;
else if(anims[i].animationName == "pulse_inverted") pulseInvertedStart = anims[i].currentTime;
}
if(spanTarget.classList.contains('pulse_invert')) {
spanTarget.classList.remove('pulse_invert');
spanTarget.classList.remove('pulse');
} else if(spanTarget.classList.contains('pulse')) {
spanTarget.classList.add('pulse_invert');
spanTarget.classList.remove('pulse');
} else {
spanTarget.classList.add('pulse');
}
anims = document.getAnimations()
for(let i = 0; i < anims.length; i++) {
if(anims[i].animationName == "pulse_active") {
if(pulseActiveStart) anims[i].currentTime = pulseActiveStart;
} else if(anims[i].animationName == "pulse_inverted") {
if(pulseInvertedStart) anims[i].currentTime = pulseInvertedStart;
}
}
}div {
display: flex;
flex-flow: column;
}
span.pulse {
color: #f00;
animation: pulse_active 1.5s ease-in infinite;
}
span.pulse_invert {
color: #00f;
animation: pulse_inverted 3s ease-in infinite;
}
@keyframes pulse_active {
0% { opacity: 0; }
50% { opacity: 0.66; }
100% { opacity: 0; }
}
@keyframes pulse_inverted {
0% { opacity: 1; }
50% { opacity: 0.33; }
100% { opacity: 1; }
}<div>
<span id="spA" onclick="toggleFx('spA')">A</span>
<span id="spB" onclick="toggleFx('spB')">B</span>
<span id="spC" onclick="toggleFx('spC')">C</span>
<span id="spD" onclick="toggleFx('spD')">D</span>
</div>답변 #2
그들을 동기화하기 위해서는 애니메이션을 동시에 초기화해야 합니다.
여기서 나는 span에 대한 animation-name을 지정합니다. 다른 속성이 정의될 때까지 애니메이션은 실행되지 않습니다(사실 나는 다른 렌더링 엔진들이 동일한 동작을 공유하는지 궁금합니다).
let pulseList = {};
function toggleFx(spanID) {
let spanTarget = document.getElementById(spanID);
// toggle setting
if (spanID in pulseList) {
delete pulseList(spanID)
} else {
pulseList[spanID] = true;
}
// update display
for (let sp of 'ABCD') {
let span = document.getElementById(`sp${sp}`);
span.classList.remove('pulse'); // clear any prev animation
if (`sp${sp}` in pulseList) { // add/re-add animation as needed
span.classList.add('pulse');
}
}
}div {
display: flex;
flex-flow: column;
}
span {
animation-name: pulse_active;
}
span.pulse {
color: #f00;
animation: pulse_active 1.5s ease-in infinite;
}
@keyframes pulse_active {
0% {
opacity: 0
}
50% {
opacity: 0.66
}
100% {
opacity: 0
}
}<div>
<span id="spA" onclick="toggleFx('spA')">A</span>
<span id="spB" onclick="toggleFx('spB')">B</span>
<span id="spC" onclick="toggleFx('spC')">C</span>
<span id="spD" onclick="toggleFx('spD')">D</span>
</div>편집
적어도 Blink와 Gecko는 같은 동작을 공유하지만, 저는 WebKit Safari에서는 테스트하지 않았습니다.
