Jamstack’te Çizgi Roman Okumak

Raymond Camden

Bir gün Jamstack’le ilgili gerçekten iyi, Kurumsal düzeyde bir blog yazısı yazacağım ve hiçbir işe yaramayan saçma sapan şeylerden bahsetmeyeceğim. Bu gün o gün değil. Hevesli bir çizgi roman okuyucusu olmayanlarınız için, tıpkı “normal” kitaplar gibi çizgi romanların da dijital formatlarda geldiğini bilmiyor olabilirsiniz. Bunları Kindle’ınızda veya diğer uygulamalar aracılığıyla görüntüleyebilirsiniz ve onlar için oldukça büyük bir pazar var.

Kindle’da okuduklarınızın dışında, çizgi romanlar genellikle çizgi roman arşivi biçim. Bu özel bir format değil, kelimenin tam anlamıyla sadece görüntüleri içeren sıkıştırılmış bir dosyadır. Uzantıdan sıkıştırma türünü anlayabilirsiniz:

Ayrıca 7z, ACE (bunu hiç duymadım) ve TAR (AKA, sıkıştırmayı her açmam gerektiğinde google’da yapmam gereken biçim) sürümleri de var.

Eleventy’ye dijital çizgi roman desteği ekleyip ekleyemeyeceğimi görmenin eğlenceli olacağını düşündüm (tuhaf bir eğlence fikrim var). Beklediğimden biraz daha fazla iş oldu ama yaptıklarımı paylaşayım dedim. Zor… ve harika bir okuma deneyimi değil ama işe yarıyor. Benim çözümüm kullanacak onbirancak bunu diğer statik site oluşturucularda kullanabilmeniz gerekir.

Yol Alınmadı

Normalde seçmediğim yaklaşımlar hakkında konuşmak için çok zaman harcamam ama bu durumda onu çiğnemek için çok zaman harcadım ve paylaşmak istiyorum. Niye Belli bir yaklaşım benimsememeye karar verdim. Eleventy’nin son zamanlardaki en havalı özelliklerinden biri, özel şablonlar. Yani örneğin, kurabilirim .pdf geçerli bir şablon uzantısı olarak seçin ve Eleventy’ye bunu nasıl ele alması gerektiğini söyleyin.

Bu yola gitmeyi gerçekten çok istiyordum ama sahip olduğum sorun şu ki çizgi romanlarımla ilgili bir sürü dosyam olacağını biliyordum. Örneğin, bir kapak küçük resmi. Sadece “bire bir” değildi (.cbr ile .html örneğin) ve içgüdülerim bana öyle hissettirdi ki, bu özellik aklımdaki şey için yetersiz bir uyum sağladı.

Yine burada yanılıyor olabilirim ve bu yüzden özelliği kullanmama sebebimi paylaşıyorum.

Kaynak Malzeme

Çoğu çizgi roman telif hakkıyla korunan materyaldir, ancak çizgi romanların ne kadar uzun süredir var olduğu nedeniyle, kamu malı olan birkaç tane olduğu ortaya çıktı. Harika bir web sitesi buldum, Çizgi Roman+üzerinden erişim sağlayan kırk bin ücretsiz çizgi roman indirebilirsiniz. Bunun dışında bir nedenden dolayı erken çizgi romanları görmenin harika bir yolu olup olmadığına bakın. Demom için koleksiyonlarından üç çizgi roman aldım.

Plan

Bir kaynak çizgi roman göz önüne alındığında, aşağıdaki plana karar verdim:

  • İlk olarak, dosyayı bir sayfa dizinine çıkarın. Bu, sayfaları HTML ile göstermeme izin verecek.
  • Bir sayfa dizinimiz olduğuna göre, ilk görüntünün bir kapak olduğunu varsayalım ve güzel boyutlu bir küçük resim oluşturun.
  • Yapılarımızın daha hızlı çalışması için yukarıdakilerin tümünü önbelleğe alınabilir bir yerde saklayın.
  • Ön uç için bir Süper basit okuma deneyimi – temelde ileri geri gitmek için düğmeler ve bir seferde bir sayfa ortalanmış.

Çizgi Romanları Ayrıştırma

Yukarıda dediğim gibi karar verdim. karşı Eleventy’deki özel şablon özelliğini kullanarak ve bunun yerine global veri özelliğini kullandı. ben YARATTIM _data/comics.js ve kodlamaya başladım. Yol boyunca birkaç varsayımdan fazlasını yaptım, her şeyden önce sadece destekleyeceğim .cbr ve .cbz. Bu, dosyaları ayrıştırmak için yalnızca iki kitaplığa ihtiyacım olacağı anlamına geliyordu.

Dosyam biraz karmaşık ve dürüst olmak gerekirse, biraz yeniden düzenleme gerektirebilir, bu yüzden önce onu bitler halinde paylaşmama izin verin, umarım daha mantıklı olur.

İlk olarak, birkaç sabit:

const inputDir="./comics"; 
const cacheDir="./comiccache"; 

Ardından, sonucumu başlatın ve dizinimi okuyun:

let comics = [];

console.log('Comic Processing:n');
let files = fs.readdirSync(inputDir);

// first, ensure we filter to .cbr or .cbz, could be done using glob npm module instead
files = files.filter(f => {
    let ext = f.split('.').pop().toLowerCase();
    return ['cbr','cbz'].indexOf(ext) >= 0;
});

Bu noktada, bir dizi dosyamız var. üzerlerinden geçtim ve belirttim f geçerli dosya olarak:

for(let i=0; i<files.length; i++) {

    let f = files[i];

Her çizgi roman için ana dizinin altında kendi önbellek dizinine ihtiyacım var. cacheDir yukarıda tanımlanmıştır. Bunun için dosya adının ağırlaştırılmış sürümünü kullandım:

let cacheFolder = cacheDir + "https://www.raymondcamden.com/" + slugify(f) + "https://www.raymondcamden.com/";
console.log(`For ${f}, the cache is ${cacheFolder}`);
if(!fs.existsSync(cacheFolder)) fs.mkdirSync(cacheFolder);

Şimdi, içeriği bir ‘sayfalar’ dizinine çıkaran ilk ana işlemimize ihtiyacımız var:

/*
Always check for the pages first, as we use that to make thumbnails.
*/
let pagesFolder = cacheFolder + 'pages/';

if(!fs.existsSync(pagesFolder)) {
    fs.mkdirSync(pagesFolder);
    console.log(`Need to extract the pages to ${pagesFolder}`);
    /*
    we use one library for zips, another for rars
    */
    let ext = f.split('.').pop().toLowerCase();
    if(ext === 'cbz') {
        console.log(`Process the ZIP file, ${f}`);
        let zip = new AdmZip(inputDir + "https://www.raymondcamden.com/" + f);
        /*
        Can't use this as it doesn't allow us to ignore the internal directory
        zip.extractAllTo(pagesFolder, true);
        */
        let entries = zip.getEntries();
        entries.forEach(e => {
            if(!e.isDirectory) {
                zip.extractEntryTo(e.entryName, pagesFolder, false);
            }
        });
    } else {
        console.log('Process the RAR');
        let extractor = await unrar.createExtractorFromFile({ 
            wasmBinary, 
            filepath:inputDir + "https://www.raymondcamden.com/" + f, 
            targetPath:pagesFolder, 
            filenameTransform(file) {
                return file.split("https://www.raymondcamden.com/").pop();
            }
        });
        /*
        I have no idea how this works, but thanks: https://stackoverflow.com/a/71427375/52160
        */
        [...extractor.extract().files];
        console.log(`Extracted to ${pagesFolder}`);

    }
}

Yukarıdan – önce pages klasör var ve yoksa oluşturun. Daha sonra iki şubemiz var. Zip dosyaları için kullandım adm-zip, fermuarlarla çalışmak için nispeten basit bir kitaplık. Karşılaştığım tek sorun, sonucu ‘düzleştirmem’ gerektiğinden “tek astarı” (yukarıdaki kod örneğinde yorumlanmıştır) kullanamamamdı. Yani örneğin verilen spiderman01.cbziçindekiler bir alt dizinde olabilir spiderman (01)..... Üçüncü argümanın ne olduğu ‘düz’ bir dizi görüntü istiyorum extractEntryTo yapmak.

RAR dosyaları için biraz daha zordu. kullandım düğüm-unrar-js bu sadece kullanımı çok zor bir kütüphaneydi. Açıkça söylediğim gibi, sondaki o çizginin neden işe yaradığına dair hiçbir fikrim yok ama işe yaradı. Bunun üretime dağıtmak için tamamen iyi ve güvenli olduğuna eminim.

Tamam, bu noktada çizgi romanları çıkardık. Şimdi küçük resimler yapmamız gerekiyor:

let thumb = cacheFolder + 'thumb.jpg';
if(!fs.existsSync(thumb)) {
    console.log(`Need to make the thumbnail, ${thumb}.`);
    /*
    First, find the first image in pagesFolder. We're going to get all and get the first
    item, but I'm NOT sure I trust the sorting. 

    Also, note I found one comic with a Thumbs.db, so we will filter to first jpg/gif/png
    */
    let images = fs.readdirSync(pagesFolder);
    let sourceImage = null;

    while(!sourceImage) {
        let image = images.shift();
        let ext = image.split('.').pop().toLowerCase();
        if(['jpg','gif','png'].indexOf(ext) >= 0) sourceImage = image;
    }

    const image = await Jimp.read(pagesFolder + "https://www.raymondcamden.com/" + sourceImage);
    await image.resize(THUMB_WIDTH, Jimp.AUTO);
    // hard coding for now
    await image.quality(80);
    await image.writeAsync(thumb);
}

Bunun için kullandığım Jimnastik, başka bir kullanımı kolay kitaplık. Karşılaştığım tek gerçek tuhaflık, bir arşiv içeren bir arşivdi. Thumbs.db Dosyayı görmezden gelmem gerekiyordu.

Son kısım verilerimi alır ve sitede kullanıma hazırlar:

/*
Ok, let's construct the data we're returning...
*/
let comic = {};
comic.filename = f;
comic.slug = slugify(f);
comic.pages = fs.readdirSync(pagesFolder).filter(f => {
    let ext = f.split('.').pop().toLowerCase();
    return ['jpg','gif','png'].indexOf(ext) >= 0;
}).map(p => {
    return cacheWebDir + "https://www.raymondcamden.com/" + comic.slug + '/pages/' + p;
});
comic.thumb = thumb;
comic.numPages = comic.pages.length;
comics.push(comic);

Sadece ön uçta işleri kolaylaştırmak için birkaç sayfa değeri belirlediğimi fark edeceksiniz. Ayrıca, kullanımına dikkat edin cacheWebDirBunu göstermedim, ama bu:

const cacheWebDir="/comiccache";

Temel olarak, önbelleğimin “web” versiyonu. Vay! Şimdi onları sergilemeye devam edin.

Çizgi Romanların Görüntülenmesi

Pekala, daha önce de söylediğim gibi bir gerçek ön uçta basit bir deneyim. İlk olarak, küçük resimleri gösteren ana sayfa:

Birini tıkladıktan sonra, üstte ve altta kontroller bulunan sayfa başına bir resim görürsünüz:

Çizgi romanın ilk sayfası

Ve karşılaştırma için, nasıl çelenk çizileceğini güzel bir şekilde gösteren başka bir sayfa:

Başka sayfa

Dürüst olmak gerekirse, elimden gelenin en iyisini yaptım ve şunu buldum:

Kötü çizilmiş bir çelenk

Dürüst olmak gerekirse, çizimin ortasında felç geçirmiş gibi görünüyorum. Her neyse, ana sayfa her çizgi romanı yineler ve küçük resmi görüntüler:

---
layout: main
title: Comic Books
---

<h2>Currently Available Comics</h2>

{% for comic in comics %}
    <a href="/comics/{{comic.slug}}"><img src="{{comic.thumb}}" class="comicThumb"></a>
{% endfor %}

Çizgi romanları görüntülemek için tek sayfalık bir yaklaşıma karar verdim. Hiç JavaScript olmadan, her çizgi roman sayfası için bir HTML sayfasıyla gitmek istedim, ancak kullanıcıyı sayfada tutmanın ve sadece görüntüyü değiştirmenin daha iyi olacağını düşündüm. İşte bunu nasıl inşa ettim:

---
layout: main
pagination:
    data: comics
    size: 1
    alias: comic
permalink: "/comics/{{comic.slug}}/"
---

<h2>{{ comic.filename }}</h2>

{% capture "nav" %}
<p class="pageNav">
<button class="prevButton">Previous</button> 
Page <span class="currentPage">1</span> of {{comic.numPages}}
<button class="nextButton">Next</button>
</p>
{% endcapture %}
{{ nav }}

<p class="pageImage">
<img src="{{comic.pages[0]}}" id="pageImage">
</p>

{{ nav }}

<script>
const totalPages = {{comic.numPages}};
let currentPage = 0;
let currentPageDom;
let currentPageImage;

let images = [
    {% for image in comic.pages %}
        '{{image}}' {% if forloop.last == false %},{% endif%}
    {% endfor %}

];

document.addEventListener('DOMContentLoaded', init, false);
function init() {

    currentPageDom = document.querySelectorAll('.currentPage');
    currentPageImage = document.querySelector('#pageImage');

    document.querySelectorAll('.nextButton').forEach(nb => {
        nb.addEventListener('click', nextPage); 
    });

    document.querySelectorAll('.prevButton').forEach(pb => {
        pb.addEventListener('click', previousPage); 
    });

}

function nextPage() {
    console.log('go next');
    if(currentPage+1 < totalPages) {
        currentPage++;
        currentPageDom.forEach(c => c.innerText = currentPage + 1);
        currentPageImage.src = images[currentPage];
    }
}

function previousPage() {
    console.log('go prev');
    if(currentPage > 0) {
        currentPage--;
        currentPageDom.forEach(c => c.innerText = currentPage + 1);
        currentPageImage.src = images[currentPage];
    }
}
</script>

Burada dikkat edilmesi gereken birkaç şey var. ben kullandım capture Değişken oluşturmak için sıvı etiketi, nav, sayfamın üstünde ve altında kullanabileceğim. Tabii ki ilk sayfaya geçiyoruz.

JavaScript’te, düğmelerim için sadece bir değil, iki tane olduğundan biraz karmaşık olması gereken olay dinleyicilerim var. Bu nedenle forEach sorgu seçicileri ile. O zaman ileri ve geri gitmek için sadece bir işleyicidir ve bunun için çok ileri gitmediğimden emin olmak için sınırları (0 ve totalPages) kullanıyorum.

Canlı versiyonu görmek ister misiniz? Elbette yaparsın! Burada görüntüleyebilirsiniz: https://comicbookjamstack.netlify.app/. Depo burada bulunabilir: https://github.com/cfjedimaster/eleventy-demos/tree/master/comicbooks. Netlify’ı gerçekten bunu yapmak için ayarlamadığınız sürece önbelleğin çalışmayacağını unutmayın. Bunu son yazımda ele aldım: Netlify Önbellek Eklentisini Eleventy ile Test Etme

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.