Photobox zonder weesafbeeldingen

We gaan Photobox integreren in Drupal. Een zwakte van Photobox is dat ze voor de initiële voorstelling van de afbeeldingen, geen rooster voorzien. Dit moet je zelf doen. De sterkte van Photobox is de slideshow met miniaturen. Als we dus een knap rooster maken, dan hebben we een knappe combinatie.

Maar...

We kennen het allemaal. Afbeeldingen schikken zich naast elkaar maar de laatste afbeeldingen vullen de laatste rij niet. Zulke afbeeldingen noem ik 'wezen'. Ze zijn afhankelijk van de breedte van het scherm en zeer storend.

Hoe programmeer je nu dat je een volle rij krijgt op het einde? 

Er zijn drie parameters die we op voorhand moeten weten.

  1. het totaal aantal afbeeldingen
  2. het minimum aantal kolommen
  3. het maximum aantal kolommen

Zo kan je kiezen voor smartphones (portret view) voor 2 minimum en 3 maximum.

 

Voor smartphones (landscape) of tablet kan je dit verhogen naar 3 en 5 bijvoorbeeld.

Voor brede schermen kies je bijvoorbeeld 5 tot 9..

Ongeacht de programmeertaal moet je nu een systeem vinden om een geschikt rooster te vinden. Een stroomdiagram of flowchart heet dat... Ik heb deze redenering gemaakt en ik ga dit met een concreet voorbeeld uiteenzetten.

Stel: 11 afbeeldingen in totaal, 3 kolommen minimum en 5 maximum.

1) Deel door de mogelijke kolomwaarden:

  • 11/3 = 3,67 , 11/4 = 2,75 , 11/5 = 2,2      best passend is 5, dit heeft slechts een 0,2 als overschot. Neem dus het kleinste overschot, indien gelijk neem dan de grootste kolomwaarde.

2) Verminder het aantal afbeeldingen met de gevonden kolomwaarde. Dit is logisch want we kunnen evenveel afbeeldingen plaatsen als er kolommen zijn.

  • 11-5 = 6

3) Herhaal 1 en 2 tot er geen afbeeldingen meer over zijn.

  • 6/3 = 2 , 6/4 = 1,5 , 6/5 = 1,2  best passend = 3
  • 6-3 = 3
  • 3/3 = 1, 3/4 = 0,75 3/5 = 0,6  best passend = 3
  • 3-3 = 0 => einde

Nog een voorbeeldje:

17 afbeeldingen in totaal, 3 kolommen minimum en 5 maximum

  • 17/4 = best  17-4 = 13
  • 13/4 = best   13-4 = 9
  • 9/3 = best  9-3 = 6
  • 6/3 = best 6-3 = 3
  • 3/3 = best 3-3 = 0

Ik heb speciaal priemgetallen gebruikt als totaal aantal, omdat die de moeilijkste zijn om te schikken. Je zit echter hoe dit systeem zijn weg vindt. Zijn er nadelen... Ja. Je kunt het aantal kolommen zo scherp instellen dat er geen match mogelijk is. 

Voorbeeld 11 afbeeldingen minimum en maximum 5 kolommen

11/5 = best passend  11 - 5 = 6

6/5 = best passend 6-5 = 1

1 ??? blijft verweesd achter. Het komt er op neer dat je realistisch moet blijven en toch een aantal kolomwaarden voorzien. Je zou ook kunnen een lus inbouwen en het minimum aantal kolommen verminderen tot er een match is. Met een minimum van 2 en 3 kom je  altijd tot een match.

Eénmaal je weet per rij, hoeveel kolommen je gaat gebruiken, moet je nu de breedte van je afbeeldingen aanpassen zodanig dat ze de volledige rij opvullen.

Als je 4 afbeeldingen per rij hebt en er onder 3, dan moeten die 3 samen, even breed zijn als de 4 erboven. Dit doe je door de breedte van je scherm (vw) te delen door het aantal kolommen. 

bvb stel 100 vw schermbreedte: 4 afbeeldingen op 25 vw en er onder 3 afbeeldingen op 33,33 vw. Hoe er rekening mee dat je op een pagina niet altijd 100vw ter beschikking hebt omwille van marges en padding. Een pagina bij Adaptive Theme beslaat bijvoorbeeld maar 80vw, de rest is marge links en rechts. In ons programma gaan we dus de werkelijke breedte waarover we beschikken moeten ingeven. Logisch.

Hoe gaan we nu deze breedtes toewijzen aan de betrokken afbeeldingen.. Wel.. we geven elke afbeeldingen een ID mee en kennen voor deze ID in CSS een breedte toe, liefst met @media. Zo laat ik het programma voor 3 breedtes het optimale rooster berekenen. (smartphone, tablet en pc)

Een voorbeeldje van deze werkwijze, geprogrammeerd in twig.

counter bevat het totaal aantal afbeeldingen. Het spreekt voor zich dat dit eerst moet berekend worden door een andere script (vb ook in twig). Met deze script ga je ook de afbeeldingen tonen en de juiste ID meegeven. Gans onderaan bezorg ik je zo'n script, maar hier zie je al een uitgewerkt voorbeeld om een goed rooster op te bouwen.

{# bereken de ideale breedte om geen wezen te krijgen voor brede schermen #}
{% set number = counter %}
{% set body_width = 81 %} {# in vw #}

{% set max_column = 9 %}
{% set min_columns = 5 %}

{% set cur_diff = 1 %}
{% set cur_column = 0 %}
{% set width = [] %}

{% set continue = true %}
{% for x in 0..number if continue %}
{% for columns in min_columns..max_column %}
  {% set rows = number/columns %}
  
    {% if (rows - rows|round(0,'floor')) <= cur_diff %}
      {% set cur_diff = (rows - rows|round(0,'floor')) %}
      {% set cur_column = columns %}         
    {% endif %}    
                                               
{% endfor %}                               

{% for count in 1..cur_column %}
{% set width = width|merge([(body_width/cur_column)|round(2)]) %}
{% endfor %}
{% set  number = number - cur_column %}
   {% if number <= 0 %}
      {% set continue = false %}
   {% endif %}
{% endfor %}

<style>
 {% for i in 0..counter-1 %}
  #item{{i}}{
  width:{{width[i]|round(2,'floor')}}vw;
  }
{% endfor %} 
  
</style>

Resultaat:

Als we dit nu willen combineren met een afbeeldingsgalerij zoals Photobox... Ik gebruik hier een twigveld met de volgende modules:

Voor Photobox is er nog CSS en javascript nodig (gebruik bij voorkeur de Asset injector):

Vul de eerste regels in om het juiste inhoudstype en veldnaam te gebruiken. Aan de rest hoef je niet te komen...

{% set highest_nid = 500 %}
{% set content_type = "fotografen" %}
{% set field_name = "field_fotografen_afbeeldingen" %}

<style>
.gallery{
  display: flex;
  flex-wrap:wrap;
  justify-content:space-evenly;
  padding:0;
  margin:0;
    }
 .gallery .photobox_item{
  padding:0;
  margin-bottom:0.5em;
  }
  .container .gallery a img {
  border: 2px solid #fff;
  -webkit-transition: -webkit-transform .15s ease;
  -moz-transition: -moz-transform .15s ease;
  -o-transition: -o-transform .15s ease;
  -ms-transition: -ms-transform .15s ease;
  transition: transform .15s ease;
  position: relative;  
}
  .container .gallery a:hover img {
  -webkit-transform: scale(1.05);
  -moz-transform: scale(1.05);
  -o-transform: scale(1.05);
  -ms-transform: scale(1.05);
  transform: scale(1.05);
  z-index: 5;
}
  .title a {
  color:white !important;
  }
</style>

{% set img_big =[] %} 
{% set img_small =[] %}
{% set all_nid =[] %}
{% set all_title =[] %}
{% set counter = 0 %}
{% for nid in 0..highest_nid %}
{% set zoeknode = bamboo_load_entity('node', nid) %}
{% if zoeknode.bundle == content_type %}
{% set titel = zoeknode.title.value %}
{% set afbeeldingen = drupal_field(field_name, 'node', nid, {type: 'image_url',settings: {image_style: 'rechthoek_photobox'}}) %}
{% set afbeeldingengroot = drupal_field(field_name, 'node', nid, {type: 'image_url', settings: {image_style: 'big'}}) %}
{% set x = 0 %}
   {% for afbeelding in afbeeldingen %}
     {% if afbeeldingen[x] is not empty %}
       {% set img_big = img_big|merge([afbeeldingengroot[x]]) %}
       {% set img_small = img_small|merge([afbeeldingen[x]]) %}
       {% set all_nid = all_nid|merge([nid]) %}
       {% set all_title = all_title|merge([titel]) %}  
    {% set counter = counter + 1 %}
    {% endif %}
    {% set x = x + 1 %}    
   {% endfor %}
{% endif %}
{% endfor %}

{% for media in 1..3 %}
{% if media == 1 %}
{# bereken de ideale breedte om geen wezen te krijgen voor brede schermen #}
{% set number = counter %}
{% set body_width = 81 %} {# in vw #}
{% set max_column = 9 %}
{% set min_columns = 5 %}
{% endif %}
{% if media == 2 %}
{# bereken de ideale breedte om geen wezen te krijgen voor tablets #}
{% set number = counter %}
{% set body_width = 81 %} {# in vw #}
{% set max_column = 5 %}
{% set min_columns = 3 %}
{% endif %}
{% if media == 3 %}
{# bereken de ideale breedte om geen wezen te krijgen voor smartphones #}
{% set number = counter %}
{% set body_width = 75 %} {# in vw #}
{% set max_column = 3 %}
{% set min_columns = 2 %}
{% endif %}
{% set cur_diff = 1 %}
{% set cur_column = 0 %}
{% set width = [] %}
{% set continue = true %}
{% for x in 0..number if continue %}
{% for columns in min_columns..max_column %}
  {% set rows = number/columns %}  
    {% if (rows - rows|round(0,'floor')) <= cur_diff %}
      {% set cur_diff = (rows - rows|round(0,'floor')) %}
      {% set cur_column = columns %}         
    {% endif %}                                                   
{% endfor %}                             
{% for count in 1..cur_column %}
{% set width = width|merge([(body_width/cur_column)|round(2)]) %}
{% endfor %}
{% set  number = number - cur_column %}
   {% if number <= 0 %}
      {% set continue = false %}
   {% endif %}
{% endfor %}

<style>
  {% if media == 1 %}
     {% for i in 0..counter-1 %}
      #item{{i}}{width:{{width[i]|round(2,'floor')}}vw;}
     {% endfor %}
  {% endif %}
  {% if media == 2 %}
     @media screen and (min-width:500px) and (max-width:850px){
     {% for i in 0..counter-1 %}
      #item{{i}}{width:{{width[i]|round(2,'floor')}}vw;}
     {% endfor %}
     }
  {% endif %}
  {% if media == 3 %}
     @media screen and (max-width:500px){
     {% for i in 0..counter-1 %}
      #item{{i}}{width:{{width[i]|round(2,'floor')}}vw;}
     {% endfor %}
  }
  {% endif %}  
</style>
{% endfor %}

<div class="photobox_container">
<div class="gallery" >
{% for i in 0..counter-1 %}
<a class='photobox_item' id='item{{i}}' href='{{ img_big[i] }}'>
        <img src='{{ img_small[i] }}' 
         alt="<a href='/node/{{all_nid[i]}}'>&#8594; {{ all_title[i] }}</a>"></a>
        {% endfor %}
</div>
</div>

 <!-- Script -->
        <script type='text/javascript'>
        $(document).ready(function(){
             $('.gallery').photobox('a',{ time:5000, autoplay:true});          
            
        });
        </script>

Als je het nog knapper wilt... Je kunt de breedte van de afbeeldingen per rij laten variëren. De redenering die ik gebruik is de volgende:

Laat per afbeelding in een rij de breedte willekeurig variëren. De ene keer breder, de andere keer smaller. Telkens met verschillende waarden. Als je de laatste afbeeldingen in de rij bereikt heb, laat je deze breedte zo instellen dat het de rij volledig vult. Voor de hoogte kan je ook variabel werken.. Zoek per rij de smalste afbeelding en neem dit als hoogte voor de ganse rij. De kleinste afbeelding wordt zo vierkant.

Deze voorstelling komt aardig in de buurt van de Unite Gallery Justified Tiles voorstelling. Dit in combinatie met de Photobox, geeft het volgens mij één van de meest aantrekkelijke voorstellingen van afbeeldingen. Onderaan zie je de volledige script. En... ik heb er nog een extraatje aan toegevoegd.. De volgorde van de afbeeldingen werd door elkaar geschud... Zo komen ze na het clearen van de cache, telkens in een andere volgorde...

{% set highest_nid = 500 %}
{% set content_type = "fotografen" %}
{% set field_name = "field_fotografen_afbeeldingen" %}

<style>
.gallery{
  display: flex;
  flex-wrap:wrap;
  justify-content:center;
  padding:0;
  margin:0;
    }
 .gallery .photobox_item{
  padding:0;
  margin-bottom:2px;
  }
  .container .gallery a img {
  border: solid 2px white;
  -webkit-transition: -webkit-transform .15s ease;
  -moz-transition: -moz-transform .15s ease;
  -o-transition: -o-transform .15s ease;
  -ms-transition: -ms-transform .15s ease;
  transition: transform .15s ease;
  position: relative;  
  object-fit:fill;
}
  .container .gallery a:hover img {
  -webkit-transform: scale(1.05);
  -moz-transform: scale(1.05);
  -o-transform: scale(1.05);
  -ms-transform: scale(1.05);
  transform: scale(1.05);
  z-index: 5;
}
  .title a {
  color:white !important;
  }
</style>

{% set img_big =[] %} 
{% set img_small =[] %}
{% set all_nid =[] %}
{% set all_title =[] %}
{% set counter = 0 %}
{% for nid in 0..highest_nid %}
{% set zoeknode = bamboo_load_entity('node', nid) %}
{% if zoeknode.bundle == content_type %}
{% set titel = zoeknode.title.value %}
{% set afbeeldingen = drupal_field(field_name, 'node', nid, {type: 'image_url',settings: {image_style: 'rechthoek_photobox'}}) %}
{% set afbeeldingengroot = drupal_field(field_name, 'node', nid, {type: 'image_url', settings: {image_style: 'big'}}) %}
{% set x = 0 %}
   {% for afbeelding in afbeeldingen %}
     {% if afbeeldingen[x] is not empty %}
       {% set img_big = img_big|merge([afbeeldingengroot[x]]) %}
       {% set img_small = img_small|merge([afbeeldingen[x]]) %}
       {% set all_nid = all_nid|merge([nid]) %}
       {% set all_title = all_title|merge([titel]) %}  
    {% set counter = counter + 1 %}
    {% endif %}
    {% set x = x + 1 %}    
   {% endfor %}
{% endif %}
{% endfor %}

{% for media in 1..3 %}

{% if media == 1 %}
{# bereken de ideale breedte om geen wezen te krijgen voor brede schermen #}
{% set number = counter %}
{% set body_width = 81 %} {# in vw #}
{% set max_column = 9 %}
{% set min_columns = 5 %}
{% endif %}
{% if media == 2 %}
{# bereken de ideale breedte om geen wezen te krijgen voor tablets #}
{% set number = counter %}
{% set body_width = 81 %} {# in vw #}
{% set max_column = 5 %}
{% set min_columns = 3 %}
{% endif %}
{% if media == 3 %}
{# bereken de ideale breedte om geen wezen te krijgen voor smartphones #}
{% set number = counter %}
{% set body_width = 75 %} {# in vw #}
{% set max_column = 3 %}
{% set min_columns = 2 %}
{% endif %}
{% set cur_diff = 1 %}
{% set cur_column = 0 %}
{% set width = [] %}
{% set height = [] %}
{% set continue = true %}
{% for x in 0..number if continue %}
{% for columns in min_columns..max_column %}
  {% set rows = number/columns %}  
    {% if (rows - rows|round(0,'floor')) <= cur_diff %}
      {% set cur_diff = (rows - rows|round(0,'floor')) %}
      {% set cur_column = columns %}         
    {% endif %}                                                   
{% endfor %}  
{# bepaal de hoogte aan de hand van de smalste afbeelding #}                                                  
{% set adjust = 0 %}
{% set temp = 0 %}
{% set smallest = 100 %}
{% set wissel = random(0, 1) %}                                                 
{% for count in 1..(cur_column-1) %}                                          
{% if (count + wissel)/2 == (count/2)|round(0) %}                                                  
{% set var = random(10,20) %}
{% else %}
{% set var = random(-20,-10) %}                                                  
{% endif %}                                                  
{% set adjust = adjust + var %}
{% set temp = body_width/cur_column %}
{% set width = width|merge([(temp + temp * var/100)]) %}
{% if (temp + temp * var/100) < smallest %}
   {% set smallest = (temp + temp * var/100) %}
{% endif %}
{% endfor %}                                          
{% set width = width|merge([(temp - temp * adjust/100)]) %}
{% if (temp - temp * adjust/100) < smallest %}
   {% set smallest = (temp - temp * adjust/100) %}
{% endif %}
 
                                           
                                              

{% for count in 1..cur_column %}
{% set height = height|merge([smallest]) %}                                         
{% endfor %}                                           

                                               
{% set  number = number - cur_column %}
   {% if number <= 0 %}
      {% set continue = false %}
   {% endif %}
{% endfor %}

<style>
  {% if media == 1 %}
     {% for i in 0..counter-1 %}    
      #item{{i}}{width:{{width[i]|round(2)}}vw;height:{{height[i]|round(2)}}vw !important;overflow:hidden;}
     {% endfor %}
  {% endif %}
  {% if media == 2 %}
     @media screen and (min-width:600px) and (max-width:950px){
     {% for i in 0..counter-1 %}
      #item{{i}}{width:{{width[i]|round(2)}}vw;height:{{height[i]|round(2)}}vw !important;overflow:hidden;}
     {% endfor %}
     }
  {% endif %}
  {% if media == 3 %}
     @media screen and (max-width:600px){
     {% for i in 0..counter-1 %}
      #item{{i}}{width:{{width[i]|round(2)}}vw;height:{{height[i]|round(2)}}vw !important;overflow:hidden;}
     {% endfor %}
  }
  {% endif %}  
</style>
{% endfor %}

{% set order = [] %}
{% for c in 0..counter-1 %} 
   {% set order = order|merge([c]) %}    
{% endfor %} 

{% set randomizedArr = [] %}
{% set tmp = [] %}
{% for q in 0..100 if randomizedArr|length < counter %} 
    {% set tmp = random(order) %}
    {% if tmp not in randomizedArr %}
        {% set randomizedArr = randomizedArr|merge([tmp]) %} 
    {% endif %}
{% endfor %}                                                 

<div class="photobox_container">
<div class="gallery" >
{% for i in 0..counter-1 %}
  {% set n = randomizedArr[i] %}
<a class='photobox_item' id='item{{i}}' href='{{ img_big[n] }}'>
        <img src='{{ img_small[n] }}' 
         alt="<a href='/node/{{all_nid[n]}}'>&#8594; {{ all_title[n] }}</a>"></a>
{% endfor %}
</div>
</div>

 <!-- Script -->
        <script type='text/javascript'>
        $(document).ready(function(){
             $('.gallery').photobox('a',{ time:5000, autoplay:true});          
            
        });
        </script>