CSS transition, bir özelliğin değeri değiştiğinde yeni değerin bir anda uygulanması yerine belirli bir süreye yayılarak uygulanmasını sağlar. Örneğin bir düğmenin arka planı fareyle üzerine gelindiğinde beyazdan siyaha anında atlamak yerine yarım saniye içinde yumuşakça koyulaşır. Hangi özelliğin geçiş yapacağını, ne kadar süreceğini, ne kadar gecikmeyle başlayacağını ve hızın zaman içinde nasıl değişeceğini siz belirlersiniz; aradaki kareleri tarayıcı kendisi hesaplar.

Bu yazıda transition kısayolunu ve beş alt özelliğini, farklı uzunluktaki değer listelerinin nasıl eşleştiğini, ease, cubic-bezier(), steps() ve linear() zamanlama fonksiyonlarını, display: none ile gizlenen öğelere giriş ve çıkış geçişi vermek için gereken transition-behavior: allow-discrete ile @starting-style kuralını MDN belgelerine dayanarak anlatıyoruz. Birden fazla ara adım, döngü ya da kendiliğinden başlayan hareket gerekiyorsa doğru araç geçiş değil animasyondur; o konu @keyframes ve animation yazısında işleniyor.

Geçiş ne zaman devreye girer?

MDN, iki durum arasında gerçekleşen bu tür hareketlere örtük geçiş (implicit transition) der. Başlangıç ve bitiş durumunu siz yazarsınız, aradaki durumları ise tarayıcı türetir. Bu yüzden bir geçişin çalışması için iki şey gerekir: öğenin üzerinde bir transition tanımı ve özelliğin değerini sonradan değiştiren bir olay. Değişikliği bir sözde sınıf (:hover, :focus, :active gibi), JavaScript ile eklenen bir class ya da bir medya sorgusunun eşleşmeye başlaması tetikleyebilir.

Geçiş kendiliğinden başlamaz ve tekrar etmez. Değer bir kez değişir, tarayıcı eski değerden yenisine süre boyunca ilerler ve durur. Değer geri döndüğünde geçiş ters yönde yeniden çalışır. Sayfa açılır açılmaz başlayan, sonsuz dönen ya da üç ara durumdan geçen bir hareket istiyorsanız bunu geçişle değil @keyframes ile kurarsınız.

css
.dugme {
  background-color: #cccccc;
  transition-property: background-color;
  transition-duration: 1s;
}

.dugme:hover,
.dugme:focus {
  background-color: #eeeeee;
  /* sonuç: arka plan 1 saniyede açık griye döner */
}

Geçiş tanımının durum değişikliğini yaşayan kuralda değil, öğenin temel kuralında olduğuna dikkat edin. Böylece hem fare üzerine geldiğinde hem de ayrıldığında aynı tanım kullanılır ve hareket iki yönde de yumuşak olur.

transition kısayolu ve beş alt özelliği

transition kısayolu ve beş alt özelliği: transition-property, transition-duration, transition-timing-function, transition-delay

Geçişler beş alt özellikle kontrol edilir. MDN, değerlerin birbiriyle uyumsuz hale gelmesini önlediği için çoğu durumda transition kısayolunun kullanılmasını önerir. Yine de her alt özelliğin ne yaptığını bilmek, kısayolun nasıl ayrıştırıldığını anlamak için gereklidir.

ÖzellikNe belirlerMDN notu
transition-propertyHangi özelliklerin geçiş yapacağıKısayolda yazılmazsa all varsayılır
transition-durationGeçişin ne kadar süreceğiVarsayılan 0s, yani geçiş yok
transition-timing-functionAra değerlerin hangi hız eğrisiyle hesaplanacağıVarsayılan ease
transition-delayDeğer değiştikten sonra ne kadar beklenip başlanacağıKısayolda yazılmazsa 0s
transition-behaviorAyrık (discrete) özelliklerde geçiş başlatılıp başlatılmayacağıallow-discrete ya da normal

transition-duration varsayılan olarak 0s olduğu için yalnızca özellik adını yazmak hiçbir şey değiştirmez: süre sıfırsa değişiklik anında olur. Negatif bir süre ise bildirimi geçersiz kılar ve tarayıcı o satırı yok sayar. transition-property için none hiçbir özelliğin geçiş yapmayacağını, all geçiş yapabilen tüm özelliklerin yapacağını söyler. Bir kısayol özellik adı (örneğin background) yazarsanız onun canlandırılabilen tüm alt özellikleri geçişe dahil olur.

Kısayolda her tek özellik geçişi virgülle ayrılır ve en fazla bir özellik adı, bir zamanlama fonksiyonu, iki zaman değeri ve bir davranış anahtar sözcüğü içerir. Sıra konusunda tek kural zaman değerleriyle ilgilidir: Zaman olarak ayrıştırılabilen ilk değer süreye, ikinci değer gecikmeye atanır. Özellik adını yazmazsanız all varsayılır. Kısayolun tamamı için özel değer none, yani hiç geçiş olmamasıdır.

css
.kutu {
  /* özellik | süre */
  transition: margin-right 4s;
}

.kutu-2 {
  /* özellik | süre | zamanlama | gecikme */
  transition: margin-right 4s ease-in-out 1s;
}

.kutu-3 {
  /* iki ayrı özellik, iki ayrı süre */
  transition:
    margin-right 4s,
    color 1s;
}

.kutu-4 {
  /* özellik adı yok: all varsayılır; 200ms süre, 50ms gecikme */
  transition: 200ms linear 50ms;
}

Bu ayrıştırma kuralı sık yapılan bir hatanın kaynağıdır. transition: opacity 0.3s 1s; yazan biri bunu "1 saniye sürsün" diye okuyabilir; oysa ilk zaman değeri süre olduğundan geçiş 0,3 saniye sürer ve 1 saniye gecikmeyle başlar. Tek bir zaman değeri yazdığınızda o her zaman süredir.

all ile tek tek özellik adlarını birlikte kullanabilirsiniz. Listede all önce gelir ve ardından belirli bir özellik yazılırsa, sonraki tanım o özellik için öncekini geçersiz kılar:

css
.kart {
  transition:
    all 200ms,
    opacity 400ms;
  /* sonuç: değişen her özellik 200ms, yalnızca opacity 400ms sürer */
}

Değer listeleri farklı uzunlukta olursa

Alt özellikleri ayrı ayrı yazdığınızda transition-property ana liste görevi görür. Süre, gecikme ve zamanlama listeleri bu ana listeyle sırayla eşleştirilir. Bir liste kısa kalırsa tarayıcı onu baştan tekrar ederek uzatır; uzun gelirse fazlasını keser. İki durumda da bildirim geçerli kalır, yani hata vermez.

css
div {
  transition-property: opacity, left, top, height;
  transition-duration: 3s, 5s;
  /* tarayıcının yorumu: 3s, 5s, 3s, 5s
     opacity 3s | left 5s | top 3s | height 5s */
}

div.ikinci {
  transition-property: opacity, left;
  transition-duration: 3s, 5s, 2s, 1s;
  /* tarayıcının yorumu: 3s, 5s (fazlası atılır) */
}

Bu tekrar kuralı kullanışlıdır ama okunabilirliği düşürebilir. Üç ayrı özelliğe üç ayrı süre vermek istiyorsanız kısayolu virgüllü biçimde yazmak, hangi sürenin hangi özelliğe ait olduğunu satır satır gösterir ve sonradan bir özellik eklendiğinde eşleşmenin kaymasını önler.

Hangi özellikler geçiş yapabilir?

Hangi özellikler geçiş yapabilir? Canlandırılamaz, Ayrık (discrete), Hesaplanan değere göre, Tekrarlanabilir liste

MDN'ye göre aksi belirtilmedikçe tüm CSS özellikleri canlandırılabilir; ancak nasıl canlandırılacakları özelliğin animasyon türüne bağlıdır. Her özelliğin MDN sayfasındaki "Formal definition" tablosunda bu tür yazar. Geçişler yalnızca enterpolasyon, yani iki değer arasında ara değer hesaplama yapar.

Animasyon türüDavranış
CanlandırılamazGeçiş bu özelliği hiç etkilemez
Ayrık (discrete)Ara değer yoktur; ilerlemenin yüzde 50 noktasında başlangıç değerinden bitiş değerine atlar
Hesaplanan değere göreDeğerin bileşenleri ayrı ayrı ara değerlerle hesaplanır (renk, uzunluk gibi)
Tekrarlanabilir listeListe uzunlukları en küçük ortak katlarına tamamlanır, sonra her öğe ayrı hesaplanır

Hesaplanan değere göre canlanan bir özellikte iki değerin bileşen sayısı ya da türü uyuşmazsa özellik ayrık davranışa düşer. Kayıtsız özel özellikler (--renk gibi) de ayrık canlanır; bu yüzden bir CSS değişkenini doğrudan geçişe sokmak çoğu zaman beklenen yumuşaklığı vermez. Değişkeni bir türle kaydetmek bu durumu değiştirir ve @property yazısında anlatılıyor.

Zamanlama fonksiyonları: hız eğrisini seçmek

transition-timing-function, geçişin süre boyunca hangi hızla ilerleyeceğini bir <easing-function> ile tanımlar. Aynı değer türü animation-timing-function için de kullanılır. Hazır anahtar sözcüklerin her biri sabit dört noktalı bir kübik Bézier eğrisinin kısaltmasıdır:

Anahtar sözcükEşdeğeriHareket
ease (varsayılan)cubic-bezier(0.25, 0.1, 0.25, 1)Yavaş başlar, ortada hızlanır, sonda yavaşlar
linearlinear(0, 1)Baştan sona sabit hız
ease-incubic-bezier(0.42, 0, 1, 1)Yavaş başlar, giderek hızlanır
ease-outcubic-bezier(0, 0, 0.58, 1)Hızlı başlar, giderek yavaşlar
ease-in-outcubic-bezier(0.42, 0, 0.58, 1)Yavaş başlar, hızlanır, sonda yine yavaşlar
step-startsteps(1, jump-start)Hemen bitiş değerine atlar
step-endsteps(1, jump-end)Sona kadar başlangıçta kalır, sonda atlar

Kendi eğrinizi cubic-bezier(x1, y1, x2, y2) ile tanımlarsınız. Buradaki iki kontrol noktasının x koordinatları 0 ile 1 arasında olmak zorundadır; y koordinatları bu aralığın dışına çıkabilir. y değerini 1'in üstüne ya da 0'ın altına taşımak, değerin hedefi kısa süre aşıp geri dönmesine yol açar ve "yaylanma" hissi verir. MDN örneklerinden biri cubic-bezier(0.2, -2, 0.8, 2) değerini kullanır.

steps(n, konum) süreyi n eşit parçaya böler ve değeri yumuşak değil kademeli değiştirir. İkinci parametre atlamanın ne zaman olacağını belirler ve yazılmazsa end kabul edilir. n değeri 0'dan büyük bir tam sayı olmalıdır; jump-none kullanıldığında en az 2 olmalıdır.

  • jump-start (ya da start): İlk atlama geçiş başlar başlamaz olur.
  • jump-end (ya da end): Son atlama geçiş biterken olur.
  • jump-none: İki uçta da atlama olmaz; değer hem yüzde 0 hem yüzde 100 noktasında süre/n kadar bekler.
  • jump-both: Hem başta hem sonda bekleme olur; bu da fazladan bir adım ekler.

steps(5, ...) için MDN'nin verdiği tabloyu somutlaştıralım. jump-end ile ilerleme sırasıyla yüzde 0, 20, 40, 60 ve 80 değerlerinde bekler; jump-start ile yüzde 20, 40, 60, 80 ve 100 değerlerinde bekler. jump-none iki ucu da adım sayar ve ilerleme yüzde 0, 25, 50, 75 ve 100 değerlerinde bekler; jump-both ile beş durağın hepsi iki ucun arasında kalır. Saat ibresi ya da daktilo efekti gibi kademeli hareketlerde steps() doğal sonuç verir.

linear() fonksiyonu birden fazla ilerleme noktası arasında doğrusal geçiş yapar ve karmaşık eğrileri çok sayıda nokta ile yaklaşık olarak çizmenize izin verir. En az iki sayı gerekir; 0 başlangıcı, 1 sonu temsil eder. Bir sayının ardından yazılan yüzde, o ilerlemeye zamanın hangi noktasında ulaşılacağını söyler. Yüzde yazılmazsa noktalar eşit aralıklarla dağıtılır.

MDN'deki hesap örneğini adım adım izleyelim: 100 saniye süren ve 100 piksellik bir değişim yapan bir hareketin zamanlaması linear(0, 0.25 75%, 1) olsun. 0,25 ilerlemeye sürenin yüzde 75'inde ulaşılır: 100 x 0,75 = 75 saniyede 100 x 0,25 = 25 piksel yol alınır. Kalan 100 - 25 = 75 piksel, kalan 100 - 75 = 25 saniyede tamamlanır. Yani hareket uzun süre yavaş ilerler, sonda hızlanır. linear(0, 0.5 25% 75%, 1) yazılırsa 25. saniyede 50 piksele ulaşılır, 75. saniyeye kadar 75 - 25 = 50 saniye orada beklenir ve son 50 piksel son 25 saniyede alınır.

css
.ibre {
  transition: rotate 2s steps(4, end);
  /* sonuç: dönüş dört kademede, son atlama en sonda */
}

.yay {
  transition: scale 0.6s cubic-bezier(0.2, -2, 0.8, 2);
  /* sonuç: değer hedefi aşıp geri döner */
}

.kademeli {
  transition: translate 1s linear(0, 0.25 75%, 1);
}

MDN linear() için geçersiz örnekler de verir: tek parametre (linear(0.5)), artan sırada olmayan yüzdeler (linear(0, 0.25 80%, 0.5 60%, 1)) ve sayı yerine sözcük kullanmak geçersizdir. linear anahtar sözcüğü ile linear(0, 1) fonksiyonu aynı sonucu verir.

transition-delay: pozitif ve negatif gecikme

Gecikme sıfır, pozitif ya da negatif olabilir. 0s geçişi hemen başlatır, pozitif bir değer verilen süre kadar bekletir. Negatif değer ise ilginç bir şey yapar: Geçiş hemen başlar ama sanki o süre kadar önce başlamış gibi ortasından devam eder. 2 saniyelik bir geçişe -1s gecikme verirseniz hareket yarı yoldan başlar ve 1 saniyede biter.

css
#gecikmeli {
  font-size: 14px;
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}

#gecikmeli:hover {
  font-size: 36px;
  /* sonuç: fare geldikten 2 saniye sonra başlar, 4 saniye sürer */
}

display: none ile giriş ve çıkış geçişleri

Geçişlerin uzun süre çözemediği bir sorun vardı: display: none ile gizlenen bir öğeyi yavaşça soldurarak kaybettirmek ya da görünür yaparken yavaşça belirtmek. display ayrık bir özelliktir ve varsayılan olarak geçişe girmez. Bunu değiştiren iki parça vardır.

Birincisi transition-behavior: allow-discrete. Bu değer ayrık özellikler için de geçiş başlatılmasını sağlar. Normalde ayrık özellikler yüzde 50 noktasında değişir; ancak display: none ve content-visibility: hidden için tarayıcı özel davranır ve öğenin geçiş boyunca görünür kalmasını sağlar: none değerinden block değerine giderken değer yüzde 0 noktasında, block değerinden none değerine giderken yüzde 100 noktasında değişir.

İkincisi @starting-style kuralı. MDN, beklenmedik sonuçları önlemek için geçişlerin bir öğenin ilk stil güncellemesinde ve display değeri none durumundan başka bir değere geçtiğinde varsayılan olarak tetiklenmediğini belirtir. Öğenin "önceki" bir durumu yoktur. @starting-style, geçişin hangi değerlerden başlayacağını tanımlayarak bu boşluğu doldurur.

css
.panel {
  display: none;
  opacity: 0;
  transition:
    opacity 1s,
    display 1s allow-discrete;
}

.panel.acik {
  display: block;
  opacity: 1;
}

/* .panel.acik kuralından SONRA gelmeli */
@starting-style {
  .panel.acik {
    opacity: 0;
  }
}
/* sonuç: açılırken 1 saniyede belirir, kapanırken 1 saniyede solar */

Bu örnekte üç parça birlikte çalışır. display geçiş listesine allow-discrete ile eklendiği için kapanırken öğe solma bitene kadar görünür kalır; aksi halde anında yok olurdu. @starting-style açılış için başlangıç opaklığını verir; o olmadan öğe solmadan aniden belirirdi.

Sıralama burada kritiktir. MDN, @starting-style kuralının asıl kuralla aynı özgüllüğe sahip olduğunu ve bu yüzden asıl kuraldan sonra yazılması gerektiğini vurgular. Önce yazarsanız asıl kural başlangıç stilini ezer ve giriş geçişi görünmez. Aynı sonucu iç içe yazımla da alabilirsiniz; o zaman sıra sorunu ortadan kalkar:

css
.panel.acik {
  display: block;
  opacity: 1;

  @starting-style {
    opacity: 0;
  }
}

MDN bir ayrıntıya daha dikkat çeker: Başlangıç stilleri yalnızca öğe ilk kez çizildiğinde ya da none durumundan görünür bir duruma geçtiğinde kullanılır. Öğe görünür hale geldikten sonra sınıfı kaldırılırsa başlangıç stiline değil, öğenin varsayılan kuralındaki değere geri döner. Yani yönetmeniz gereken üç durum vardır: başlangıç durumu, geçiş yapılan durum ve varsayılan durum. Giriş ve çıkış hareketleri bu yüzden farklı görünebilir.

Popover ve modal dialog gibi üst katmana (top layer) taşınan öğelerde listeye bir de overlay özelliği allow-discrete ile eklenir. Bu, öğenin üst katmandan çıkarılmasını çıkış hareketi bitene kadar erteler. @starting-style yalnızca geçişlerle ilgilidir; aynı etkiyi @keyframes animasyonuyla kurarken bu kurala ihtiyaç duyulmaz.

Erişilebilirlik: hareketi azaltma tercihi

MDN, bazı hareketlerin dikkat eksikliği yaşayanlar için dikkat dağıtıcı olabileceğini, belirli hareket türlerinin vestibüler bozukluğu, epilepsisi ya da migreni olan kişilerde rahatsızlık tetikleyebileceğini belirtir. Kullanıcı işletim sisteminde hareketi azaltma ayarını açtıysa prefers-reduced-motion medya özelliği reduce değerini alır. @media (prefers-reduced-motion) yazmak @media (prefers-reduced-motion: reduce) ile aynıdır.

css
.kart {
  transition:
    translate 0.4s ease-out,
    opacity 0.4s;
}

@media (prefers-reduced-motion: reduce) {
  .kart {
    transition: opacity 0.4s;
    /* sonuç: kayma kalkar, yalnızca solma kalır */
  }
}

Burada geçişi tamamen kapatmak yerine yer değiştirme hareketini kaldırıp opaklık değişimini bırakıyoruz. MDN'nin örneği de aynı yaklaşımı izler: Ölçekleme gibi büyük hareketleri daha sakin bir solma ile değiştirir. Medya sorgusu sözdiziminin ayrıntıları için @media sözdizimi yazısına bakabilirsiniz.

Sık yapılan hatalar

Sık yapılan hatalar: Süreyi yazmamak, Zaman değerlerini karıştırmak, auto değerine geçiş beklemek, display değişikliğini listeye eklememek
  • Süreyi yazmamak: transition-property: opacity; tek başına bir şey yapmaz, çünkü süre varsayılan olarak 0s değeridir.
  • Zaman değerlerini karıştırmak: Kısayoldaki ilk zaman süredir, ikincisi gecikmedir.
  • auto değerine geçiş beklemek: height: 0 değerinden height: auto değerine geçiş belirtimce önerilmez ve tarayıcıya göre farklı sonuç verir.
  • display değişikliğini listeye eklememek: Çıkışta solma görmek istiyorsanız display özelliğini allow-discrete ile geçiş listesine koymanız gerekir.
  • @starting-style kuralını erken yazmak: Asıl kuraldan önce gelirse aynı özgüllük nedeniyle ezilir.
  • Kayıtsız değişkene geçiş vermek: --deger gibi kayıtsız özel özellikler ayrık canlanır ve yüzde 50 noktasında atlar.

JavaScript tarafında geçiş olaylarını (transitionrun, transitionstart, transitionend) dinleyebilirsiniz; MDN, geçiş tamamlanmadan öğe display: none yapılırsa ya da değer yeniden değiştirilirse transitionend olayının tetiklenmediğini not eder. Belirli bir geçişin neden hiç görünmediğini teşhis etmek için CSS sık hatalar ve hata ayıklama yazısı genel bir kontrol listesi sunuyor.

Sık Sorulan Sorular

transition ile animation arasındaki fark nedir?

Geçiş iki durum arasında, bir değer değiştiğinde çalışır ve ara durumları tarayıcı türetir. Animasyon @keyframes ile istediğiniz kadar ara durum tanımlar, kendiliğinden başlayabilir, tekrar edebilir ve yön değiştirebilir. Basit hover ve odak efektleri için geçiş yeterlidir.

transition: all kullanmak sorun yaratır mı?

Sözdizimi açısından geçerlidir ve MDN örneklerinde de kullanılır. Ancak değişen her özellik geçişe girer; ileride eklenen bir özellik de beklenmedik biçimde yavaşça değişebilir. Hangi özelliklerin hareket edeceğini bildiğiniz durumlarda onları tek tek yazmak davranışı öngörülebilir kılar.

Geçişi sayfa yüklenirken çalıştırabilir miyim?

Varsayılan olarak geçişler öğenin ilk stil güncellemesinde tetiklenmez. @starting-style ile başlangıç değerleri tanımlarsanız öğe ilk çizildiğinde de bu değerlerden geçiş yapar. Destek durumu için uyumluluk tablosuna bakın.

Ayrı translate, rotate ve scale özellikleri geçişe girer mi?

Evet. MDN'nin örneklerinde rotate ve scale özellikleri doğrudan transition listesinde kullanılır. Bu özellikler transform yazısında ayrıntılı olarak anlatılıyor.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: CSS öğrenme rehberi · CSS animation ve @keyframes · CSS transform · :hover ve :focus · @property ile canlanan değişkenler · View transitions · display değerleri