Sorunun cevabı; evet yapılır. Sırf bir önceki yazıyla aynı olmasın diye bu başlığı koydum. Javascript ile kendimizce bir drum pad (launchpad falan diye de bilinir) yapacağız. Bonus olarak da yaptığımız drum pad ile şu parçanın nasıl çalınacağını göstereceğim.

Javascript ile Drum Pad yapılır mı?

Evet. Yukarıdaki video konusu tahmin ettiğiniz gibi şakaydı. (Hiç komik değil)

Ama olsun, en azından basit ritimler yapabileceğiniz, yaparken de zevk alacağınız bir uygulama olabilir. Hem oldukça da hoş görünecek...

Uygulamayı yazarken şu yazıdaki mantığın aynısını kullanacağız. O yüzden açıklamalarda çok detaya girmeyeceğim.

Başlamadan önce...

Davul, trampet vs. ikonları bulmak baya bi uzun zamanımı aldı. Bir kaç yerden toparlayıp yarım yamalak bir şeyler oluşturmuşken (hatta shaker ikonuna mikrofon koymayı bile düşündüm) flaticon.com adresinde (hayalimdekileri olmasa da) aradığım ikonları buldum. Sonra indirme sayfasına girdiğimde ne göreyim; istediğiniz ikonları png, svg, psd ya da icon-font formatında indirebiliyorsunuz. Lan nasıl mutlu oldum anlatamam. O ikonların siyah olması, "olum bu ne biçim renk ya" dedikten sonra böyle çılgın indirme seçenekleriyle karşılaşmak... Anlayamazsınız...

Yani sonuç olarak adamlar iyi hizmet veriyor kullanın demeye getiriyorum lafı. Ancak istediğim ikonları indirdikten sonra içinden çıkan html dosyasında, ikonları kullandığımız sayfaya "Font generated by flaticon.com" ibaresini koymamızı istemişler. Ama siz koymayın. Ortamlarda ben ücretli kullanıyorum dersiniz. Kim bilecek?

Uygulama için seçtiğim ikonları indirmek için buraya tıklanması gerektiği hakkında rivayetler olduğu doğrudur.

Şunu da hazır başlamamışken söyleyeyim; drumpadimizin arayüzü aslında basit bi grid yapısından oluşacağı için (CSS kısmını) ilk başta sıfırdan yazmayı düşündüm. Uğraştım, ettim falan. Bi boka benzemedi.

Peki ben ne yaptım? Tabi ki anında Google'a "CSS 3D Buttons"u yapıştırdım. Güzel şeyler vardı, şuradaki hoşuma gitti, kullandım. Yapanın ellerine sağlık.

Javascript Drumpad
Sonuç olarak ortaya böyle bi görüntü çıktı.

Son olarak, uygulamada kullandığım davul seslerini şuradan aldım. Kendinize göre güzel şeyler aramaya koyulabilir ya da benimkini indirebilirsiniz.

Giriş kısmı fazla uzadı galiba. Çok yazıyı gören kaçmasa bari. :(

Arayüz

Önce her zamanki gibi HTML ile arayüzü hazırlayacağız. Biz 4 x 4 'lük bir drum pad oluşturacağımız için HTML kodları aşağıdaki gibi olacak.

 <div class="drumpad">
  <span class="pad" data-drum="kick1" data-shortcut="Z"><i class="flaticon-bass4"></i></span>
  <span class="pad" data-drum="kick2" data-shortcut="A"><i class="flaticon-bass4"></i></span>
  <span class="pad" data-drum="kick3" data-shortcut="Q"><i class="flaticon-bass4"></i></span>
  <span class="pad" data-drum="kick4" data-shortcut="1"><i class="flaticon-bass4"></i></span>

  <span class="pad" data-drum="hihat1" data-shortcut="C"><i class="flaticon-drum9"></i></span>
  <span class="pad" data-drum="hihat2" data-shortcut="D"><i class="flaticon-drum9"></i></span>
  <span class="pad" data-drum="hihat3" data-shortcut="E"><i class="flaticon-drum9"></i></span>
  <span class="pad" data-drum="hihat4" data-shortcut="3"><i class="flaticon-drum9"></i></span>

  <span class="pad" data-drum="crash1" data-shortcut="B"><i class="flaticon-cymbal"></i></span>
  <span class="pad" data-drum="crash2" data-shortcut="G"><i class="flaticon-cymbal"></i></span>
  <span class="pad" data-drum="shaker1" data-shortcut="T"><i class="flaticon-maracas4"></i></span>
  <span class="pad" data-drum="shaker2" data-shortcut="5"><i class="flaticon-maracas4"></i></span>

  <span class="pad" data-drum="snare1" data-shortcut="M"><i class="flaticon-side10"></i></span>
  <span class="pad" data-drum="snare2" data-shortcut="J"><i class="flaticon-side10"></i></span>
  <span class="pad" data-drum="snare3" data-shortcut="U"><i class="flaticon-side10"></i></span>
  <span class="pad" data-drum="snare4" data-shortcut="7"><i class="flaticon-side10"></i></span>
 </div>

Kapsayıcı olarak drumpad sınıfı her bir buton(pad) için pad sınıfı verdik. Butonlar için span elementini kullandık. Span elementlerindeki data-drum parametresi ses dosyasının adını, data-shortcut ise klavye kısayolunu tutuyor. İçlerindeki i elementi flaticon ikonlarını kullanabilmek için.

Böyle Drumpad Olmaz Olsun

Görüntü önemli. Şimdi uygulamamıza biraz şekil şükül katalım. CSS kodlarnızı açın.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html, body{
    width: 100%;
    height: 100%;
    color: #FAFAFA;
    font-size: 50px;
    background: #F9BF3B;
}

#drumpad{
    width: 100%;
    height: 100%;
}

Yıldız * seçicisi tüm elementleri etkiler. Margin ve padding değerlerini sıfırlayıp, box-sizing'i border-box yaptık. Yani bir elemente padding verdiğimizde, border eklediğimizde vs. boyutları değişmeyecek. Ya da üst sınırı aşmayacak desek daha doğru olur.

Sayfanın arkaplan rengini yazı boyutunu ve yazı rengini ayarladık. Drumpad kapsayıcısının yüzde yüz yükseklik alabilmesi için sayfa boyutlarını 100% yaptık. Aynı şekilde drumpad boyutunu da belirledik. Tabi siz tam ekran yapmak zorunda değilsiniz, nasıl isterseniz.

.pad {
    width: 24%;
    height: 23%;
    float: left;
    display: block;
    margin: 0.5%;
    text-align: center;
    position: relative;
    background-color: rgba(219,87,5,1);

    /* Köşe Yuvarlaklığı */
    -webkit-border-radius: 8px;
       -moz-border-radius: 8px;
            border-radius: 8px;

    /* Gölge */
    -webkit-box-shadow: 0px 9px 0px rgba(219,31,5,1), 0px 9px 25px rgba(0,0,0,.7);
       -moz-box-shadow: 0px 9px 0px rgba(219,31,5,1), 0px 9px 25px rgba(0,0,0,.7);
            box-shadow: 0px 9px 0px rgba(219,31,5,1), 0px 9px 25px rgba(0,0,0,.7);

    /* Geçiş efekti */
    -webkit-transition: all .1s ease;
       -moz-transition: all .1s ease;
        -ms-transition: all .1s ease;
         -o-transition: all .1s ease;
            transition: all .1s ease;
}

Şimdi de drumpad içindeki butonları stillendirdik. Genişlik ve yükseklik değerleri verdik. Sola yasladık, içindeki metni ortaladık, arkaplan rengini belirledik, kenarlarını 8px yuvarladık, az biraz gölge verip bir de geçiş efekti verdik.

Bu yaptığımız butonun normal hali içindi. Şimdi de tıklandığında neler olacak onu ayarlayalım.

.pad:active {
    /* Gölge */
    -webkit-box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
       -moz-box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
            box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
      
    top: 6px;
}

.pad-pressed {
    /* Gölge */
    -webkit-box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
       -moz-box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
            box-shadow: 0px 3px 0px rgba(219,31,5,1), 0px 3px 6px rgba(0,0,0,.9);
      
    top: 6px;
}

Tıklandığında sadece gölge değişecek ve yukarıdan 6px boşluk kazanacak. Yalnız dikkat edin eğer butonun position özelliği relative değilse top özelliği işe yaramaz.

Hemen altındaki .pad-pressed tanımlaması butonun :active (tıklandığı zaman) durumunun javascript ile verilemediği(ya da veremediğim) için. Klavyeden basıldığında butona basılmış gibi bir etki yaratmak için böyle bir şey yaptım.

CSS düzenlemesi bu kadar. Şimdi yukarıda indirdiğiniz ikon ve font dosyalarını proje içinde bir klasöre çıkartın (benimki: icon) ve HTML sayfanızın head bölümünde ikonların css dosyasını çağırın.

<link rel="stylesheet" type="text/css" href="icons/flaticon.css">

Sonra dosyaları çıkardığınız yerdeki flaticon.css dosyasını açtıktan sonra [class^="flaticon-"] bölümündeki font-size tanımlamasını silin. Javascript ile font boyutunu (yani ikonların) otomatik ayarlayacağımız için bunu yaptık.

Javascript kodlarına gelmeden önce son olarak body etiketinde onload ve onresize olaylarının tanımlı olduğuna emin olun.

<body onload="onBodyLoad()" onresize="setIconSize()">

Fonksiyon isimleri standart değil tabi, isteğinize göre türkçeleştirebilirsiniz. Onload olayı sayfa yüklendiğide, onresize ise sayfanın boyutları değiştirildiğinde tetiklenir.

Baş Döndüren Dolgun Kodlar

Şimdi geldik en cafcaflı kısma. Önce tüm buton (pad) elementlerini tutacağımız pads ve basılı olan tuşları tutacağımız keydown değişkenlerini tanımlayalım.

var pads, keydown = {};
Şimdi de sayfa yüklendiğinde çalışacak onLoad olayını.
function onBodyLoad(){
    pads = document.querySelectorAll(".pad");

    // Padler için Click Olayı
    for(var i = 0; i < pads.length; i++){
        pads[i].onmousedown = function (event){
            var drum = this.dataset.drum;

            new Audio("Drumkit/" + drum + ".wav").play();
        };
    }
            
    setIconSize();

    document.addEventListener("keydown", onKeyDown, true);
    document.addEventListener("keyup", onKeyUp, true);
}

Önce querySelectorAll fonksiyonu ile tüm pad sınıflı nesneleri alıp pads değişkenine dizi olarak atadık. Ardından bir for döngüsü yardımıyla tüm diziyi dolaşıp hepsine teker teker aynı onMouseDown (fare ile tıklandığında) olayı tanımaladık.

onMousedown içinde yaptığımız şey şu; dataset nesnesi ile HTML kısmında tanımladığımız data-drum özelliğini aldıktan sonra (ki bu dosya adıydı hatırlarsanız) yeni bir Audio (ses) nesnesi oluşturduk ve oynattık. Dosya ismini aldıktan sonra başına klasör adını ve sonuna dosya uzantısını eklediğinize emin olun.

setIconSize fonksiyonu ile ikonların boyutlarını (biraz aşağıda) ayarlayacağız.

En son da onKeyDown (tuşa basıldığında) ve onKeyUp (tuş serbest bırakıldığında) olaylarını tanımladık.

Kamu Spotu

setIconSize fonksiyonunu hazırlamadan önce font boyutunu sayfa boyutuna göre javascript ile değiştirmeyi denemiştim. Bi ton uğraşıp bir türlü yapamayınca internette kısa bi araştırma yaptım ve fittext.js diye bi jQuery eklentisi buldum. Tam sayfaya jQuery'yi import ettikten sonra fittext'i indiriyordum ki, birden şunu hatırladım:

Sonra düşünmeye başladım, acaba neden javascript kodları iplenmiyor olabilir diye. Sayfaya normal bir yazı ekledim, değişiyordu ama font ikonlar aynıydı. Sonra da flaticon.css içine baktım, kahroldum. Bu kadar zamandır kafa patlattığım şey sadece font-size özelliğinin css'de tanımlanma sorunuymuş. Evet, doğru tahmin ettiniz. Yukarıda sildiğiniz font-size işte bu font-size.

Neyse, bir kaç yol denedikten sonra aslında gayet basit olduğunu fark ettim ve fonksiyonu şöyle hazırladım. Bence siz de öyle yapmalısınız.

function setIconSize (){
    var pad = document.querySelector(".pad");
    document.body.style.fontSize = (pad.clientHeight / 1.2);
}

Mantık basit; bir buton alıyoruz ve sayfanın font boyutunu pad'in yarısı kadar yapıyoruz. Padler sayfa boyutuna göre boyutlandırıldığı için teknik olarak sayfa boyutuna göre font boyutu belirlenmiş oluyor.

Bu halde çalıştırdığınızda göreceksiniz ki drumpadi fare ile tıklayarak çalabiliyorsunuz. Ama tabi ki klavyeden de kontrol edilse çok iyi olur, çok da güzel olur. Eyyorlamam bu kadar.

function onKeyDown(event){
    var pressedKey = String.fromCharCode(event.charCode || event.keyCode);

    if(!keydown[pressedKey]){
        for(var i = 0; i < pads.length; i++){
            if(pads[i].dataset.shortcut == pressedKey){
                pads[i].onmousedown();
                pads[i].classList.add("pad-pressed");
                keydown[pressedKey] = true;

                break;
            }
        }
    }
}

Sayın Press TV muhabirleri, işte onKeyDown fonksiyonumuz. Fonksiyona gelen event nesnesinden basılan tuşun ascii karakterini aldık ve stringe çevirdik. Bu şekilde basılan tuşu elde etmiş olduk.

Onun hemen altındaki if bloğu şu anlama geliyor; eğer keydown dizisinde bu tuş yoksa. Yani şuanda tuş basılı durumda değilse, şunları yap;

onBodyLoad bölümünde topladığımız pads dizisi içinde dolaşarak, her bir butonun onMouseDown olayını çalıştır(tıklanmış gibi varsay), sınıflarına pad-pressed'i ekle (hatırlarsanız CSS kısmında tanımlamıştık) ve keydown dizisine bu tuşu ekle ki, tuş basılı tutulduğunda sürekli bu işlemler baştan yapılmasın.

Basılı olan tuşları, bırakıldığında keydown dizisinden çıkarmamız gerek. O da şöyle;

function onKeyUp(event){
    var pressedKey = String.fromCharCode(event.charCode || event.keyCode);
    keydown[pressedKey] = false;
    
    for(var i = 0; i < pads.length; i++){
        if(pads[i].dataset.shortcut == pressedKey){
            pads[i].classList.remove("pad-pressed");

            break;
        }
    }
}

Yine onKeyDown'da olduğu gibi basılan tuşu pressedKey değişkenine aldık. Sonra bu tuşun keydown dizisi içindeki değerini false yaptık. Son olarak da pads dizisini dolaşarak hepsinden pad-pressed sınıfını kaldırdık. Böylece tuşa basılıp çekildiğinde tüm tuşlar eski haline gelecek.

İşte bu kadar! Hadi yine iyisiniz sizi gidi köfteler...