检测 WebP 支持

如何通过 Javascript 检测对 WebP 的支持?如果可能的话,我想使用特征提取而不是浏览器检测,但我找不到这样做的方法。现代化(Www.modernizr.com)不检查它。

82758 次浏览

我觉得这样可能行得通:

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

WebPJS 使用更智能的 WebP 支持检测,不需要任何外部图像: Http://webpjs.appspot.com/

这里的代码不需要请求一个图像。更新与 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';
});

我发现当页面使用 JavaScript 时,webp 支持的特性检测需要300多毫秒。因此,我编写了具有缓存特性的 一个脚本:

  • 脚本缓存
  • 本地存储高速缓存

当用户第一次访问页面时,它只检测一次。

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


})();

这是一个古老的问题,但现在的现代化支持 Webp 检测。

Http://modernizr.com/download/

在非核心探测下寻找 img-webp

这就是我的解决方案——大约需要6毫秒,我认为 WebP 只是现代浏览器的一个特性。使用另一种方法,使用虚拟画布.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;
}
}

具有 htaccess 的 WebP 图像

将以下内容放在 .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>

阅读更多 给你

HTML5中的首选解决方案

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

W3C 上的维基

有一种方法可以测试 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;
}

这种方法大大提高了我的渲染时间

使用@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);
}
}
}
}

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

这里有一个简单的函数,基于 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
})
}

以下是詹姆斯 · 韦斯特盖特在 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));

假的

没错

Chrome: 真实

我喜欢来自芮马克斯的同步回答,但不幸的是 FF65仍然返回假,尽管有能力显示 WebP。

谷歌官方说法:

由于一些旧的浏览器有 对 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 支持的代码最好放在回调函数中。

还要注意,其他同步解决方案在 < strong > Firefox65中不能很好地工作

我的简短版本。我用它来给浏览器 webP 或 jpg/png。

谷歌吃这个,和旧的 iphone (f u c k i 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')
}
      

})

改进的版本可以处理基于芮马克斯的 Firefox。我根据这个答案的注释添加了不同字符串的扫描。

如果这种改进被社区所接受,那么它应该被编辑成那个答案。

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

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

这是一个 HTML/Javascript 混合方法,可以让您按照首选项(您的首选项)来确定支持的图像类型。在这个示例中,它将返回浏览器中第一个支持的图像类型,并检查 AVIF、 WebP、 JpegXL 和 JPG。

<picture style="display:none;">
<source type=image/avif srcset="data:image/avif;base64,AAAAFGZ0eXBhdmlmAAAAAG1pZjEAAACgbWV0YQAAAAAAAAAOcGl0bQAAAAAAAQAAAB5pbG9jAAAAAEQAAAEAAQAAAAEAAAC8AAAAGwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAARWlwcnAAAAAoaXBjbwAAABRpc3BlAAAAAAAAAAQAAAAEAAAADGF2MUOBAAAAAAAAFWlwbWEAAAAAAAAAAQABAgECAAAAI21kYXQSAAoIP8R8hAQ0BUAyDWeeUy0JG+QAACANEkA= 1x">
<source type=image/webp srcset="data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA 1x">
<source type=image/jxl srcset="data:image/jxl;base64,/woAEBAJCAQBACwASxLFgoUJEP3D/wA= 1x">
<img onload=console.log(this.currentSrc.substring(this.currentSrc.indexOf(':')+1,this.currentSrc.indexOf(';'))) src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQIAHAAcAAD/2wBDAAMDAwMDAwMDAwMEBAQEBAYFBQUFBgkGBwYHBgkOCAoICAoIDgwPDAsMDwwWEQ8PERYZFRQVGR4bGx4mJCYyMkP/wgALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/aAAgBAQAAAABU/wD/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/AH//2Q==">
</picture>

您可以将 log 函数替换为您需要的任何内容。

这种做法的好处是:

  1. 您不必在 Javascript 中创建和查询大量对象,因此它是高效的
  2. 浏览器不需要获取任何图像,它们是内联编码的,所以它是快速和同步的。你可以把这个放在任何地方,不用回调就可以在下一行得到答案。
  3. 浏览器只会为支持的第一行创建一个图像结果,因此它是高效的。
  4. 通过添加一行,很容易添加未来的图像支持。
  5. 您可以根据在应用程序中使用的任何优先级对图像进行排序。
  6. 您可以通过修剪您不关心的图像类型将其转换为单独的测试。
  7. 即使不支持 PICTURE 元素,但是需要 currentSrc,这也应该可以工作,因此 IE11将失败。.在这种情况下,只需在 img 中测试 currentSrc,或者假设 JPG 总是内置支持。

编辑: 删除了进入示例的换行符,谢谢。

好消息,它在 Safari 上能用。

document.addEventListener('DOMContentLoaded', function() {
testWebP(document.body)
})


function testWebP(elem) {
const webP = new Image();
webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
webP.onload = webP.onerror = function () {
webP.height === 2 ? elem.classList.add('webp-true') : elem.classList.add('webp-false')
}
console.log(webP)
}

消息来源: https://gist.github.com/Protoff/d6643387f03d47b44b2d7c3cf7b3e0a0

上述解决方案可能不适用于 Safari 和 firefox。因此,我开始寻找一个更健壮的解决方案,偶然发现了一个关于 webp 支持的很棒的库: 网络英雄我们只能从这个库中获取 detectWebpSupport函数:

var __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function(resolve) {
resolve(value);
});
}
return new(P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}


function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}


function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function(thisArg, body) {
var _ = {
label: 0,
sent: function() {
if (t[0] & 1) throw t[1];
return t[1];
},
trys: [],
ops: []
},
f, y, t, g;
return g = {
next: verb(0),
"throw": verb(1),
"return": verb(2)
}, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
return this;
}), g;


function verb(n) {
return function(v) {
return step([n, v]);
};
}


function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return {
value: op[1],
done: false
};
case 5:
_.label++;
y = op[1];
op = [0];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2]) _.ops.pop();
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
} catch (e) {
op = [6, e];
y = 0;
} finally {
f = t = 0;
}
if (op[0] & 5) throw op[1];
return {
value: op[0] ? op[1] : void 0,
done: true
};
}
};


function detectWebpSupport() {
return __awaiter(this, void 0, void 0, function() {
var testImageSources, testImage, results;
return __generator(this, function(_a) {
switch (_a.label) {
case 0:
testImageSources = [
"data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==",
"data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA="
];
testImage = function(src) {
return new Promise(function(resolve, reject) {
var img = document.createElement("img");
img.onerror = function(error) {
return resolve(false);
};
img.onload = function() {
return resolve(true);
};
img.src = src;
});
};
return [4 /*yield*/ , Promise.all(testImageSources.map(testImage))];
case 1:
results = _a.sent();
return [2 /*return*/ , results.every(function(result) {
return !!result;
})];
}
});
});
}


detectWebpSupport().then(d => console.log('does it support?', d))

//* WebP 支持检查 从“反应”导入{ useState,useEffect } ;

const WebpSupportCheck = (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];
}


const IsWebpSupported = () => {
const [state, setState] = useState()
useEffect(() => {


WebpSupportCheck('lossy', function (feature, isSupported) {
if (isSupported) {
setState(true)
} else {
setState(false)
}
})


}, [state])
return state
}


export default IsWebpSupported