2013-08-01 9 views
10

Czy można uzyskać/ustawić bieżący procent animacji animacji CSS3 @keyframes przy użyciu javascript, jQuery lub w inny sposób?Pobieranie/ustawianie bieżących procentów @keyframes/zmiana klatek kluczowych

Załóżmy na przykład, że istnieje div z klasą o nazwie .spin, która po prostu obraca się w kółko za pomocą klatki kluczowej o nazwie spin.

  1. Próbowałem uzyskać bieżącą wartość procentową animacji za pomocą $('.spin').css('animation'), $('.spin').css('animation: spin'), a kilka innych sposobów, ale wszystkie one alert pusty

  2. Jestem również zainteresowany zmieniając oryginalną animację przy każdej predefiniowanej klatce kluczowej% i próbowałem bezskutecznie rzeczy takich jak $('.spin').css('animation', '..new definition here...') i $('.spin').css('spin', '50%', '...new definition here...).

Jakieś pomysły?

Odpowiedz

12

Osiągnąłem mniej więcej to, co chciałem, używając czystego javascript z moim CSS3.

Aby mój eksperyment mógł wymyślić sposób na osiągnięcie tych celów, stworzyłem podstawową animację CSS3 obracającą okrąg wokół małej okrągłej ścieżki. Moim celem było, gdy przycisk został kliknięty, zmień pochodzenie animacji do nowej lokalizacji

Aby osiągnąć pierwszy cel uzyskania wartość procentową animacji po prostu przybliżeniem aktualny procent stosując następujący setInterval , który wyświetla przybliżony bieżący procent. To kursuje co 40 milisekund, aby odpowiadać na czas trwania animacji (milliseconds/100)

var result = document.getElementById('result'), currentPercent = 0; 
var showPercent = window.setInterval(function() { 
    if(currentPercent < 100) 
    { 
    currentPercent += 1; 
    } 
    else { 
    currentPercent = 0; 
    } 
    result.innerHTML = currentPercent; 
}, 40); 

Uwaga dotycząca tego rozwiązania:

  • To nie jest idealne ze względu ponieważ licznik pracuje cały czas, kiedy kolejna zakładka kliknięciu jednak animacja zostanie zatrzymana, więc staną się niezsynchronizowane.
  • Jest również uszkodzony po kliknięciu przycisku długo po ostatnim kliknięciu. Widocznie setInterval biegnie trochę dłużej niż animacji, więc stają się coraz mniej synchronizowane każdej iteracji
  • Szukałem wszędzie na bardziej idealne rozwiązanie, ale nie byli w stanie wymyślić jeden dzień jeszcze

Aby osiągnąć drugi cel ustawienia nowej definicji wartości% animacji, wymagało to nieco bardziej złożonego rozwiązania.Po wylaniu przez dziesiątki artykułów i stron internetowych (ważne te wymienione poniżej), udało mi się wymyślić następujące rozwiązanie:

var circle = document.getElementById('circle'), 
    button = document.getElementById('button'); 
var result = document.getElementById('result'), 
    totalCurrentPercent = 0, 
    currentPercent = 0; 
var showPercent = window.setInterval(function() { 
    if(currentPercent < 100) 
    { 
    currentPercent += 1; 
    } 
    else { 
    currentPercent = 0; 
    } 
    result.innerHTML = currentPercent; 
}, 40); 
function findKeyframesRule(rule) { 
    var ss = document.styleSheets; 
    for (var i = 0; i < ss.length; ++i) { 
     for (var j = 0; j < ss[i].cssRules.length; ++j) { 
      if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule) { return ss[i].cssRules[j]; } 
     } 
    } 
    return null; 
} 
function change(anim) { 
    var keyframes = findKeyframesRule(anim), 
     length = keyframes.cssRules.length; 
    var keyframeString = []; 
    for(var i = 0; i < length; i ++) 
    { 
    keyframeString.push(keyframes[i].keyText); 
    } 
    var keys = keyframeString.map(function(str) { 
    return str.replace('%', ''); 
    }); 
    totalCurrentPercent += currentPercent; 
    if(totalCurrentPercent > 100) 
    { 
    totalCurrentPercent -= 100; 
    } 
    var closest = getClosest(keys); 
    var position = keys.indexOf(closest), 
     firstPercent = keys[position]; 
    for(var i = 0, j = keyframeString.length; i < j; i ++) 
    { 
    keyframes.deleteRule(keyframeString[i]); 
    } 
    var multiplier = firstPercent * 3.6; 
    keyframes.insertRule("0% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 0) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 0) + "deg); background-color:red; }"); 
    keyframes.insertRule("13% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 45) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 45) + "deg); }"); 
    keyframes.insertRule("25% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 90) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 90) + "deg); }"); 
    keyframes.insertRule("38% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 135) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 135) + "deg); }"); 
    keyframes.insertRule("50% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 180) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 180) + "deg); }"); 
    keyframes.insertRule("63% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 225) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 225) + "deg); }"); 
    keyframes.insertRule("75% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 270) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 270) + "deg); }"); 
    keyframes.insertRule("88% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 315) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 315) + "deg); }"); 
    keyframes.insertRule("100% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 360) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 360) + "deg); }"); 
    circle.style.display = "inherit"; 
    circle.style.webkitAnimationName = anim; 
    window.clearInterval(showPercent); 
    currentPercent = 0; 
    showPercent = self.setInterval(function() { 
    if(currentPercent < 100) 
    { 
     currentPercent += 1; 
    } 
    else { 
     currentPercent = 0; 
    } 
    result.innerHTML = currentPercent; 
    }, 40); 
} 
button.onclick = function() { 
    circle.style.webkitAnimationName = "none"; 
    circle.style.display = "none"; 
    setTimeout(function() { 
     change("rotate"); 
    }, 0); 
} 
function getClosest(keyframe) { 
    var curr = keyframe[0]; 
    var diff = Math.abs (totalCurrentPercent - curr); 
    for (var val = 0; val < keyframe.length; val++) { 
    var newdiff = Math.abs(totalCurrentPercent - keyframe[val]); 
    if (newdiff < diff) { 
     diff = newdiff; 
     curr = keyframe[val]; 
    } 
    } 
    return curr; 
} 

Check out the experiment itself here tym komentarzy opisujących to, co każda część javascript robi

Uwagi dotyczące tego rozwiązania:

  • próbowałem dokonać zmiany funkcji jako non-zakodowane możliwie
  • działa w porządku dla przybliżenia obecnego @keyvalue procent
  • Przejście z jednej animacji na drugą jest tylko tak gładkie, jakkolwiek bliskie jest najbliższe% wartości animacji do bieżącego% animacji, więc dodanie do animacji więcej definicji% uczynić go jeszcze bardziej gładka

W procesie próbując wymyślić rozwiązanie problemu, znalazłem te Przydatne zasoby:

  • RussellUresti's answer in this SO post i corresponding example był dość wpływowy i znacznie ułatwione moje ostateczne rozwiązanie
  • Aby uzyskać możliwie najbliższe wartości na podstawie wejściowych i Array wartości, użyłem metody paxdiablo w this SO post (dziękuję)
  • This plugin, a ja nie używaliśmy go siebie, wydawało się osiągnąć bardzo podobne (choć pozornie nie do końca jak konfigurowalny) efekt w jQuery

--- EDIT ---

Jeśli tylko przy użyciu przejścia CSS lub można zmienić animację przejścia po prostu użyć zamiast tego można użyć this simpler approach. Możesz wstrzymać przejście, kopiując atrybuty zmienione przez przejście, ustawiając atrybuty na zmienione atrybuty, a następnie usuwając klasę, która je animuje. Oczywiście, jeśli używasz tego samego obiektu do animacji/pauzy, będziesz musiał animować pierwsze kliknięcie, a następnie wstrzymać następne kliknięcie. Można łatwo zrobić czystą równowartość javascript także

Uwaga:!important w atrybutu CSS zmieniane jest konieczne, chyba że masz bardziej wyrównanej wybieraka dla klasy animacji niż selektor jQuery, aka coś podobnego div #defaultID.animationClass { w przeciwieństwie do tylko #defaultID.animationClass {. Od #defaultID#defaultID.animationClass i to zarówno jeden poziom, w tym przykładzie wymaga !important

--Another Edit--

Aby uzyskać więcej informacji na ten temat, sprawdź my post on CSS-Tricks

Powiązane problemy