Wykrywanie obsługi WebP

99

Jak mogę sprawdzić obsługę WebP za pomocą JavaScript? Chciałbym, jeśli to możliwe, użyć wykrywania funkcji zamiast wykrywania przeglądarki, ale nie mogę znaleźć sposobu, aby to zrobić. Modernizr ( www.modernizr.com ) nie sprawdza tego.

dieki
źródło
1
Jeśli załadujesz taki obraz do elementu Image, a następnie sprawdzisz szerokość i wysokość w przeglądarce, która nie obsługuje tego formatu, otrzymasz coś?
Pointy,
(Miałem na myśli „ obiekt obrazu ”, a nie element; na przykład „nowy obraz ()” ...)
Pointy,
Wygląda dobrze. W ten sposób mogę uzyskać informację „Obsługuję WebP”; ale nie mogę otrzymać komunikatu „Nie obsługuję WebP”.
dieki
2
@Simon_Weaver pytanie i komentarze mają już kilka lat. Stare pytania rzadko są „utrzymywane” w jakikolwiek znaczący sposób; zawsze możesz jednak dodać nową odpowiedź.
Pointy

Odpowiedzi:

119

To jest moje rozwiązanie - trwa około 6 ms i myślę, że WebP to tylko funkcja dla nowoczesnej przeglądarki. Używa innego podejścia, używając funkcji canvas.toDataUrl () zamiast obrazu jako sposobu wykrywania funkcji:

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;
 }
}
Rui Marques
źródło
7
To powinna być akceptowana odpowiedź, ponieważ wszystkie inne mają opóźnienie z powodu wskazania zasobu sieciowego lub identyfikatora URI danych
imal hasaranga perera
6
Wersja uproszczona:webp = e => document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
António Almeida
41
Nie działa to w przeglądarce Firefox 65, która obsługuje wyświetlanie webp, ale nie tworzy adresu URL danych webp z elementu canvas.
carlcheel
4
Uwielbiam to, ponieważ jest synchroniczne, jednak @carlcheel jest poprawne. FF 65 (który obsługuje webp) nadal zwraca tutaj false :-(
RoccoB,
13
To byłoby wspaniale, gdyby pracował dla FF65 i Edge18. Oba obsługują webp, ale serializują kanwę za pomocą „data: image / png”
niezdefiniowany błąd
55

Myślę, że coś takiego może zadziałać:

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

W Firefoksie i IE, program obsługi "onload" nie zostanie w ogóle wywołany, jeśli obraz nie zostanie zrozumiany, a zamiast tego wywoływany jest "onerror".

Nie wspomniałeś o jQuery, ale jako przykład radzenia sobie z asynchroniczną naturą tego sprawdzenia możesz zwrócić obiekt 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();
}

Wtedy możesz napisać:

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

Oto przykład jsfiddle.


Bardziej zaawansowany kontroler: http://jsfiddle.net/JMzj2/29/ . Ten ładuje obrazy z adresu URL danych i sprawdza, czy ładuje się pomyślnie. Ponieważ WebP obsługuje teraz również bezstratne obrazy, możesz sprawdzić, czy aktualna przeglądarka obsługuje tylko stratny WebP, czy też bezstratny WebP. (Uwaga: to pośrednio sprawdza również obsługę adresów URL danych).

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");
});
Spiczasty
źródło
Niesamowite! Działa to w FF, Chrome i IE 9. Z jakiegoś powodu nie działa w IE8 ani IE7.
dieki
U mnie to działa w IE7 - wypróbuj jsFiddle, do którego właśnie dołączyłem na końcu odpowiedzi.
Pointy,
Cóż, moja pierwotna odpowiedź miała po prostu „onload” - nie wiedziałem, że nawet wystąpił „onerror” dla obiektów graficznych :-)
Pointy
Och, prawda. Użyłem danych: url zamiast obrazu, a IE7 tego nie obsługuje. : P
dieki
1
Właśnie zdałem sobie sprawę, że mogę sprawić, aby IE8 działał poprawnie z danymi: URL-e i IE7 wyrzuci dla niego błąd; problem polegał na tym, że nie obsługują ich bezpośrednio w javascript. Zobacz: jsfiddle.net/HaLXz
dieki
38

Preferowane rozwiązanie w HTML5

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

Wiki na W3C

Andrei Krasutski
źródło
2
Działa doskonale, type="image/webp"jest krytyczny, aby przeglądarka pominęła go w przypadku nieznanego formatu!
adrianTNT
7
Jest to dobre, ale nie działa w przypadku obrazów tła, a jeśli modernizujesz webp do witryny i wymaga to modyfikacji kodu HTML, co oznacza również modyfikację wszystkich miejsc w twoim css, które odnoszą się do tagu img.
kloddant
1
Tak, to najlepsze rozwiązanie problemu. Dzięki
Janith
Czy wszystkie przeglądarki obsługujące webp obsługują również element obrazu?
molerat
1
Chociaż preferowany jest natywny HTML5, większość przeglądarek ładuje zarówno JPG, jak i WEBP, co negatywnie wpływa na czas pobierania. Dogłębnie: smashingmagazine.com/2013/05/…
Jan Werkhoven
25

Oficjalny sposób Google:

Ponieważ niektóre stare przeglądarki częściowo obsługują WebP , lepiej jest dokładniej określić, której funkcji WebP próbujesz użyć i wykryć tę konkretną funkcję. Oto oficjalne zalecenia Google dotyczące wykrywania określonej funkcji WebP:

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

Przykładowe zastosowanie:

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

Zwróć uwagę, że ładowanie obrazu jest nieblokujące i asynchroniczne . Oznacza to, że jakikolwiek kod zależny od obsługi WebP powinien być umieszczony w funkcji wywołania zwrotnego.

Należy również pamiętać, że inne rozwiązania synchroniczne nie będą działać dobrze z przeglądarką Firefox 65

AbdelHady
źródło
17

To stare pytanie, ale Modernizr obsługuje teraz wykrywanie Webp.

http://modernizr.com/download/

Poszukaj w img-webpobszarze Wykrywania niezwiązane z rdzeniem.

Jake Wilson
źródło
1
Źródło jest przydatne, aby zobaczyć, co zrobili github.com/Modernizr/Modernizr/blob/ ...
Simon_Weaver
Dla mnie zadziałało całkiem niezawodnie i pozwala na pracę z klasami css .webp i .no-webp dla większej elastyczności.
molerat
13

Oto wersja odpowiedzi Jamesa Westgate'a w 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: fałsz

FF65: prawda

Chrome: prawda

Uwielbiam synchroniczną odpowiedź od Rui Marques, ale niestety FF65 nadal zwraca fałsz pomimo możliwości wyświetlania WebP.

RoccoB
źródło
8

Oto kod bez konieczności żądania obrazu. Zaktualizowano o nowe skrzypce 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';
});
James Westgate
źródło
5
To było dla mnie całkowicie zepsute. Rozwidliłem
qwerty
Czy to zadziała we wszystkich przeglądarkach? Mam na myśli problemy innych rozwiązań w Safari + FF65 +.
molerat
Myślę, że to jest najlepsze rozwiązanie. Nadal chciałbym zobaczyć testy na starszych przeglądarkach i przeglądarkach, które nie obsługują webp.
Kostanos
5

WebPJS wykorzystuje inteligentniejsze wykrywanie wsparcia WebP bez zewnętrznych obrazów: http://webpjs.appspot.com/

unxed
źródło
Skrypt, którego używają do ładowania swojego pliku, może być używany bez faktycznego korzystania z ich pliku. Po prostu wymień wnętrze na to, co chcesz zrobić, jeśli nie ma obsługi WebP.
tgrosinger
1
Wygląda na to, że używają adresów URL danych, z których ostatecznie skorzystałem.
dieki
5

Zauważyłem, że funkcja wykrywania obsługi WebP wymaga 300 + ms, gdy strona jest obciążona JavaScript. Napisałem więc skrypt z funkcjami buforowania:

  • pamięć podręczna skryptów
  • lokalna pamięć podręczna

Wykrywa tylko raz, kiedy użytkownik po raz pierwszy uzyskuje dostęp do strony.

/**
 * @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();
    });
  };

})();
sorrycc
źródło
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. :) -->

Sunday Power Inemesit
źródło
2

Istnieje sposób na natychmiastowe przetestowanie obsługi WebP . Jest zsynchronizowany i dokładny, więc nie trzeba czekać na wywołanie zwrotne, aby renderować obrazy.

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

Ta metoda znacznie poprawiła mój czas renderowania

Guy Sopher
źródło
5
nie działa na Firefoksie ... który obsługuje, image/webpale zwraca wartość false w tym przypadku (ale działa poprawnie zarówno w Safari, jak i Chrome)
14m
2

oto prosta funkcja z Obietnicą opartą na odpowiedzi 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
  })
}
Liron Navon
źródło
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Nic3500
Pomyślałem, że to całkiem jasne, próbujemy załadować obraz webp z ciągu base64 (który ma 1px szerokości i wysokości), jeśli załadowaliśmy go poprawnie (wywołano onload), jest obsługiwany, jeśli nie (wywołano onerror) to nie jest, po prostu go opakowałem w obietnicy.
Liron Navon
2

Moja krótka wersja. Używam go, aby nadać przeglądarce webP lub jpg / png.

Google je to, a stary iPhone (f̶u̶c̶k̶i̶n̶g̶ ̶s̶h̶e̶e̶t̶ -safari) też działa świetnie!

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

Alexander Sanik
źródło
1

Obrazy WebP z htaccess

Umieść następujące elementy w swoim .htaccesspliku, a obrazy jpg / png zostaną zastąpione obrazami WebP, jeśli zostaną znalezione w tym samym folderze.

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

Przeczytaj więcej tutaj

Andrei Krasutski
źródło
1

Rozszerzenie Webp Wykryj i zamień 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');
  }
})();
Nieograniczone isa
źródło
1

Ulepszona wersja do obsługi Firefoksa oparta na Rui Marques. Dodałem skanowanie różnych ciągów na podstawie komentarzy do tej odpowiedzi.

Jeśli poprawienie zostanie zaakceptowane przez społeczność, należy je zredagować w odpowiedzi.

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;
}
Jeffrey Simon
źródło
0

Używając odpowiedzi @ Pointy jest to dla 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);
            }
        }
    }
}
Serj Sagan
źródło