Обнаружение поддержки WebP


96

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


1
Если вы загрузите такое изображение в элемент Image, а затем проверите ширину и высоту в браузере, который не поддерживает этот формат, получите ли вы что-нибудь?
Pointy

(Я имел в виду « объект изображения », а не элемент; например, «новое изображение ()» ...)
Pointy

Выглядит хорошо. Таким образом я могу получить ответ «Я поддерживаю WebP»; но я не могу получить сообщение «Я не поддерживаю WebP».
Snostorm


2
@Simon_Weaver, вопрос и комментарии - всем несколько лет назад. Старые вопросы редко «поддерживаются» сколь-нибудь существенным образом; однако вы всегда можете добавить новый ответ.
Pointy

Ответы:


118

Это мое решение - занимает около 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 данных
imal hasaranga perera 02

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 (у которого есть поддержка webp) по-прежнему возвращает false здесь :-(
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 «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();
}

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

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, который я только что связал с концом ответа.
Pointy

Ну, в моем исходном ответе просто была «загрузка» - я даже не знал, что для объектов Image есть «ошибка» :-)
Pointy

Ах да. Я использовал URL-адрес data: вместо изображения, и IE7 этого не поддерживает. : P
snostorm

1
Я только что понял, что могу заставить IE8 правильно работать с URL-адресами data: и заставить 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, также поддерживают элемент изображения?
молерат

23

Официальный способ от 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 для большей гибкости.
молерат

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: правда

Мне нравится синхронный ответ от Руи Маркеса, но, к сожалению, 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
Для меня это было полностью нарушено. Я раздвоил его и заставил работать: jsfiddle.net/z6kH9
qwerty

Будет ли это работать во всех браузерах? Я имею в виду проблемы других решений в Safari + FF65 +.
молерат

Думаю, это лучшее решение. По-прежнему хотелось бы увидеть тесты в старых браузерах и браузерах, не поддерживающих 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)
14

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 05

Я думал, что это довольно ясно, мы пытаемся загрузить изображение webp из строки base64 (которая имеет ширину и высоту 1 пиксель), если мы загрузили его правильно (вызывается 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.