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:
--- 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