Blog
Hareketli SVG ve Web Sitelerine Kazandırdığı Güzellikler
- Ayrıntılar
- Kategori: Bilişim
- 12 Kasım 2015 tarihinde yayınlandı.
- THEMT tarafından yazıldı.
SVG Teknolojisinin gücü, esnekliği ve küçük hacmi Web Sitelerine kattığı renklilik açısından inanılmaz! Bir de hareketlilik kazandırılırsa eğer; gerçekten de ortam Üniversitelerin Bahar şenliklerini aratmayacaktır!
Öncelikle şunu belirteyim bu yazı aslında; çeviriyle karışık yorumlamadır. Yazının aslına tympanus.net adresinde denk geldim ve Luis Manuel tarafından yazılmıştır.
Yazıyı beğendim, çünkü; ilerleyen günlerde, web camiasında, SVG ve teknolojileri çok ilginç ve güzel şekillerde karşımıza çıkmaya başlayacaktır. Şimdiden olayı öğrenmek ve bir şeyler yapmaya başlamak gerek. Bu sebeple faydalı olması adına yazıyı çevirdim. Tabi yazıyı bire-bir çeviremezdim, içinde Web Geliştirmeyle ilgili bazı kavramlar var, bunların daha anlaşılabilir olması için yorumlamam gerekti.
Yazar sahibi bu ilginç etkili (efektli) menü simgesi yazısını paylaştığı için çok mutlu ya da menü düğmesi diyelim, Yazının arkasındaki fikir Dribble’ın Shot Hamburger Menüsüne dayanıyor. Fakat farkı şu ki, tıklamaya kalkıştığınızda bir tür eğlenceli “Ninja” etkisiyle (efektiyle) kapanış halini alıyor. Kapatmak istediğinizde ise animasyon ters şekilde “Hamburger Simgesi”ne geri dönüyor. Aşağıdaki görseldeki gibi:
Bu derste etkiyi (efekti); SVG ve Segment kütüphanesi adı verilen yeni bir kütüphaneyi kullanarak oluşturacağız. Öncelikle bir-takım hazırlık planı yapmak gerekiyor. Bittikten sonra Segment aşamasına geçebiliriz ve sonrasında ise Hamburger simgesinin çizimi yapıp hareket ettirebileceğiz.
Planlama
Bu etkiyi yapabilmek için, SVG kullanımından daha iyi bir şey düşünülemez ve tabi ki yeni Segment kütüphanesi… Bu kütüphaneye alternatif olarak GSAP’ın DrawSVGPlugin kütüphanesi kullanılabilir.
Ana fikir şu; simgedeki üç yatay çizginin, hareket yoluyla birlikte oluşturulması ki bu hareket yolu aslında kapanma eyleminde kullanılacak güzergâh, ya da kapanma eylemindeki hareketin kendisi.
Segment kütüphanesi ise; SVG yollarında bulunan (path) çizgilerini hareket ettirmemizi sağlayacak. Böylece SVG yolundaki çizgiyi istediğimiz şekilde hareket ettirebileceğiz. Bu kısım biraz karışık olmuş olabilir. Yol, çizgi vs. hep aynı anlammış gibi duruyor ama değil :)
Yol çizimlerini herhangi bir vektör tabanlı programda yapabilirsiniz. Mesela Adobe Illustrator (Favorimdir, tek geçerim!) ya da ücretsiz olan Inkscape. Ama bu çalışmada elle oluşturulacak, böylece olabilecek en iyi doğruluk elde edilmiş olacak. Bu kısımda yazara katılmıyorum! Sanırım vektör tabanlı program yeteneği yeterli seviyede değil, bu yüzden de çizimin yeterli olmayacağı fikrinde… ki alakası yok, çok da başarılı çizilebilir.
Unutmamanız gereken şey de şu; hareket zamanı (easing/timing) olarak “elastik” kullanılacak, bu sebeple yolların uzunlukları önemli. Devam etmeden önce Segment kütüphanesine bir göz atalım.
Segment’e Giriş
SVG hareketlendirme için ana aracımız Segment olacak ve birazcık JavaScript sınıfı. Segment’i kullanmak gerçekten basit:
<!--Segment betiğinin eklenmesi (<2kb) --> <script src="/dist/segment.min.js"></script> <!-- SVG yolunun tanımlanması --> <svg> <path id="my-path" ...> </svg> <script> // SVG yoluyla birlikte Segment sınıfının tanımlanması var myPath = document.getElementById("my-path"), segment = new Segment(myPath); // Görmek istediğiniz konumdaki çizgi-segmentinin belirlenmesi // Yazımdili: .draw(başlangıç, bitiş[, süreç(duration), ayarlar]) segment.draw("25%", "75% - 10", 1); /* Buraya bir-takım kodlar gelecek */ // Stanadrt bir zamanlama (easing) fonksiyonun tanımlanması (t parametresi 0 ila 1 aralığında olmalı) function cubicIn(t) { return t * t * t; } // Geri-çağırma (callback) fonksiyonunun tanımlanması function done() { alert("Done!"); } // Yolun çizilmesi segment.draw(0, "100%", 1, {delay: 1, easing: cubicIn, callback: done}); </script>
Çizim
Çok hızlı bir animasyon olacak, eğer animasyonu kare-kare irdelersek tüm yolları çizebiliriz. Sonuç aşağıdaki gibi bir şey olacaktır:
Aşağıdaki kod yazar tarafından parça-parça oluşturuldu. (Giderek yazar hakkında kötü düşüncelerim artıyor :D)
<svg width="100px" height="100px"> <path d="M 30 40 L 70 40 C 90 40 90 75 60 85 A 40 40 0 0 1 20 20 L 80 80"></path> <path d="M 30 50 L 70 50"></path> <path d="M 70 60 L 30 60 C 10 60 10 20 40 15 A 40 38 0 1 1 20 80 L 80 20"></path> </svg>
Sıra şimdi de CSS kullanarak SVG yollarına kişilik kazandırmakta. Yollara ID belirlersek eğer erişim ve tanımlama işlemleri daha kolay olacaktır. HTML yapısı aşağıdaki gibidir:
<!-- Olay-Yeri (Wrapper) --> <div id="menu-icon-wrapper" class="menu-icon-wrapper"> <!-- Mevcut yollarıyla beraber SVG elemanı --> <svg width="100px" height="100px"> <path id="pathA" d="M 30 40 L 70 40 C 90 40 90 75 60 85 A 40 40 0 0 1 20 20 L 80 80"/> <path id="pathB" d="M 30 50 L 70 50"/> <path id="pathC" d="M 70 60 L 30 60 C 10 60 10 20 40 15 A 40 38 0 1 1 20 80 L 80 20"/> </svg> <!-- Animasyon tetikleyicisi --> <button id="menu-icon-trigger" class="menu-icon-trigger"></button> </div>
Ve sahnede CSS:
// Olay-yeri, sabit boyutlarda tanımlanmıştır // Not; pointer-events özelliği ‘none’ olarak belirtilmiştir. // Bu olay içindeki HTML elemanlarında pointer-events’e ihtiyacımız olmayacak. .menu-icon-wrapper{ position: relative; display: inline-block; width: 34px; height: 34px; pointer-events: none; transition: 0.1s; } // ikinci örnek için ölçekleme. .menu-icon-wrapper.scaled{ transform: scale(0.5); } // SVG elemanını konumlandırma. .menu-icon-wrapper svg{ position: absolute; top: -33px; left: -33px; } // Yol elemanları için CSS tanımı .menu-icon-wrapper svg path{ stroke: #fff; stroke-width: 6px; stroke-linecap: round; fill: transparent; } // pointer-events özelliğini sadece tektikleyici için ‘auto’ olarak tanımladık .menu-icon-wrapper .menu-icon-trigger{ position: relative; width: 100%; height: 100%; cursor: pointer; pointer-events: auto; background: none; border: none; margin: 0; padding: 0; }
Hareket
SVG kodunun hazır olmasıyla birlikte, yeni görevimiz; animasyon bölümlerinde kullanılan zamanlama (easing) fonksiyonlarını belirlemek ve düzgün bir eşzamanlı harekete ulaşmak. Yani yazının başındaki GIF görselinde olduğu gibi.
Şimdide; Hamburger menüsündeki üst ve alt barları nasıl hareket ettirdiğimize bakalım. Öncelikle; başlangıç ve bitiş değerleriyle birlikte tüm segmentleri hazır etmemiz gerekiyor ve nereden başlayacağımızla ilgili elimizde hiçbir veri yok, tek verimiz GIF görselindeki animasyon. Doğru değerleri bulmak için birazcık deneme-yanılma yapmamız gerekiyor.
var pathA = document.getElementById('pathA'), pathC = document.getElementById('pathC'), segmentA = new Segment(pathA, 8, 32), segmentC = new Segment(pathC, 8, 32);
Hareket ettirmeye hazırız! Hareket süresince aynı uzunluk değerlerini kullanmaya devam edin (bitiş – başlangıç = 24) Hareket sırasını irdelersek eğer, ilk hareketin düz bir zamanlama ile başladığını görüyoruz ve tabi ki elastiki şekilde bir bitişle birlikte. Bunun için üst ve alt barlarda aynı fonksiyonu kullanacağız çünkü bu ikisi aynı tip zamanlamaya sahip.
// Düz zamanlamaya sahip kısım, ve bir sonraki geri-çağırım function inAC(s) { s.draw('80% - 24', '80%', 0.3, {delay: 0.1, callback: function(){ inAC2(s) }}); } // Elastiki zamanlamaya sahip kısım, elastic-out tipinde function inAC2(s) { s.draw('100% - 54.5', '100% - 30.5', 0.6, {easing: ease.ease('elastic-out', 1, 0.3)}); } // Hareketlerin oynatılması! inAC(segmentA); // üst bar inAC(segmentC); // alt bar
Şimdi de ihtiyacımız olan; aynı işlemin orta bar için tekrar edilmesi:
// Hazırlık var pathB = document.getElementById('pathB'), segmentB = new Segment(pathB, 8, 32); // Barın birazcık uzatılması function inB(s) { s.draw(8 - 6, 32 + 6, 0.1, {callback: function(){ inB2(s) }}); } // ve sıçrama etkisiyle (bounce effect’le) birlikte barın küçülmesi function inB2(s) { s.draw(8 + 12, 32 - 12, 0.3, {easing: ease.ease('bounce-out', 1, 0.3)}); } // Hareketi oynat! inB(segmentB);
Hareketi, hamburger menü simgesine tekrar döndürmek için kullanacağımız yapı:
function outAC(s) { s.draw('90% - 24', '90%', 0.1, {easing: ease.ease('elastic-in', 1, 0.3), callback: function(){ outAC2(s) }}); } function outAC2(s) { s.draw('20% - 24', '20%', 0.3, {callback: function(){ outAC3(s) }}); } function outAC3(s) { s.draw(8, 32, 0.7, {easing: ease.ease('elastic-out', 1, 0.3)}); } function outB(s) { s.draw(8, 32, 0.7, {delay: 0.1, easing: ease.ease('elastic-out', 2, 0.4)}); } // Hareketleri oynat! outAC(segmentA); outB(segmentB); outAC(segmentC);
Son olarak; tüm bu hareketleri sadece tek tıklamayla birlikte başlatmak için yapılması gereken:
var trigger = document.getElementById('menu-icon-trigger'), toCloseIcon = true; trigger.onclick = function() { if (toCloseIcon) { inAC(segmentA); inB(segmentB); inAC(segmentC); } else { outAC(segmentA); outB(segmentB); outAC(segmentC); } toCloseIcon = !toCloseIcon; };
Yapmak istediğimiz tamamdır! Yani mission complete successfuly :) ama hala ufak bir sorunumuz var. Tüm tarayıcılarda aynı şekilde görülmeyecektir. Farklı tarayıcılarda yol uzunlukları farklı şekilde hesaplanacaktır. Peki, nasıl çözeceğiz? Aslında çözüm çok basit!: Hızlıca kendi SVG uzatıcımızı yapacağız, böylece yollar daha uzun olacak. Bunu yapmamızla birlikte boyutlandırma ya da ölçekleme işlemleri istediğimiz şekilde olacak. Şu aşamada, SVG çizimini x10 kat daha büyük yapacağız:
<svg width="1000px" height="1000px"> <path id="pathA" d="M 300 400 L 700 400 C 900 400 900 750 600 850 A 400 400 0 0 1 200 200 L 800 800"></path> <path id="pathB" d="M 300 500 L 700 500"></path> <path id="pathC" d="M 700 600 L 300 600 C 100 600 100 200 400 150 A 400 380 0 1 1 200 800 L 800 200"></path> </svg>
Daha sonra ise; CSS kullanarak asıl boyutumuza geri ölçekleyeceğiz:
.menu-icon-wrapper svg { transform: scale(0.1); transform-origin: 0 0; }
Not, ayrıca JavaScript kodundaki kesirli (float) değerlerini de 10la çarparak arttırmamız gerek ve tabi ki CSS içindeki stroke-with değerini de ayarlamak gerekecektir.