Script avanzato per estrarre il colore dominante e generare gradienti dinamici

Script avanzato per estrarre il colore dominante e generare gradienti dinamici

Estrazione Colore Dominante con Gradiente

Questo script JavaScript analizza un’immagine per estrarre i colori dominanti e applica un gradiente dinamico a un container. Il colore del testo viene impostato automaticamente in base al contrasto medio per garantire leggibilità.

Istruzioni d’uso

  1. Assegna l’id “myImage” al widget immagine e “targetDiv” al contenitore.
  2. Incolla lo script JavaScript prima della chiusura del </body> o in un widget HTML/JS.
  3. Lo script analizzerà l’immagine, calcolerà i colori dominanti e applicherà il gradiente al container target.
  4. Funziona meglio se l’immagine è same-origin o se il server permette Access-Control-Allow-Origin.

Parametri opzionali nello script: dimensione del canvas di sampling, livello di quantizzazione dei colori, filtri su luminosità e dimensione dei cluster.

Risultato: il container target mostra un gradiente tra i colori dominanti dell’immagine, con testo leggibile automaticamente.

				
					
/*
  Estrattore colore dominante veloce e robusto (no librerie esterne).
  - Cambia imageContainerId e targetContainerId se necessario.
  - Funziona meglio se l'immagine è same-origin o il server fornisce Access-Control-Allow-Origin.
*/
(async function() {
  const imageContainerId = 'myImage';    // elemento che contiene <img>
  const targetContainerId = 'targetDiv'; // container Elementor da colorare

  // Parametri (regolabili)
  const SAMPLE_SIZE = 200;    // lato massiccio del canvas di sampling (200x200 mantiene qualità/speed)
  const QUANTIZE_LEVEL = 24;  // quantizzazione colore (più basso = più bin)
  const MIN_PIXEL_COUNT = 20; // ignora cluster troppo piccoli
  const IGNORE_BRIGHTNESS_ABOVE = 0.94; // ignora near-white
  const IGNORE_BRIGHTNESS_BELOW = 0.03; // ignora near-black

  function rgbToHex(r,g,b) {
    return '#' + [r,g,b].map(v =&gt; v.toString(16).padStart(2,'0')).join('');
  }

  function rgbToHsl(r,g,b) {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    let h=0, s=0, l=(max+min)/2;
    if (max !== min) {
      const d = max-min;
      s = l&gt;0.5 ? d/(2-max-min) : d/(max+min);
      switch(max) {
        case r: h = (g-b)/d + (g<b> rgb (minima) ---
  function hslToRgb(h, s, l) {
    let r, g, b;
    if (s === 0) {
      r = g = b = l; // achromatic
    } else {
      const hue2rgb = (p, q, t) =&gt; {
        if (t  1) t -= 1;
        if (t &lt; 1/6) return p + (q - p) * 6 * t;
        if (t &lt; 1/2) return q;
        if (t &lt; 2/3) return p + (q - p) * (2/3 - t) * 6;
        return p;
      };
      const q = l  v/255).map(v =&gt; v  {
      // se l'elemento esistente è same-origin e già caricato, lo usiamo
      try {
        const urlObj = new URL(url, location.href);
        if (urlObj.origin === location.origin &amp;&amp; img.complete &amp;&amp; img.naturalWidth &gt; 0) {
          return resolve(img);
        }
      } catch(e) { /* ignore */ }

      const i = new Image();
      i.crossOrigin = 'anonymous'; // deve essere impostato PRIMA di src
      const t = setTimeout(() =&gt; reject(new Error('Timeout image load')), timeout);
      i.onload = () =&gt; { clearTimeout(t); resolve(i); };
      i.onerror = (e) =&gt; { clearTimeout(t); reject(new Error('Errore caricamento immagine')); };
      i.src = url;
    });
  }

  // quantizzazione semplice per raggruppare colori
  function quantizeKey(r,g,b,level) {
    // mappa 0..255 su 0..level-1
    const q = v =&gt; Math.floor(v * (level-1) / 255);
    return `${q(r)}_${q(g)}_${q(b)}`;
  }

  // main sampling + clustering
  async function computeDominant(imgEl) {
    // canvas dimension keeping aspect ratio
    const w = imgEl.naturalWidth || imgEl.width;
    const h = imgEl.naturalHeight || imgEl.height;
    if (!w || !h) throw new Error('Immagine senza dimensioni');

    // dimensione canvas di sampling: manteniamo lato massimo SAMPLE_SIZE
    const scale = Math.min(1, SAMPLE_SIZE / Math.max(w,h));
    const cw = Math.max(1, Math.round(w * scale));
    const ch = Math.max(1, Math.round(h * scale));

    const canvas = document.createElement('canvas');
    canvas.width = cw;
    canvas.height = ch;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(imgEl, 0, 0, cw, ch);

    const data = ctx.getImageData(0,0,cw,ch).data;
    const map = new Map();
    let totalCount = 0;

    // optionally exclude big white margins by focusing sampling area in center
    const cx0 = Math.floor(cw * 0.08), cy0 = Math.floor(ch * 0.08);
    const cx1 = Math.ceil(cw * 0.92), cy1 = Math.ceil(ch * 0.92);

    for (let y = cy0; y &lt; cy1; y++) {
      for (let x = cx0; x &lt; cx1; x++) {
        const idx = (y * cw + x) * 4;
        const r = data[idx], g = data[idx+1], b = data[idx+2], a = data[idx+3];
        if (a  ignora
        // filtro luminosità per scartare sfondi quasi bianchi/neri
        const [hue, sat, lum] = rgbToHsl(r,g,b);
        if (lum &gt; IGNORE_BRIGHTNESS_ABOVE || lum &lt; IGNORE_BRIGHTNESS_BELOW) continue;
        // quantizza
        const key = quantizeKey(r,g,b,QUANTIZE_LEVEL);
        let entry = map.get(key);
        if (!entry) {
          entry = {count:0, rSum:0, gSum:0, bSum:0};
          map.set(key, entry);
        }
        entry.count++;
        entry.rSum += r;
        entry.gSum += g;
        entry.bSum += b;
        totalCount++;
      }
    }

    if (totalCount === 0) throw new Error(&#039;Nessun pixel valido (forse immagine sfondo bianco o CORS)&#039;);

    // crea array di cluster medi
    const clusters = [];
    for (const [k,e] of map.entries()) {
      if (e.count  {
      const lumFactor = 1 - Math.min(0.5, Math.abs(c.l - 0.5)); // 0..0.5 -&gt; 0.5..1
      c.score = c.count * (0.5 + c.s) * (0.5 + lumFactor);
    });

    clusters.sort((a,b) =&gt; b.score - a.score);

    // top cluster
    const top = clusters[0];
    return {
      hex: rgbToHex(top.r, top.g, top.b),
      rgb: [top.r, top.g, top.b],
      count: top.count,
      score: top.score,
      clusters // utile per debug / scelta dei top 2
    };
  }

  // esegui flusso
  let loaded;
  try {
    loaded = await loadImage(src);
  } catch (e) {
    // fallback: se immagine originale è caricata e same-origin, usala
    if (img.complete &amp;&amp; img.naturalWidth &gt; 0) {
      loaded = img;
    } else {
      console.error('Impossibile caricare immagine per analisi:', e);
      return;
    }
  }

  let result;
  try {
    result = await computeDominant(loaded);
  } catch (e) {
    console.error('Errore computazione dominante:', e);
    return;
  }

  // --- Modifica: prendiamo due colori e applichiamo gradiente ---
  const top1 = result.clusters &amp;&amp; result.clusters[0];
  const top2 = result.clusters &amp;&amp; result.clusters[1];

  let hex1 = result.hex;
  let rgb1 = result.rgb;

  let hex2, rgb2;
  if (top2) {
    rgb2 = [top2.r, top2.g, top2.b];
    hex2 = rgbToHex(...rgb2);
  } else {
    // se non c'è un secondo cluster, generiamo un colore "complementare leggermente ruotato" del primo
    const [h, s, l] = rgbToHsl(...rgb1);
    const newH = (h + 0.08) % 1; // ruota la tinta di ~29° (0.08*360)
    rgb2 = hslToRgb(newH, s, Math.min(0.9, Math.max(0.1, l))); // mantieni s/l in range
    hex2 = rgbToHex(...rgb2);
  }

  // applica gradiente da alto-sinistra a basso-destra (to bottom right)
  target.style.backgroundImage = `linear-gradient(to bottom right, ${hex1}, ${hex2})`;
  // rimuoviamo backgroundColor per evitare conflitti
  target.style.backgroundColor = 'transparent';

  // scegli colore testo in base al contrast ratio approssimativo (lum media)
  const lumAvg = (getLuminance(...rgb1) + getLuminance(...rgb2)) / 2;
  target.style.color = (lumAvg &gt; 0.55) ? '#000' : '#fff';
  // opzionale: se il tuo contenitore ha elementi con colori fissi (pulsanti) potresti voler cambiare solo una overlay

  console.log('Dominant colors chosen:', hex1, hex2, 'details:', {result, rgb2});

})();


				
			

Privacy Policy

Informativa sul trattamento dei dati personali ai sensi dell’Art. 13 del Regolamento (UE) 2016/679 (GDPR)

Data Ultimo Aggiornamento: 06/11/25


1. Identità e dati di contatto del Titolare del Trattamento

Titolare del trattamento dei dati personali è:

Angelo Marra
Indirizzo: Via Marcantonio Franceschini 10, 40128 Bologna (BO)
Partita IVA: IT04068171208
Codice Fiscale: MRRNGL80B13A509N
Email: angelo@marrisonlab.com
PEC: angelomarra80@pec.it


2. Dati trattati, Finalità e Base Giuridica

2.1. Dati Raccolti e Finalità (Modulo Contatti)

Dati Raccolti: Nome, Cognome, Indirizzo email, Messaggio e qualsiasi altro dato spontaneamente inserito dall’utente nel modulo.

Finalità: I Suoi Dati Personali sono trattati esclusivamente per rispondere alle Sue richieste di informazione, contatto, preventivo e/o per adempiere a misure precontrattuali adottate su Sua richiesta.

Base Giuridica: La base giuridica del trattamento è l’esecuzione di misure precontrattuali adottate su richiesta dello stesso (Art. 6.1, lett. b, GDPR).

2.2. Dati di Navigazione (Esplicativo)

I sistemi informatici acquisiscono dati (es. indirizzi IP) la cui trasmissione è implicita nell’uso dei protocolli di Internet. Tali dati sono utilizzati al solo fine di ricavare informazioni statistiche anonime e controllarne il corretto funzionamento. Si rimanda alla Cookie Policy per i dettagli sull’uso dei cookie.


3. Periodo di Conservazione dei Dati e Destinatari

Modalità del Trattamento: Il trattamento dei dati avviene mediante strumenti manuali, informatici e telematici.

Conservazione: I dati raccolti tramite il modulo di contatto sono conservati per il tempo strettamente necessario a evadere la Sua richiesta, salvo diversi obblighi di legge.

Destinatari: I Suoi dati non saranno diffusi. Potranno essere trattati da soggetti autorizzati (collaboratori) e da soggetti esterni (es. fornitori di servizi hosting) che agiscono come Responsabili del Trattamento, opportunamente nominati.


4. I Suoi Diritti (Art. 15-22 GDPR)

In qualità di Interessato, Lei ha il diritto di esercitare i seguenti diritti in qualsiasi momento:

  • Diritto di accesso (Art. 15)
  • Diritto di rettifica (Art. 16)
  • Diritto alla cancellazione (Diritto all’Oblio, Art. 17)
  • Diritto di limitazione di trattamento (Art. 18)
  • Diritto di opposizione al trattamento (Art. 21)
  • Diritto alla portabilità dei dati (Art. 20)

Per esercitare tali diritti, La preghiamo di contattare il Titolare all’indirizzo email angelo@marrisonlab.com o PEC angelomarra80@pec.it.

Inoltre, Lei ha il diritto di proporre reclamo all’Autorità di Controllo competente (Garante per la Protezione dei Dati Personali – www.garanteprivacy.it).