Geçen bir kaç günümü HTML5 ile gelen canvas elementiyle ilgili bir dizi soruna ayırdıktan sonra, nice genç dinamik yazılımcılar yabancı forumlarda, bloglarda çözüm ararken heder olmasın diye bu yazı dizisini başlatmak istedim. Ve başlattım!

Basit çizim uygulaması #1 (HTML5 / Javascript)

Bu yazıda temel çizim fonksiyonlarını yazacağız. Sonraki yazılarda çizimin rengi, fırça boyutu, boyama biçimi gibi özellikler ekleyeceğiz. Kozmos yar ve yardımcımız olsun.

Başlangıç

Basit bir HTML iskeleti oluşturalım, içinde biraz CSS, hiç Javascript ve ecücük canvas olsun.

<!DOCTYPE html>
<html lang="tr">
<head>
    <title>Çizim Uygulaması</title>
    <meta charset="utf-8"/>
    <style type="text/css">
        #sahne{
            border: 1px solid #DDDDDD;
        }
    </style>
    <script type="text/javascript">
        function onBodyLoad(){
            // Burada JS kodları olacak. İtirazı olan?
        }
    </script>
</head>
<body onload="onBodyLoad()">
    <canvas id="sahne" width="660" height="330"></canvas>
</body>
</html>

Ne yaptık?

ID değeri sahne olan bir canvas elementi oluşturduk, genişlik ve yükseklik (660x330) değerlerini belirledik. CSS kısmında canvas sınırları belli olsun diye 1px kenarlık ekledik.

Javascript kısmında onBodyLoad isminde boş bir fonksiyon tanımladık ve body onload özelliğine onu yazdık. Böylece sayfa yüklendiğinde yazdığımız Javascript kodları çalışacak. Şimdilik HTML ve CSS kısmında işimiz bitti, geri kalan herşeyi Javascript kısmında yapacağız.

Canvas Ayarları

HTML içinde oluşturduğumuz canvas elementini alıp, çizim yapabilmemiz için context nesnesi oluşturalım. Tabi yazdığımız tüm kodları onBodyLoad fonksiyonu içine yazıyoruz.

var canvas = document.getElementById("sahne");
var context = canvas.getContext("2d");

context.strokeStyle = "#212121";
context.lineJoin = "round";
context.lineWidth = 3;

getElementById fonksiyonu ile ID değeri verilen elementi yakalarız. Canvas içindeki getContext fonksiyonu da adı üzerinde context nesnesini getirir.

Çizim rengini strokeStyle belirler, HEX olarak renk kodu ya da ingilizce renk isimleri (blue, red vs.) verilebilir. Çizgilerin birleşim yerlerinin yuvarlanması için lineJoin özelliğini "round" yaptık. Çizgi kalınlığını da lineWidth özelliği ile 3px belirledik.

Değişkenler

Çizim yaparken kullanacağımız değişkenleri tanımlayalım. Çizim değişkeni true/false değer alacak ve o anda çizim yapılıyor mu yapılmıyor mu bilgisini tutacak. Rota (dizi) değişkeni yaptığımız çizimin kırılma noktalarının koordinatlarını tutacak.

var cizim = false;
var rota = []; // Boş dizi

Rota değişkeninin her bir elemanı JSON nesnesi olacak ve şimdilik üç parametresi olacak; x, y ve drag. X, Y parametreleri koordinatları, sürükle (drag) parametresi ise o anda çizim yeni mi başladı yoksa başlayan bir çizginin devamı mı olduğu bilgisini tutacak.

Olaylar (Events)

Şimdi kullanacağımız olayları tanımlayalım. onMouseDown: fare butonu ile canvas elementine tıklandığında (çizim başladığında) tetiklenir. onMouseMove: fare canvas üzerinde hareket ederken, yani çizim sürerken. onMouseUp: tıklanan fare butonu bırakıldığında (yani çizim sona erdiğinde) tetiklenir. Son olarak onMouseLeave: fare canvas elementinin dışına çıktığında tetiklenir.

// Fare butonuna basıldığında
canvas.onmousedown = function(event){
    console.log("event: onMouseDown");
};

// Fare hareket ederken
canvas.onmousemove = function(event){
    console.log("event: onMouseMove");
};

// Fare butonu kaldırıldığında
canvas.onmouseup = function(){
    console.log("event: onMouseUp");
};

// Fare, canvas elementinin dışına çıktığında
canvas.onmouseleave = function(){
    console.log("event: onMouseLeave");
};

Kullanacağımız tüm eventları tanımladık. Şimdilik sadece console.log ile konsola yazı yazdırıyoruz. Tarayıcının konsolunu açarak çalışıp çalışmadığını deneyebilirsiniz.

Firefox için Shift + CTRL + K, Chrome kullanıyorsanız Shift + CTRL + J eğer Internet Explorer kullanıyorsanız CTRL + W tuşlarını kullanabilirsiniz.

Evet, IE kullananlar aramızdan ayrıldığına göre devam edebiliriz. :)

Çizim koordinatlarını belirleme

Şimdi, onMouseDown ve onMouseMove eventlarında farenin konumunu alıp rota dizisine ekleyelim.

canvas.onmousedown = function(event){
    var mouseX = event.pageX - canvas.offsetLeft;
    var mouseY = event.pageY - canvas.offsetTop;
       
    cizim = true;
       
    rota.push({
        x : mouseX,
        y : mouseY,
        drag : false
    });
};

Ne Yaptık?

Fonksiyonun gönderdiği event objesinin pageX ve pageY parametreleri farenin sayfa içindeki konumunu verir. Canvas içindeki offsetLeft objenin sola olan uzaklığı (X) ve offsetTop yukarıya olan uzaklığını (Y) verir. Farenin canvas üzerindeki konumunu bulmak için farenin konumundan canvas'ın konumunu çıkartıyoruz.

mouseX ve mouseY koordinatlarını bulduktan sonra çizim işleminin başladığını belirten cizim = true; komutunu yazıyoruz.

Push fonksiyonu dizi içine yeni bir eleman eklemeye yarar. Fonksiyon içine yazdığımız küme parantezleri { } eklediğimiz verinin bir JSON objesi olduğunu gösteriyor. X ve Y parametrelerine az önce aldığımız mouseX ve mouseY değerlerini verdik. Drag parametresini false verdik çünkü çizim şuanda devam etmiyor, daha yeni başladı.

Çizimin devamını getirme

onMouseDown ile çizimin başlangıcını belirttik, şimdi de rota dizisine çizimin devamındaki koordinatları ekleyelim.

canvas.onmousemove = function(event){
    var mouseX = event.pageX - canvas.offsetLeft;
    var mouseY = event.pageY - canvas.offsetTop;

    if(cizim){
        rota.push({
            x : mouseX,
            y : mouseY,
            drag : true
        });
    }
};

Aslında yukarıda yaptığımız işlemin hemen hemen aynısı. Tek fark rota eklerken drag parametresini true yaptık. Yani şuanda çizim sürüyor demek. Bir de push fonksiyonunu if koşulunun içine yazdık. Buradaki amaç şu; fare ile tıklandığında çizim başlıyor yani true oluyor. Eğer çizim başlamadıysa (canvasa tıklanmadıysa) fare ile canvasın üzerinde gezerken çizim yapmasın.

Çizimi sonlandırma

Şimdi de onMouseUp ve onMouseLeave eventlarını kullanarak çizimi sonlandıralım.
canvas.onmouseup = function(){
    cizim = false;
};

canvas.onmouseleave = function(){
    cizim = false;
};

Rakamlardan renklere...

Evet, geldik enginarın kalbine, fasülyenin faydalarına. Bu kadar koordinat topladıktan sonra ekrana çizmezsek ayıp etmiş olurduk herhalde.

function boya(){
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
       
    for(var i=0; i < rota.length; i++){  
        context.beginPath();
           
        if(rota[i].drag && i){
            context.moveTo(rota[i -1 ].x, rota[i - 1].y);
        }else{
            context.moveTo(rota[i].x - 1, rota[i].y);
        }
        
        context.lineTo(rota[i].x, rota[i].y);
        context.closePath();
        context.stroke();
    }
}

Boya fonksiyonunu belirli aralıklarla çalıştırarak ekrandaki çizimin sürekli güncel kalmasını sağlayacağız. Fonksiyonun başındaki clearRect fonksiyonu canvasın belirli bir bölgesini temizlemeye yarar. Aldığı parametreler sırasıyla x, y, genişlik ve yükseklik. X ve Y değerlerini 0, genişlik ve yükseklik değerlerini canvas'ın genişlik ve yüksekliğini verdik. Yani canvas elementini komple temizleyecek.

Hemen aşağıdaki for döngüsü rota uzunluğunda dönecek ve koordinatlara çizimleri yapacak. İlk önce beginPath ile koordinat belirlemeyi başlattık. Ardından moveTo ile başlangıç pozisyonunu belirleyeceğiz. Ancak bir if koşulu görüyorsunuz. Anlamı şu; eğer drag true ise yani çizim yeni başlamadıysa ve i sıfırdan büyükse rota başlangıcını şuanki elemandan değil bir öncekinden i - 1 başlat. Aksi halde (else) koordinatın X değerini 1 azalt, Y aynı kalsın.

Sonra lineTo methodu ile çizginin gideceği koordinatı belirle. Aslında yaptığımız işlem şu; eğer çizim tıkla-bırak işlemi ise sadece nokta koy, aksi halde bir önceki tıklama pozisyonundan şimdiki pozisyona bir çizgi çek.

Son olarak closePath ile pozisyonlama işlemini bitirip stroke ile de çizimi gerçekleştiriyoruz.

Kamera, motor!

Mouse hareketlerini kontrol edip rota dizisinde biriktirdik. Boya fonksiyonu ile topladığımız koordinatları çizimlere çevirdik. Sıra geldi boya fonksiyonunu çalıştırmaya.

setInterval(boya, 30);

setInterval fonksiyonu istediğimiz bir fonksiyonu belirli aralıklarla çalıştırmaya yarar. İlk parametre fonksiyon adı, ikincisi milisaniye cinsinden çalıştırma aralığı. Biz 30 ms aralıklarla boyadık. Eğer kasma yaşıyorsanız bu rakamı artırabilir ya da daha hızlı çalışmasını istiyorsanız düşürebilirsiniz.

Bu aralığın daha global bir değer olmasını istiyorsanız 1000 / 30 gibi bir değer vererek FPS değeri elde edebilirsiniz.

Bakmadan almam!

Eğer bu zımbırtının çalışan bir halini görmek istiyorsanız şuraya bakabilirsiniz.

HTML ve Javascript ile Canvas çizim uygulaması
Bu uygulamayla mesela şöyle yaratıcı ve sanat şah eseri çalışmalar yapılabilir...

Kolay gelsin!