Detectar el soporte de WebP


96

¿Cómo puedo detectar la compatibilidad con WebP a través de Javascript? Me gustaría usar la detección de funciones en lugar de la detección del navegador si es posible, pero no puedo encontrar la manera de hacerlo. Modernizr ( www.modernizr.com ) no lo busca.


1
Si carga una imagen de este tipo en un elemento Imagen y luego verifica el ancho y el alto en un navegador que no admite el formato, ¿obtiene algo?
Puntiagudo

(Quise decir " Objeto de imagen ", no elemento; como "nueva imagen ()" ...)
Puntiagudo

Se ve bien. Puedo obtener un "Sí apoyo WebP" de esta manera; pero no puedo obtener un "No soy compatible con WebP".
Snostorm


2
@Simon_Weaver la pregunta y los comentarios tienen varios años. Las viejas preguntas rara vez se "mantienen" de manera significativa; Sin embargo, siempre puedes agregar una nueva respuesta.
Puntiagudo

Respuestas:


118

Esta es mi solución: está tomando alrededor de 6 ms y estoy considerando que WebP es solo una característica para un navegador moderno. Utiliza un enfoque diferente usando la función canvas.toDataUrl () en lugar de la imagen como forma de detectar la característica:

function support_format_webp()
{
 var elem = document.createElement('canvas');

 if (!!(elem.getContext && elem.getContext('2d')))
 {
  // was able or not to get WebP representation
  return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0;
 }
 else
 {
  // very old browser like IE 8, canvas not supported
  return false;
 }
}

7
Esta debería ser la respuesta aceptada, porque todos los demás tienen un retraso debido a que apuntan a un recurso de red o un URI de datos
imal hasaranga perera

6
Versión simplificada:webp = e => document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
António Almeida

35
Esto no funciona en Firefox 65, que admite mostrar webp, pero no crear una URL de datos webp a partir de un elemento de lienzo.
carlcheel

4
Me encanta esto porque es sincrónico, sin embargo, @carlcheel tiene razón. FF 65 (que tiene soporte webp) todavía devuelve falso aquí :-(
RoccoB

11
Esto sería genial si funcionara para FF65 y Edge18. Ambos admiten webp pero serializan el lienzo con "data: image / png"
undefinederror

55

Creo que algo como esto podría funcionar:

var hasWebP = false;
(function() {
  var img = new Image();
  img.onload = function() {
    hasWebP = !!(img.height > 0 && img.width > 0);
  };
  img.onerror = function() {
    hasWebP = false;
  };
  img.src = 'http://www.gstatic.com/webp/gallery/1.webp';
})();

En Firefox e IE, el controlador "onload" simplemente no se llamará en absoluto si la imagen no se puede entender, y en su lugar se llama "onerror".

No mencionaste jQuery, pero como ejemplo de cómo lidiar con la naturaleza asincrónica de esa verificación, podrías devolver un objeto jQuery "Deferred":

function hasWebP() {
  var rv = $.Deferred();
  var img = new Image();
  img.onload = function() { rv.resolve(); };
  img.onerror = function() { rv.reject(); };
  img.src = 'http://www.gstatic.com/webp/gallery/1.webp';
  return rv.promise();
}

Entonces podrías escribir:

hasWebP().then(function() {
  // ... code to take advantage of WebP ...
}, function() {
  // ... code to deal with the lack of WebP ...
});

Aquí hay un ejemplo de jsfiddle.


Un verificador más avanzado: http://jsfiddle.net/JMzj2/29/ . Éste carga imágenes de una URL de datos y comprueba si se carga correctamente. Dado que WebP ahora también admite imágenes sin pérdida, puede verificar si el navegador actual solo admite WebP con pérdida o también WebP sin pérdida. (Nota: esto también comprueba implícitamente el soporte de URL de datos).

var hasWebP = (function() {
    // some small (2x1 px) test images for each feature
    var images = {
        basic: "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==",
        lossless: "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA="
    };

    return function(feature) {
        var deferred = $.Deferred();

        $("<img>").on("load", function() {
            // the images should have these dimensions
            if(this.width === 2 && this.height === 1) {
                deferred.resolve();
            } else {
                deferred.reject();
            }
        }).on("error", function() {
            deferred.reject();
        }).attr("src", images[feature || "basic"]);

        return deferred.promise();
    }
})();

var add = function(msg) {
    $("<p>").text(msg).appendTo("#x");
};

hasWebP().then(function() {
    add("Basic WebP available");
}, function() {
    add("Basic WebP *not* available");
});

hasWebP("lossless").then(function() {
    add("Lossless WebP available");
}, function() {
    add("Lossless WebP *not* available");
});

¡Increíble! Esto funciona en FF, Chrome e IE 9. Por alguna razón, no funciona en IE8 o IE7.
snostorm

Me funciona en IE7: prueba el jsFiddle que acabo de vincular al final de la respuesta.
Puntiagudo

Bueno, mi respuesta original solo tenía el "onload" - No sabía que incluso había un "onerror" para los objetos Image :-)
Puntiagudo

Oh, duh. Usé un data: url en lugar de una imagen, y IE7 no lo admite. : P
snostorm

1
Me acabo de dar cuenta de que puedo hacer que IE8 funcione correctamente con data: urls, y que IE7 arroje un error; el problema es que no los admiten directamente en javascript. Ver: jsfiddle.net/HaLXz
snostorm

36

Solución preferida en HTML5

<picture>
  <source srcset="/path/to/image.webp" type="image/webp">
  <img src="/path/to/image.jpg" alt="insert alt text here">
</picture>

Wiki en el W3C


2
Funciona perfectamente, type="image/webp"es fundamental para que el navegador lo omita si tiene un formato desconocido.
adrianTNT

6
Esto es bueno, pero no funciona para imágenes de fondo, y si está adaptando webp a un sitio, y requiere modificar su html, lo que también significa modificar todos los lugares en su css que hacen referencia a la etiqueta img.
kloddant

1
Sí, esta es la mejor solución para el problema. Gracias
Janith

¿Todos los navegadores que admiten webp también admiten el elemento de imagen?
molerat

23

Forma oficial de Google:

Dado que algunos navegadores antiguos tienen soporte parcial para webp , es mejor ser más específico qué función webp está intentando usar y detectar esta función específica, y aquí está la recomendación oficial de Google sobre cómo detectar una función webp específica:

// check_webp_feature:
//   'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'.
//   'callback(feature, isSupported)' will be passed back the detection result (in an asynchronous way!)
function check_webp_feature(feature, callback) {
    var kTestImages = {
        lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
        lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
        alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
        animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
    };
    var img = new Image();
    img.onload = function () {
        var result = (img.width > 0) && (img.height > 0);
        callback(feature, result);
    };
    img.onerror = function () {
        callback(feature, false);
    };
    img.src = "data:image/webp;base64," + kTestImages[feature];
}

Ejemplo de uso:

check_webp_feature('lossy', function (feature, isSupported) {
    if (isSupported) {
        // webp is supported, 
        // you can cache the result here if you want
    }
});

Tenga en cuenta que la carga de imágenes no se bloquea y es asincrónica . Esto significa que cualquier código que dependa del soporte de WebP debe colocarse preferiblemente en la función de devolución de llamada.

También tenga en cuenta que otras soluciones sincrónicas no funcionarán bien con Firefox 65



12

Aquí hay una versión de la respuesta de James Westgate en ES6.

function testWebP() {
    return new Promise(res => {
        const webP = new Image();
        webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
        webP.onload = webP.onerror = () => {
            res(webP.height === 2);
        };        
    })
};

testWebP().then(hasWebP => console.log(hasWebP));

FF64: falso

FF65: verdadero

Chrome: verdadero

Me encanta la respuesta sincrónica de Rui Marques, pero desafortunadamente FF65 todavía devuelve falso a pesar de tener la capacidad de mostrar WebP.


8

Aquí está el código sin tener que solicitar una imagen. Actualizado con el nuevo violín de qwerty.

http://jsfiddle.net/z6kH9/

function testWebP(callback) {
    var webP = new Image();
    webP.onload = webP.onerror = function () {
        callback(webP.height == 2);
    };
    webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
};

testWebP(function(support) {
    document.body.innerHTML = support ? 'Yeah man!' : 'Nope';
});

5
Eso estaba completamente roto para mí. Lo bifurqué y lo hice funcionar: jsfiddle.net/z6kH9
qwerty

¿Funcionará esto en todos los navegadores? Me refiero a los problemas de otras soluciones en Safari + FF65 +.
molerat

Creo que esta es la mejor solución. Aún me gustaría ver las pruebas en los navegadores más antiguos y en los navegadores que no son compatibles con webp.
Kostanos

5

WebPJS utiliza una detección de soporte WebP más inteligente sin necesidad de imágenes externas: http://webpjs.appspot.com/


La secuencia de comandos que usan para cargar su archivo se puede usar sin usar realmente su archivo. Simplemente reemplace el interior con lo que le gustaría hacer si no hay soporte WebP.
tgrosinger

1
Parece que están usando URL de datos, con eso terminé usando.
snostorm

4

Descubrí que la función de detección de soporte webp requiere más de 300 ms cuando la página tiene mucho JavaScript. Así que escribí un script con funciones de almacenamiento en caché:

  • caché de script
  • caché de almacenamiento local

Solo detectará una vez cuando el usuario acceda por primera vez a la página.

/**
 * @fileOverview WebP Support Detect.
 * @author ChenCheng<sorrycc@gmail.com>
 */
(function() {

  if (this.WebP) return;
  this.WebP = {};

  WebP._cb = function(isSupport, _cb) {
    this.isSupport = function(cb) {
      cb(isSupport);
    };
    _cb(isSupport);
    if (window.chrome || window.opera && window.localStorage) {
      window.localStorage.setItem("webpsupport", isSupport);
    }
  };

  WebP.isSupport = function(cb) {
    if (!cb) return;
    if (!window.chrome && !window.opera) return WebP._cb(false, cb);
    if (window.localStorage && window.localStorage.getItem("webpsupport") !== null) {
      var val = window.localStorage.getItem("webpsupport");
      WebP._cb(val === "true", cb);
      return;
    }
    var img = new Image();
    img.src = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
    img.onload = img.onerror = function() {
      WebP._cb(img.width === 2 && img.height === 2, cb);
    };
  };

  WebP.run = function(cb) {
    this.isSupport(function(isSupport) {
      if (isSupport) cb();
    });
  };

})();

3

Hay una forma de probar la compatibilidad con webP al instante . Es sincronizado y preciso, por lo que no es necesario esperar una devolución de llamada para renderizar imágenes.

function testWebP = () => {
    const canvas = typeof document === 'object' ? 
    document.createElement('canvas') : {};
    canvas.width = canvas.height = 1;
    return canvas.toDataURL ? canvas.toDataURL('image/webp').indexOf('image/webp') === 5 : false;
}

Este método mejoró dramáticamente mi tiempo de renderizado


5
no funciona en Firefox ... que admite image/webppero devuelve falso en este caso (pero funciona correctamente en Safari y Chrome)
a14m

2

aquí hay una función simple con Promise basada en la respuesta de Pointy

let webpSupport = undefined // so we won't have to create the image multiple times
const webp1Px = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA'

function isWebpSupported () {
  if (webpSupport !== undefined) {
    return Promise.resolve(webpSupport)
  }

  return new Promise((resolve, _reject) => {
    const img = new Image()
    img.onload = () => {
      webpSupport = !!(img.height > 0 && img.width > 0);
      resolve(webpSupport)
    }
    img.onerror = () => {
      webpSupport = false
      resolve(webpSupport)
    }
    img.src = webp1Px
  })
}

Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor de la respuesta a largo plazo.
Nic3500

Pensé que está bastante claro, intentamos cargar una imagen webp desde una cadena base64 (que tiene 1px de ancho y alto), si la cargamos correctamente (se llama onload) es compatible, si no (se llama un error) no lo es, simplemente lo envolví en una promesa.
Liron Navon

2

Mi versión corta. Lo uso para dar navegador webP o jpg / png.

Google come esto, y el viejo iphone (f̶u̶c̶k̶i̶n̶g̶ ̶s̶h̶e̶e̶t̶ -safari) también funcionan muy bien.

function checkWebP(callback) {
    var webP = new Image();
    webP.onload = webP.onerror = function () {
        callback(webP.height == 2);
    };
    webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
};

checkWebP(function(support) {
      if(support) {
          //Do what you whant =)
         console.log('work webp');
      }else{
          //Do what you whant =)
         console.log('not work, use jgp/png')
      }
      
})


2

/* Here's a one-liner hack that works (without the use/need of any 
   externals...save bytes)...

Your CSS... */

body.no-webp .logo {
  background-image: url('logo.png');
}

body.webp .logo {
  background-image: url('logo.webp');
}
...
<body>
  <!--
  The following img tag is the *webp* support checker. I'd advise you use any 
  (small-sized) image that would be utilized on the current page eventually 
  (probably an image common to all your pages, maybe a logo) so that when 
  it'll be (really) used on the page, it'll be loaded from cache by the 
  browser instead of making another call to the server (for some other image 
  that won't be).

  Sidebar: Using 'display: none' so it's not detected by screen readers and 
  so it's also not displayed (obviously). :)
  -->
  <img 
    style='display: none'
    src='/path/to/low-sized-image.webp'
    onload="this.parentNode.classList.add('webp')"
    onerror="this.parentNode.classList.add('no-webp')"
  />
  ...
</body>


   <!-- PS. It's my first answer on SO. Thank you. :) -->


1

Imágenes WebP con htaccess

Coloque lo siguiente en su .htaccessarchivo y las imágenes jpg / png serán reemplazadas por imágenes WebP si se encuentran en la misma carpeta.

<IfModule mod_rewrite.c>
  RewriteEngine On

  # Check if browser support WebP images
  RewriteCond %{HTTP_ACCEPT} image/webp

  # Check if WebP replacement image exists
  RewriteCond %{DOCUMENT_ROOT}/$1.webp -f

  # Serve WebP image instead
  RewriteRule (.+)\.(jpe?g|png)$ $1.webp [T=image/webp,E=accept:1]
</IfModule>

<IfModule mod_headers.c>
  Header append Vary Accept env=REDIRECT_accept
</IfModule>

<IfModule mod_mime.c>
  AddType image/webp .webp
</IfModule>

Leer más aquí


1

Extensión Webp Detectar y reemplazar JavaScript:

 async function supportsWebp() {
  if (!self.createImageBitmap) return false;

  const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
  const blob = await fetch(webpData).then(r => r.blob());
  return createImageBitmap(blob).then(() => true, () => false);
}

(async () => {
  if(await supportsWebp()) {
    console.log('webp does support');
  }
  else {
    $('#banners .item').each(function(){
        var src=$(this).find('img').attr('src');
        src = src.replace(".webp", ".jpg");
        $(this).find('img').attr('src',src);
    });
    console.log('webp does not support');
  }
})();

0

Usando la respuesta de @ Pointy, esto es para Angular 2+:

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class ImageService {
    private isWebpEnabledSource = new Subject<boolean>();

    isWebpEnabledAnnounced$ = this.isWebpEnabledSource.asObservable();

    isWebpEnabled() {
        let webpImage = new Image();

        webpImage.src = 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==';

        webpImage.onload = () => {
            if (webpImage.width === 2 && webpImage.height === 1) {
                this.isWebpEnabledSource.next(true);
            } else {
                this.isWebpEnabledSource.next(false);
            }
        }
    }
}

0

Versión mejorada para manejar Firefox basada en Rui Marques. Agregué el escaneo de las diferentes cadenas en función de los comentarios a esa respuesta.

Si la comunidad acepta esta mejora, se debe editar en esa respuesta.

function canUseWebP()
{
    var elem = document.createElement('canvas');

    if (!!(elem.getContext && elem.getContext('2d')))
    {
        var testString = (!(window.mozInnerScreenX == null)) ? 'png' : 'webp';
        // was able or not to get WebP representation
        return elem.toDataURL('image/webp').indexOf('data:image/' + testString) == 0;
    }

    // very old browser like IE 8, canvas not supported
    return false;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.