Виявлення підтримки WebP


96

Як я можу виявити підтримку WebP через Javascript? Я хотів би скористатися виявленням функцій, а не виявленням браузера, якщо це можливо, але я не можу знайти спосіб це зробити. Modernizr ( www.modernizr.com ) не перевіряє його.


1
Якщо ви завантажуєте таке зображення в елемент Image, а потім перевіряєте ширину та висоту у браузері, який не підтримує формат, чи отримуєте ви щось?
Пойнті

(Я мав на увазі " об'єкт зображення ", а не елемент; наприклад, "новий образ ()" ...)
Точний

Виглядає добре. Я можу отримати "Я підтримую WebP" таким чином; але я не можу отримати "Я не підтримую WebP".
snostorm

3
Mike

2
@Simon_Weaver питанням та коментарям вже кілька років. Старі питання рідко «підтримуються» якимось значним чином; Ви завжди можете вільно додати нову відповідь.
Пойнті

Відповіді:


117

Це моє рішення - займає близько 6 мс, і я вважаю, що WebP - це лише функція для сучасного браузера. Використовує інший підхід, використовуючи функцію canvas.toDataUrl () замість зображення як спосіб виявлення ознаки:

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
Це має бути прийнятою відповіддю, оскільки всі інші мають затримку через вказівку на мережевий ресурс або URI даних
імал хасаранга перера

6
Спрощена версія:webp = e => document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
Антоніо Алмейда

35
Це не працює у Firefox 65, який підтримує відображення webp, але не створює URL-адресу даних webp з елемента полотна.
carlcheel

4
Люблю це, тому що це синхронно, однак @carlcheel правильний. FF 65 (який підтримує веб-підтримку) все ще повертає тут помилку :-(
RoccoB

11
Це було б чудово, якби це працювало для FF65 та Edge18. Вони обидва підтримують webp, але серіалізують полотно за допомогою "data: image / png"
undefinederror

55

Я думаю, що щось подібне може спрацювати:

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

У Firefox та IE обробник "onload" просто взагалі не буде викликаний, якщо зображення неможливо зрозуміти, а замість нього викликається "onerror".

Ви не згадали jQuery, але як приклад того, як поводитися з асинхронною природою цієї перевірки, ви можете повернути об'єкт jQuery "Відкладений":

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

Тоді ви могли написати:

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

Ось приклад jsfiddle.


Досконаліший засіб перевірки: http://jsfiddle.net/JMzj2/29/ . Цей завантажує зображення із URL-адреси даних і перевіряє, чи завантажується він успішно. Оскільки WebP тепер також підтримує зображення без втрат, ви можете перевірити, чи підтримує поточний браузер лише WebP із втратами або WebP без втрат. (Примітка. Це неявно також перевіряє підтримку URL-адрес даних.)

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

Чудово! Це працює в FF, Chrome та IE 9. Чомусь він не працює в IE8 або IE7.
snostorm

Це працює для мене в IE7 - спробуйте jsFiddle, який я щойно прив’язав до кінця відповіді.
Пойнті

Ну, у моїй початковій відповіді просто було "завантаження" - я не знав, що навіть існує "одна помилка" для об'єктів Image :-)
Точний

О, так. Я використовував data: url замість зображення, а IE7 цього не підтримує. : P
snostorm

1
Я щойно зрозумів, що можу змусити IE8 працювати належним чином з даними: urls і змусити IE7 видавати помилку; проблема полягала в тому, що вони не підтримують їх безпосередньо у javascript. Див .: jsfiddle.net/HaLXz
snostorm

36

Краще рішення в HTML5

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

Вікі на W3C


2
Працює ідеально, type="image/webp"це критично важливо для того, щоб браузер пропустив його, якщо невідомий формат!
adrianTNT

6
Це добре, але це не працює для фонових зображень, і якщо ви переоснащуєте webp на сайт, і це вимагає модифікації вашого html, що також означає зміну всіх місць у вашому css, які посилаються на тег img.
kloddant

1
Так, це найкраще рішення проблеми. Дякую
Джаніт

Чи всі браузери, що підтримують webp, також підтримують елемент зображення?
molerat

24

Офіційний спосіб від Google:

Оскільки деякі старі браузери мають часткову підтримку webp , тому краще бути більш конкретним, яку функцію webp ви намагаєтесь використовувати та виявити цю особливість, і ось офіційна рекомендація Google щодо виявлення певної функції 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];
}

Приклад використання:

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

Зауважте, що завантаження зображень є неблокуючим та асинхронним . Це означає, що будь-який код, який залежить від підтримки WebP, бажано вводити у функцію зворотного виклику.

Також зауважте, що інші синхронні рішення не працюватимуть добре з Firefox 65


17

Це старе питання, але Modernizr тепер підтримує виявлення Webp.

http://modernizr.com/download/

Шукайте img-webpпід Неядерні детектори.


1
Джерело корисно подивитися, що вони зробили github.com/Modernizr/Modernizr/blob/…
Simon_Weaver

Для мене це працювало досить надійно, і це дозволяє працювати з класами css .webp та .no-webp для більшої гнучкості.
molerat

12

Ось версія відповіді Джеймса Вестгейта в 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: помилково

FF65: правда

Chrome: правда

Мені подобається синхронна відповідь від Rui Marques, але, на жаль, FF65 все ще повертає false, незважаючи на можливість відображення WebP.


8

Ось код без необхідності запитувати зображення. Оновлено новою скрипкою 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
Це було для мене повністю зламано. Я роздвоївся
qwerty

Чи буде це працювати у всіх браузерах? Я маю на увазі питання інших рішень у Safari + FF65 +.
molerat

Я думаю, що це найкраще рішення. Все-таки хотів би переглянути тести на старіші браузери та браузери, які не підтримують webp.
Костанос,

5

WebPJS використовує розумніше виявлення підтримки WebP без зовнішніх зображень: http://webpjs.appspot.com/


Сценарій, який вони використовують для завантаження свого файлу, можна використовувати фактично не використовуючи їх файл. Просто замініть внутрішню частину на те, що ви хотіли б зробити, якщо немає підтримки WebP.
tgrosinger

1
Схоже, вони використовують URL-адреси даних, саме з цим я і закінчив.
snostorm

4

Я виявив, що для виявлення функцій підтримки webp потрібно 300 + мс, якщо сторінка важка для JavaScript. Тому я написав сценарій з функціями кешування:

  • кеш скриптів
  • кеш місцевого зберігання

Він виявить лише один раз, коли користувач вперше зайде на сторінку.

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

Існує спосіб миттєво перевірити підтримку webP . Він синхронізований і точний, тому немає необхідності чекати зворотного дзвінка, щоб відтворити зображення.

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

Цей метод різко покращив мій час візуалізації


5
не працює у Firefox ..., який підтримує, image/webpале повертає false у цьому випадку (але працює як у Safari, так і в Chrome)
a14m,

2

ось проста функція з Promise на основі відповіді 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
  })
}

Хоча цей код може відповісти на питання, надання додаткового контексту щодо того, як та / або чому він вирішує проблему, покращило б довгострокову цінність відповіді.
Nic3500

Я вважав, що це досить зрозуміло, ми намагаємось завантажити зображення webp із рядка base64 (який має ширину та висоту 1px), якщо ми завантажили його належним чином (називається onload), воно підтримується, якщо ні (називається onerror) це ні, я просто обернув його в обіцянку.
Liron Navon

2

Моя коротка версія. Я використовую його для надання браузеру webP або jpg / png.

Google це їсть, і старий iphone (f̶u̶c̶k̶i̶n̶g̶ ̶s̶h̶e̶e̶t̶ -safari) теж чудово працює!

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

Зображення WebP з htaccess

Помістіть у свій .htaccessфайл наступне, і зображення jpg / png будуть замінені зображеннями WebP, якщо вони знайдені в тій же папці.

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

Детальніше читайте тут


1

Розширення Webp Виявлення та заміна 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

Використовуючи відповідь @ 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);
            }
        }
    }
}

0

Покращена версія для роботи з Firefox на основі Rui Marques. До цієї відповіді я додав сканування різних рядків на основі коментарів.

Якщо громада схвалить це покращення, його слід відредагувати відповідно до цієї відповіді.

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.