Web Animasyonları API'si ile Karmaşıklığı Düzenleme
Yayınlanan: 2022-03-10Basit geçişler ve karmaşık animasyonlar arasında orta yol yoktur. Ya CSS Geçişlerinin ve Animasyonlarının sağladığı şeyler konusunda sorun yaşamazsınız ya da birdenbire alabileceğiniz tüm güce ihtiyaç duyarsınız. Web Animasyonları API'si, animasyonlarla çalışmanız için size birçok araç sunar. Ama onlarla nasıl başa çıkacağını bilmelisin. Bu makale, esnek kalırken karmaşık animasyonlarla başa çıkmanıza yardımcı olabilecek ana noktalar ve teknikler konusunda size yol gösterecektir.
Makaleye girmeden önce, Web Animasyonları API'si ve JavaScript'in temellerini bilmeniz çok önemlidir. Açıklığa kavuşturmak ve eldeki sorundan dikkatin dağılmasını önlemek için sağlanan kod örnekleri açıktır. Fonksiyonlardan ve nesnelerden daha karmaşık bir şey olmayacak. Animasyonlara güzel bir giriş noktası olarak, genel bir referans olarak MDN'yi, Daniel C. Wilson'ın mükemmel serisini ve Ollie Williams'ın CSS Animations vs Web Animations API'sini öneririm. Etkileri tanımlamanın ve istediğiniz sonuca ulaşmak için onları ayarlamanın yollarından geçmeyeceğiz. Bu makale, animasyonlarınızı tanımladığınızı ve bunları işlemek için fikir ve tekniklere ihtiyacınız olduğunu varsayar.
Arayüzlere ve bunların ne işe yaradığına genel bir bakışla başlıyoruz. Ardından neyin, ne zaman ve ne kadar süreyle kullanılacağını belirlemek için zamanlamaya ve kontrol seviyelerine bakacağız. Bundan sonra, birkaç animasyonu nesnelere sararak nasıl tek bir animasyon olarak ele alacağımızı öğreneceğiz. Bu, Web Animasyonları API'sini kullanma yolunda iyi bir başlangıç olacaktır.
Arayüzler
Web Animasyonları API'si bize yeni bir kontrol boyutu sağlıyor. Bundan önce, CSS Geçişleri ve Animasyon, efektleri tanımlamanın güçlü bir yolunu sunarken hala tek bir harekete geçirme noktasına sahipti. Bir ışık anahtarı gibi, ya açıktı ya da kapalıydı. Oldukça karmaşık efektler oluşturmak için gecikmeler ve yumuşatma işlevleriyle oynayabilirsiniz. Yine de, belirli bir noktada, hantal ve çalışmak zor hale geliyor.
Web Animations API, bu tek çalıştırma noktasını oynatma üzerinde tam kontrole dönüştürür. Işık anahtarı, kaydırıcılı bir kısma anahtarına dönüşür. İsterseniz, tüm akıllı ev olayına dönüştürebilirsiniz, çünkü oynatma kontrolüne ek olarak, çalışma zamanında efektleri tanımlayabilir ve değiştirebilirsiniz. Artık efektleri bağlama göre uyarlayabilir veya gerçek zamanlı önizlemeli bir animasyon düzenleyici uygulayabilirsiniz.
Animasyon arayüzü ile başlıyoruz. Bir animasyon nesnesi elde etmek için Element.animate
yöntemini kullanabiliriz. Ona anahtar kareler ve seçenekler verirsiniz ve animasyonunuzu hemen oynatır. Ayrıca yaptığı şey, bir Animation
nesnesi örneği döndürmesidir. Amacı, oynatmayı kontrol etmektir.
Bunları hatırlıyorsanız, bir kaset çalar gibi düşünün. Bazı okuyucuların ne olduğunu bilmediğinin farkındayım. Soyut bilgisayarlı şeyleri tanımlamak için gerçek dünya kavramlarını uygulamaya yönelik herhangi bir girişimin hızla dağılması kaçınılmazdır. Ama size - bir kaseti kurşun kalemle geri sarmanın keyfini bilmeyen bir okuyucuya - kasetçaların ne olduğunu bilen insanların bu makalenin sonunda kafalarının daha da karışacağına dair güvence verin.
Bir kutu hayal edin. Kasetin gittiği bir yuvaya ve oynatma, durdurma ve geri sarma düğmelerine sahiptir. Animasyon arabirimi örneği budur — tanımlı animasyonu tutan ve oynatımıyla etkileşim kurmanın yollarını sağlayan bir kutu . Oynamak için bir şey veriyorsunuz ve size kontrolleri geri veriyor.
Aldığınız kontroller, ses ve video öğelerinden aldığınız kontrollere kolaylıkla benzer. Bunlar oynat ve duraklat yöntemleri ve geçerli zaman özelliğidir. Bu üç kontrolle, oynatma söz konusu olduğunda her şeyi oluşturabilirsiniz.
Kasetin kendisi, canlandırılan öğeye bir referans, efektlerin tanımı ve diğer şeylerin yanı sıra zamanlamayı içeren seçenekler içeren bir pakettir . Ve işte KeyframeEffect
budur. Kaset kasetimiz, tüm kayıtları ve kayıtların uzunluğu hakkında bilgileri tutan bir şeydir. Tüm bu özellikleri fiziksel bir kasetin bileşenleriyle eşleştirmeyi daha yaşlı izleyicilerin hayal gücüne bırakacağım. Size göstereceğim şey kodda nasıl göründüğü.
Element.animate
aracılığıyla bir animasyon oluşturduğunuzda, üç şey yapan bir kısayol kullanırsınız. Bir KeyframeEffect
örneği oluşturur. Yeni bir Animation
örneğine girer. Hemen oynamaya başlar.
const animation = element.animate(keyframes, options);
Onu parçalayalım ve aynı şeyi yapan eşdeğer kodu görelim.
const animation = new Animation( // (2) new KeyframeEffect(element, keyframes, options) // (1) ); animation.play(); (3)
Kaseti (1) alın, bir oynatıcıya (2) yerleştirin, ardından Oynat düğmesine (3) basın.
Sahne arkasında nasıl çalıştığını bilmenin amacı, ana karelerin tanımını ayırabilmek ve ne zaman oynatılacağına karar vermektir. Koordine edilecek çok sayıda animasyonunuz olduğunda, oynamaya hazır olduklarını bilmeniz için önce hepsini toplamanız yararlı olabilir. Onları anında oluşturmak ve doğru zamanda oynamaya başlayacaklarını ummak, ummak isteyeceğiniz bir şey değildir. İstenilen efekti birkaç kare sürükleyerek kırmak çok kolaydır. Sürükleyen uzun bir dizi durumunda birikir ve sonuçta hiç inandırıcı bir deneyim olmaz.
Zamanlama
Komedide olduğu gibi, animasyonlarda da zamanlama her şeydir. Bir efektin işe yaraması için, belirli bir hissi elde etmek için özelliklerin değişme biçimine ince ayar yapabilmeniz gerekir. Web Animations API'de kontrol edebileceğiniz iki zamanlama düzeyi vardır.
Bireysel özellikler düzeyinde, offset
var. Offset, size tek özellik zamanlaması üzerinde kontrol sağlar. Ona sıfırdan bire bir değer vererek her efektin ne zaman başlayacağını tanımlarsınız. Atlandığında sıfıra eşittir.
@keyframes
/ from
yerine yüzdeleri to
kullanabileceğinizi hatırlayabilirsiniz. offset
yüze bölünmesiyle elde edilen şey budur. offset
değeri, tek bir yineleme süresinin bir kısmıdır .
offset
, ana kareleri bir KeyframeEffect
içinde düzenlemenize olanak tanır. Göreceli bir sayı ofseti olmak, oynatma süresi veya hızı ne olursa olsun, tüm ana karelerinizin birbirine göre aynı anda başlamasını sağlar.
Daha önce de belirttiğimiz gibi, offset
, sürenin bir parçasıdır . Şimdi bu konudaki hatalarımdan ve zaman kaybımdan kaçınmanızı istiyorum. Animasyon süresinin, bir animasyonun genel süresiyle aynı şey olmadığını anlamak önemlidir. Genellikle aynıdırlar ve kafanızı karıştırabilecek olan da budur ve kesinlikle kafamı karıştıran da budur.
Süre , bir yinelemenin tamamlanması için geçen milisaniye cinsinden süredir . Varsayılan olarak toplam süreye eşit olacaktır. Bir animasyon süresinde bir gecikme eklediğinizde veya yineleme sayısını artırdığınızda, size bilmek istediğiniz sayıyı söylemeyi bırakır. Bunu kendi yararınıza kullanmayı anlamak önemlidir.
Medya oynatma gibi daha büyük bir bağlamda bir ana kare oynatmayı koordine etmeniz gerektiğinde, zamanlama seçeneklerini kullanmanız gerekir. Aşağıdaki denklemde animasyonun başından "bitmiş" olaya kadar olan tüm süresi:
delay + (iterations × duration) + end delay
Aşağıdaki demoda çalışırken görebilirsiniz:
Bunun yapmamıza izin verdiği şey, birkaç animasyonu sabit uzunluklu medya bağlamında hizalamaktır . İstenen animasyon süresini olduğu gibi koruyarak, daha uzun süreli bir bağlama gömmek için başlangıçta gecikme ve sonunda delay
Sonu ile " delayEnd
". Bunu düşünürseniz, delay
bu anlamda ana karelerde ofset gibi davranır. Gecikmenin milisaniye cinsinden ayarlandığını unutmayın, bu nedenle onu göreceli bir değere dönüştürmek isteyebilirsiniz.
Animasyonu hizalamaya yardımcı olacak bir diğer zamanlama seçeneği iterationStart
. Bir yinelemenin başlangıç konumunu ayarlar. Bilardo topu demosuna katılın. iterationStart
Başlat kaydırıcısını ayarlayarak topun başlangıç konumunu ve dönüşünü ayarlayabilirsiniz, örneğin ekranın ortasından atlamaya başlayacak şekilde ayarlayabilir ve son karede kameradaki sayının düz olmasını sağlayabilirsiniz.
Birkaçını Bir Olarak Kontrol Edin
Bir sunum uygulaması için animasyon düzenleyici üzerinde çalışırken, bir zaman çizelgesindeki tek bir öğe için birkaç animasyon düzenlemem gerekiyordu. İlk denemem, animasyonumu bir zaman çizelgesinde doğru başlangıç noktasına koymak için offset
kullanmaktı.
Bunun offset
kullanmanın yanlış yolu olduğu çabucak kanıtlandı. Bu özel kullanıcı arayüzü açısından, zaman çizelgesindeki hareketli animasyon, animasyonun süresini değiştirmeden başlangıç konumunu değiştirmek anlamına geliyordu. offset
ile bu, sürenin değişmediğinden emin olmak için birkaç şeyi, offset
kendisini ve ayrıca kapanış özelliğinin offset
değiştirmem gerektiği anlamına geliyordu. Çözümün kavranamayacak kadar karmaşık olduğu ortaya çıktı.
İkinci sorun transform
özelliği ile geldi. Bir öğede çeşitli karakteristik değişiklikleri temsil edebilmesi nedeniyle, istediğiniz şeyi yapmasını sağlamak zor olabilir. Bu özellikleri birbirinden bağımsız olarak değiştirmek istendiğinde daha da zorlaşabilir. Ölçek fonksiyonunun değişimi, onu takip eden tüm fonksiyonları etkiler. İşte bu yüzden oluyor.
Transform özelliği, bir dizide bir değer olarak birkaç işlevi alabilir . İşlev sırasına bağlı olarak sonuç değişir. scale
alın ve translate
edin. Bazen translate
bir öğenin boyutuna göre yüzde olarak tanımlamak kullanışlıdır. Diyelim ki bir topun tam olarak üç kendi çapı kadar yükseğe zıplamasını istiyorsunuz. Şimdi, ölçek işlevini nereye yerleştirdiğinize bağlı olarak - translate
önce veya sonra - sonuç, orijinal boyutun veya ölçeklenmiş olanın üç yüksekliğinden değişir.
transform
özelliğinin önemli bir özelliğidir. Oldukça karmaşık bir dönüşüm elde etmek için buna ihtiyacınız var. Ancak bu dönüşümlerin farklı ve bir öğenin diğer dönüşümlerinden bağımsız olmasına ihtiyaç duyduğunuzda, yolunuza çıkar.
Tüm efektleri tek bir transform
özelliğine koyamayacağınız durumlar vardır. Çok hızlı bir şekilde çok fazla olabilir. Özellikle ana kareleriniz farklı yerlerden geliyorsa , dönüştürülmüş bir dizenin çok karmaşık bir birleşimine ihtiyacınız olacaktır. Mantık basit olmadığı için otomatik bir mekanizmaya pek güvenemezsiniz. Ayrıca, ne bekleyeceğinizi anlamak zorlaşabilir. Bunu basitleştirmek ve esnekliği korumak için bunları farklı kanallara ayırmamız gerekiyor.
Bir çözüm, öğelerimizi, her biri ayrı ayrı canlandırılabilen div
s'ye sarmaktır ; örneğin, tuval üzerine konumlandırmak için bir div, ölçekleme için bir diğeri ve döndürme için üçüncü bir div. Bu şekilde, yalnızca animasyonların tanımını büyük ölçüde basitleştirmekle kalmaz, aynı zamanda uygun olduğunda farklı dönüşüm kökenleri tanımlama olasılığını da açarsınız.
Bu numarayla işler kontrolden çıkmış gibi görünebilir. Daha önce yaşadığımız sorunların sayısını çarpıyoruz. Aslında, bu numarayı ilk bulduğumda çok fazla olduğu için attım. transform
özelliğimin tüm parçalardan tek parça halinde doğru sırayla derlendiğinden emin olabileceğimi düşündüm. İşleri yönetmek için çok karmaşık ve bazı şeyleri imkansız kılmak için bir transform
işlevi daha aldı. transform
özelliği dize derleyicimin doğru olması gittikçe daha fazla zaman almaya başladı, bu yüzden pes ettim.
Birkaç animasyonun oynatılmasını kontrol etmenin başlangıçta göründüğü kadar zor olmadığı ortaya çıktı. En başından beri kaset çalar benzetmesini hatırlıyor musunuz? Herhangi bir sayıda kaset alan kendi oynatıcınızı kullanabilseydiniz ne olurdu? Dahası, o oynatıcıya istediğiniz kadar düğme ekleyebilirsiniz.
Tek bir animasyonda play
çağırmak ile bir dizi animasyonu çağırmak arasındaki tek fark, yinelemeniz gerekmesidir. Herhangi bir Animation
örneği yöntemi için kullanabileceğiniz kod:
// To play just call play on all of them animations.forEach((animation) => animation.play());
Bunu, oynatıcımız için her türlü işlevi oluşturmak için kullanacağız.
Animasyonları tutacak ve oynatacak o kutuyu oluşturalım. Bu kutuları uygun olan herhangi bir şekilde oluşturabilirsiniz. Açıklığa kavuşturmak için, size bunu bir fonksiyon ve bir nesne ile yapmanın bir örneğini göstereceğim. createPlayer
işlevi, senkronize olarak oynatılacak bir dizi animasyonu alır. Tek bir play
yöntemiyle bir nesne döndürür.
function createPlayer(animations) { return Object.freeze({ play: function () { animations.forEach((animation) => animation.play()); } }); }
İşlevselliği genişletmeye başlamak için bilmeniz yeterlidir. Duraklatma ve currentTime
yöntemlerini ekleyelim.
function createPlayer(animations) { return Object.freeze({ play: function () { animations.forEach((animation) => animation.play()); }, pause: function () { animations.forEach((animation) => animation.pause()); }, currentTime: function (time = 0) { animations.forEach((animation) => animation.currentTime = time); } }); }
Bu üç yöntemle createPlayer
, istediğiniz sayıda animasyonu düzenlemek için size yeterli kontrol sağlar . Ama hadi biraz daha ileri itelim. Oyuncumuzun sadece herhangi bir sayıda kaseti değil, diğer oyuncuları da alabilmesi için yapalım.
Daha önce gördüğümüz gibi, Animation
arayüzü medya arayüzlerine benzer. Bu benzerliği kullanarak oynatıcınıza her türlü şeyi koyabilirsiniz. Buna uyum sağlamak için, hem animasyon nesneleri hem de createPlayer
gelen nesnelerle çalışmasını sağlamak için currentTime
yöntemini ince ayar yapalım.
function currentTime(time = 0) { animations.forEach(function (animation) { if (typeof animation.currentTime === "function") { animation.currentTime(time); } else { animation.currentTime = time; } }); }
Az önce oluşturduğumuz oynatıcı, tek elemanlı animasyon kanalları için birkaç div
karmaşıklığını gizlemenize izin verecek olan şeydir. Bu unsurlar bir sahnede gruplandırılabilir. Ve her sahne daha büyük bir şeyin parçası olabilir. Bu teknikle her şey yapılabilir.
Zamanlama demosunu göstermek için tüm animasyonları üç oyuncuya böldüm. Birincisi, sağdaki önizlemenin oynatılmasını kontrol etmektir . İkincisi, tüm topların ana hatlarının soldaki ve önizlemedekinin atlama animasyonunu birleştirir.
Son olarak, üçüncüsü, topların pozisyon animasyonlarını sol bir kapta birleştiren bir oyuncu. Bu oyuncu, saniyede yaklaşık 60 kare ile animasyonun sürekli bir gösteriminde topların yayılmasını sağlar.
Çözüm
Web Animasyonları API'si gibi web arayüzleri, bizim için tarayıcıların başından beri yaptığı bazı şeyleri ortaya çıkarır. Tarayıcılar, işi GPU'ya aktararak nasıl hızlı oluşturulacağını bilir. Web Animations API ile kontrol bizde. Bu kontrol biraz yabancı veya kafa karıştırıcı görünse de, onu kullanmanın da kafa karıştırıcı olması gerektiği anlamına gelmez. Zamanlama ve oynatma kontrolü anlayışıyla, bu API'yi ihtiyaçlarınıza göre evcilleştirecek araçlara sahip olursunuz. Ne kadar karmaşık olması gerektiğini tanımlayabilmelisiniz.
Daha fazla okuma
- “Animasyon Tasarlamada Pratik Teknikler,” Sarah Drasner
- “Hareket Hassasiyetleri İçin Azaltılmış Hareketle Tasarım” Val Head
- “Sesli Asistanlara Alternatif Bir Ses Kullanıcı Arayüzü,” Ottomatias Peura
- “Mobil Kullanıcı Arayüzleri İçin Daha İyi Araç İpuçları Tasarlamak,” Eric Olive