logo

Responsive images και Lazy Loading

31/08/2020

Πειραματίζομαι με διάφορες τεχνικές ώστε να βελτιώσω τις επιδόσεις του eordaia.info.

Πρώτα από όλα χρησιμοποιώ το srcset attribute του <img>, για να εμφανίζει την έκδοση της εικόνας που ταιριάζει περισσότερο με την οθόνη του χρήστη. Κρατάω 4 μεγέθη για κάθε εικόνα.

Στο srcset του λες ποια εικόνα θα εμφανίσει, αναλόγως πόσο χώρο θα πιάσει αυτή στην οθόνη. Για παράδειγμα

<img srcset="smallImage.jpg 500w,
             mediumImage.jpg 1000w,
             largeImage.jpg 1500w
             originalImage.jpg 2000w"
     src="mediumImage.jpg"
     sizes="(min-width: 940px) 66vw, 100vw">

Έτσι π.χ. μέχρι το μέγεθος της εικόνας να είναι στα 500w, θα εμφανίσει το smallImage.jpg. Το w είναι στην ουσία τα φυσικά pixels της οθόνης. Αν μια οθόνη είναι retina και έχει μεγαλύτερη πυκνότητα pixels, τότε για 2x τα 250 pixels θα αντιστοιχούν σε 500w.

Μπορεί να χρησιμοποιηθεί και το attribute sizes για μεγαλύτερο έλεγχο. π.χ στον παραπάνω κώδικα αυτό που υπάρχει στο sizes, λέει ότι πάνω από τα 940 pixels (μέγεθος οθόνης), η εικόνα θα εμφανίζεται στο 66% του viewport (δηλαδή σε μεγαλύτερο οθόνη η εικόνα μπορεί να πιάνει λιγότερο χώρο). Ενώ κάτω από τα 940 pixels η εικόνα θα πιάνει όλο το μήκος του viewport. Αφού θα μικρύνει το μέγεθος της φωτογραφίας θα εμφανίσει και την ανάλογη το srcset.

Το άλλο θέμα που είχα να αντιμετωπίσω ήταν το lazy loading. Δηλαδή να μην χρειάζεται να φορτωθούν ολόκληρες οι εικόνες για να εμφανιστεί το υπόλοιπο περιεχόμενο της σελίδας.

Στην αρχή δοκίμασα ένα component που έχει το bootstrap-vue. Το <b-img-lazy>. Δεν μου άρεσε όμως. Στο production, όποτε θέλει φορτώνει μια αρχική εικόνα πολύ χαμηλής ανάλυσης που θέλω (για να δείξει αυτή μέχρι να φορτώσει η κανονική) και όποτε θέλει όχι.

Τελικά έφτιαξα ένα δικό μου vue component για να κάνει την δουλειά. Η λογική του είναι ότι φορτώνω 2 img elements. Το ένα είναι το βασικό. Αυτό που βλέπει ο χρήστης. Το άλλο είναι το κρυφό, εκεί που φορτώνεται η εικόνα μεγάλης ανάλυσης. Μέχρι να φορτώσει η μεγάλη εικόνα, στο βασικό img δείχνει μια εικόνα χαμηλής ανάλυσης. Αφού φορτώσει αντικαθιστώ το src της μικρής εικόνας με το src της μεγάλης.

Έτσι ο χρήστης βλέπει ένα εφέ στο οποίο στην αρχή η εικόνα είναι πολύ θολή και σιγά – σιγά καθαρίζει. Δίνει την ψευδαίσθηση ότι φορτώνει σταδιακά.

Το πρόβλημα με το lazy loading (τουλάχιστον όπως το έκανα προς το παρόν) είναι ότι ο browser δεν ξέρει (μέχρι να φορτώσει η εικόνα χαμηλής ανάλυσης), πόσο χώρο θα πιάσει η εικόνα στο συγκεκριμένο σημείο της σελίδας. Έτσι βλέπεις το κείμενο να “χοροπηδάει” μόλις φορτώσουν οι εικόνες.

Αυτό είναι και το θέμα που θέλω να λύσω. Ή θα πρέπει να ξέρω από πριν το μέγεθος της εικόνας (που προς το παρόν δεν το σώζω στην βάση) ή θα πρέπει ίσως να σώζω την εικόνα και σε ένα ακόμη μικρότερο μέγεθος και ποιότητα (για να φορτώνει όσο γίνεται πιο γρήγορα).

Θα δοκιμάσω το δεύτερο και βλέπουμε πως θα πάει.

Ο κώδικας του vue component είναι ο παρακάτω.

<template>
    <div>
        <img :src="imageDisplayed" width="100%" height="auto"
             :title="photo.title" :alt="photo.title">

        <img :srcset="photo.srcset"
            :src="photo.fallback"
            :sizes="photo.sizes"
            ref="image" class="preloadedImage"
            @load="imageUploaded">
    </div>
</template>

<script>
    export default {
        data() {
            return {
                imageDisplayed: ''
            }
        },

        props: {
            photo: {
                required: true,
                type: Object
            }
        },

        created() {
            this.imageDisplayed = this.photo.preload
        },

        methods: {
            imageUploaded() {
                this.imageDisplayed =  this.$refs.image.currentSrc
            }
        }

    }
</script>

<style scoped>
    .preloadedImage {
        display: none;
    }
</style>

Στο photo που δέχεται στα props το component, στέλνω κάτι τέτοιο (από laravel component, το οποίο καλεί το vue component)

[
    'id' => $this->photo->id,
    'preload' => $this->photo->smallPhotoUrl,
    'fallback' => $this->photo->mediumPhotoUrl,
    'title' => $photo->label ?? $this->title,
    'srcset' => [
         $this->photo->smallPhotoUrl . ' 150w',
         $this->photo->mediumPhotoUrl . ' 1000w',
         $this->photo->largePhotoUrl . ' 1500w',
         $this->photo->photoUrl . ' 2000w'
     ],
     'sizes' => '(min-width: 940px) 66vw, 100vw'
]

Write your comment

rocean (at) error.gr
rocean
error.gr
feed