Detectando suporte WebP


99

Como posso detectar suporte para WebP via Javascript? Gostaria de usar a detecção de recursos em vez da detecção de navegador, se possível, mas não consigo encontrar uma maneira de fazer isso. Modernizr ( www.modernizr.com ) não verifica.


1
Se você carregar essa imagem em um elemento Image e, em seguida, verificar a largura e a altura em um navegador que não suporta o formato, você obteve alguma coisa?
Pointy,

(Eu quis dizer " objeto de imagem ", não elemento; como "nova imagem ()" ...)
Pontudo

Parece bom. Posso obter um "Aceito WebP" desta forma; mas não consigo obter um "Não sou compatível com WebP".
Dieki

3
Mike

2
@Simon_Weaver a pergunta e os comentários têm vários anos. Perguntas antigas raramente são "mantidas" de qualquer maneira significativa; você está sempre livre para adicionar uma nova resposta.
Pointy,

Respostas:


118

Esta é a minha solução - está demorando cerca de 6 ms e estou considerando que o WebP é apenas um recurso para um navegador moderno. Usa uma abordagem diferente usando a função canvas.toDataUrl () em vez da imagem como forma de detectar o recurso:

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 deve ser a resposta aceita, pois todas as outras apresentam um atraso por apontar para um recurso de rede ou URI de dados
imal hasaranga perera

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

40
Isso não funciona no Firefox 65, que suporta a exibição de webp, mas não cria um url de dados webp a partir de um elemento canvas.
carlcheel

4
Amei isso porque é síncrono, porém @carlcheel está correto. FF 65 (que tem suporte webp) ainda retorna falso aqui :-(
RoccoB

13
Isso seria ótimo se funcionasse para FF65 e Edge18. Ambos suportam webp, mas serializam a tela com "data: image / png"
undefinederror

55

Acho que algo assim pode 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';
})();

No Firefox e no IE, o manipulador "onload" simplesmente não será chamado se a imagem não puder ser entendida e, em vez disso, o "onerror" será chamado.

Você não mencionou jQuery, mas como um exemplo de como lidar com a natureza assíncrona dessa verificação, você pode retornar um 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();
}

Então você pode escrever:

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

Aqui está um exemplo jsfiddle.


Um verificador mais avançado: http://jsfiddle.net/JMzj2/29/ . Este carrega imagens de um URL de dados e verifica se ele carrega com sucesso. Como o WebP agora também oferece suporte a imagens sem perdas, você pode verificar se o navegador atual oferece suporte apenas para WebP com perdas ou também WebP sem perdas. (Observação: isso também verifica implicitamente o suporte a URL de dados.)

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");
});

Impressionante! Isso funciona no FF, Chrome e IE 9. Por algum motivo, não está funcionando no IE8 ou IE7.
dieki

Funciona para mim no IE7 - tente o jsFiddle que acabei de vincular ao final da resposta.
Pointy

Bem, minha resposta original tinha apenas o "onload" - eu não sabia que havia um "onerror" para objetos de imagem :-)
Pontudo

Oh, duh. Usei um data: url em vez de uma imagem, e o IE7 não oferece suporte para isso. : P
Dieki

1
Acabei de perceber que posso fazer o IE8 funcionar corretamente com data: urls e fazer o IE7 gerar um erro para ele; o problema é que eles não os suportam diretamente em javascript. Veja: jsfiddle.net/HaLXz
dieki

38

Solução preferida em HTML5

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

Wiki no W3C


2
Funciona perfeitamente, type="image/webp"é fundamental para o navegador ignorá-lo se o formato for desconhecido!
adrianTNT

7
Isso é bom, mas não funciona para imagens de fundo e se você estiver adaptando webp a um site, e requer a modificação de seu html, o que também significa modificar todos os locais em seu css que fazem referência à tag img.
Kloddant

1
Sim, esta é a melhor solução para o problema. Obrigado
Janith

Todos os navegadores que suportam webp também suportam o elemento de imagem?
molerat

1
Embora o HTML5 nativo seja preferível, a maioria dos navegadores carregam JPG e WEBP, o que afeta negativamente os tempos de download. Em profundidade: smashingmagazine.com/2013/05/…
Jan Werkhoven

25

Forma oficial do Google:

Como alguns navegadores antigos têm suporte parcial para webp , é melhor ser mais específico qual recurso webp você está tentando usar e detectar esse recurso específico, e aqui está a recomendação oficial do Google sobre como detectar um recurso webp específico:

// 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];
}

Exemplo de uso:

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

Observe que o carregamento da imagem não é bloqueador e é assíncrono . Isso significa que qualquer código que dependa do suporte WebP deve ser preferencialmente colocado na função de retorno de chamada.

Observe também que outras soluções síncronas não funcionarão bem com o Firefox 65



13

Aqui está uma versão da resposta de James Westgate em 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: verdadeiro

Chrome: verdadeiro

Adoro a resposta síncrona do Rui Marques, mas infelizmente FF65 continua a ser falso apesar de poder apresentar WebP.


8

Aqui está o código sem precisar solicitar uma imagem. Atualizado com o novo violino do 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
Isso foi completamente quebrado para mim. Eu fiz um fork e fiz funcionar: jsfiddle.net/z6kH9
qwerty

Isso funcionará em todos os navegadores? Estou me referindo aos problemas de outras soluções no Safari + FF65 +.
molerat

Acho que essa é a melhor solução. Ainda gostaria de ver os testes em navegadores mais antigos e em navegadores que não oferecem suporte a webp.
Kostanos

5

WebPJS usa detecção de suporte WebP mais inteligente sem a necessidade de imagens externas: http://webpjs.appspot.com/


O script que eles usam para carregar o arquivo pode ser usado sem realmente usar o arquivo. Basta substituir o interior por tudo o que você gostaria de fazer se não houver suporte para WebP.
tgrosinger

1
Parece que estão usando urls de dados, foi isso que acabei usando.
dieki

5

Descobri que a detecção de recurso de suporte webp requer 300 + ms quando a página tem muito JavaScript. Então, escrevi um script com recursos de cache:

  • cache de script
  • cache localstorage

Ele detectará apenas uma vez quando o usuário acessar a página pela primeira vez.

/**
 * @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

/* 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. :) -->


2

Existe uma maneira de testar o suporte webP instantaneamente . É sincronizado e preciso, portanto, não há necessidade de esperar por um retorno de chamada para renderizar as imagens.

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 melhorou meu tempo de renderização dramaticamente


5
não funciona no Firefox ... que suporta, image/webpmas retorna falso neste caso (mas funciona no Safari e no Chrome corretamente)
a14m

2

aqui está uma função simples com Promise baseada na resposta 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
  })
}

Embora este código possa responder à pergunta, fornecer contexto adicional sobre como e / ou por que ele resolve o problema melhoraria o valor da resposta a longo prazo.
Nic3500

Achei bem claro, tentamos carregar uma imagem webp de uma string base64 (que tem 1px de largura e altura), se carregamos corretamente (onload chamado) é compatível, se não (onerror chamado) não é, simplesmente envolvi em uma promessa.
Liron Navon

2

Minha versão curta. Eu o usei para fornecer ao navegador webP ou jpg / png.

O Google come isso, e o iphone antigo (f̶u̶c̶k̶i̶n̶g̶ ̶s̶h̶e̶e̶t̶ -safari) também funciona muito bem!

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')
      }
      
})


1

Imagens WebP com htaccess

Coloque o seguinte em seu .htaccessarquivo e as imagens jpg / png serão substituídas por imagens WebP se encontradas na mesma pasta.

<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>

Leia mais aqui


1

Extensão Webp Detectar e substituir 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');
  }
})();

1

Versão melhorada para lidar com Firefox baseada em Rui Marques. Eu adicionei a varredura para as diferentes strings com base nos comentários a essa resposta.

Se essa melhoria for aceita pela comunidade, ela deve ser editada nessa resposta.

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;
}

0

Usando a resposta de @ Pointy 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);
            }
        }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.