聊聊图片那些事

图片的底层原理

计算机中的颜色表示

计算机在表示颜色的时候,有两种形式,一种称作索引颜色(Index Color),一种称作直接颜色(Direct Color)

  • 索引色

用一个数字来代表(索引)一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。这种方式只能存储有限种颜色,比如某计算机系统中,使用一个字节的数字来索引一种颜色,一个字节 2^8 表示一个颜色,也就是最多支持 256 种颜色。

  • 直接色

使用四个数字来代表一种颜色,这四个数字分别代表这个颜色中红色、绿色、蓝色以及透明度

图片构成方式

  • 位图(Bitmap)

又叫栅格图、点阵图,使用像素阵列来表示图像。位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变图像的显示效果。位图根据位深度,有 1、4、8、16、24、32 位图像等。位图放大会失真变模糊。

  • 矢量图

计算机图形学中用点、直线或者多边形等基于数学方程的几何图元表示图像。相比较位图,矢量图保存最少的信息,体积更小,缩放不会失真。

为什么SVG格式的图片放大不会失真模糊呢?

SVG(可缩放矢量图形)是一种基于XML的图形格式,与传统的位图图形(如JPEG、PNG)不同,SVG使用矢量图形描述图像。它描述图像的方式是基于数学公式和几何路径,SVG图像使用路径来定义图形,路径包含了一系列的命令和坐标点,用于绘制线条、曲线、形状等,当你放大SVG图像时,浏览器或图形软件会重新计算路径的坐标,并按比例进行缩放,而不会引入额外的像素或失真,SVG图像也可以包含文本信息,而这些文本信息同样是基于矢量的,因此也可以无损缩放。

图片格式

不同的图片格式在不同的应用场景下有各自的优缺点。以下是常见的图片格式(JPEG、PNG、GIF、WebP)的对比:

图片格式 优点 缺点 使用场景
JPEG - 高压缩率 - 有损压缩 - 照片、复杂图像
- 广泛支持 - 可能出现压缩失真
PNG - 无损压缩 - 文件大小相对较大 - 透明背景、图标、简单图像
- 支持透明度 - 不适用于照片
GIF - 支持动画 - 有限色彩 - 简单动画、图标
- 支持透明度 - 不适用于照片
WebP - 高压缩率 - 浏览器支持相对有限 - 现代浏览器、要求高压缩率和透明度
- 支持透明度 - 部分浏览器不支持

选择合适的图片格式取决于具体的应用场景和需求。JPEG适用于照片,PNG适用于需要保持图像质量和透明背景的场景,GIF适用于简单动画和图标,而WebP则是一种现代的、通常具有较高压缩率的格式,但需考虑浏览器兼容性。

图片跨域问题

图片(以及其他资源,如脚本、样式表等)存在跨域问题是由浏览器的同源策略(Same-Origin Policy)引起的。同源策略是一种安全机制,限制了来自不同源(域名、协议、端口)的页面对资源的访问,目的是防止恶意网站通过一些手段获取用户的敏感信息。

同源策略对于JavaScript访问其他域的资源有着严格的限制。解决图片跨域主要有以下方式:

  1. CORS(跨域资源共享) 在提供图片的服务器上配置CORS头,允许特定域(你的网站域)的访问。

  2. JSONP(仅适用于GET请求) 使用Script标签 JSONP利用了Script标签不受同源策略限制的特性,通过动态创建Script标签来加载跨域资源。

  3. 代理服务器:在你的域内设置一个代理服务器,该代理服务器可以访问其他域上的资源。前端通过访问代理服务器来获取资源,绕过了同源策略。

图片相关优化操作

  • 图片懒加载。使用intersectionObserve或者使用 getBoundingClientRect结合视窗的高度来判断是否在可视区域内,如果在即将预设的data-src属性赋值到img标签src属性即可

  • 图片预加载。动态创建img标签,提前加载图片,后续可从缓存中读取。

  • 图片手动压缩。 使用压缩工具进行手动压缩

  • 图片CDN加速。 静态资源放在CDN上,可以加速用户访问速度

  • 图片加载失败处理

js 复制代码
const imgOnError = (e) => {
  e.currentTarget.onerror = null
  e.currentTarget.src = '默认占位图片地址'
  e.currentTarget.className = 'error'
}

// react
<img src={oss_url} onError={imgOnError}/>
  • img标签在移动端如果src上没有一个有效的值,会产生一个白色的边框。有时候我们为了兜底隐藏该边框需要做一个处理。
css 复制代码
img[src=""],
img:not([src]) {
    opacity: 0;
}
  • 图片裁剪。 图片裁剪大致流程如下:

详细流程推荐阅读👉:从零开始做一个图片裁剪组件

  • 图片添加水印

推荐阅读👉:# 前端水印实现方案

图片资源优化

前端根据不同的设备的分辨率自动调整图片大小

1.使用srcsetsizes属性

srcset 属性用于指定一组备选的图片资源,以逗号分隔每个图片资源并指定对应的分辨率宽度,浏览器会根据当前设备的像素密度(DPR)和 CSS 像素比例(devicePixelRatio)来选择最佳匹配的图片资源进行加载;而 sizes 属性则用于设置媒体查询条件或者视口尺寸条件,用于判断图片显示在页面中的大小。

以下是一个示例代码:

js 复制代码
<img src="small.jpg"
     srcset="small.jpg 640w, medium.jpg 960w, large.jpg 1200w"
     sizes="(max-width: 480px) 100vw, (max-width: 1024px) 50vw, 33.3vw">

在上述示例中,srcset 属性列出了三种不同大小的备选图片资源,并指定了相应的分辨率宽度。如果当前设备的窗口宽度为 800px,浏览器将会选择 medium.jpg 这个备选图片资源进行加载。而 sizes 属性则设置了两个条件,当窗口宽度小于等于 480px 时,图片将会占满整个窗口宽度;当窗口宽度大于 480px 且小于等于 1024px 时,图片将会占据窗口宽度的一半;当窗口宽度大于 1024px 时,图片将会占据窗口宽度的三分之一。

需要注意的是,srcset 和 sizes 属性并不兼容所有浏览器,使用前建议进行兼容性检查。

2.模拟scrsetsizes属性 首先,将 img 元素的 src 属性设置为最小分辨率的图片,然后在 JavaScript 中获取该元素并动态地更新其 src 属性,以便在设备像素密度变化时加载最合适的图片资源。

html 复制代码
<img id="my-image" src="small.jpg">
js 复制代码
function getBestImageSrcset() {
  var windowWidth = window.innerWidth;
  var images = [
    {srcset: "small.jpg", width: 640},
    {srcset: "medium.jpg", width: 960},
    {srcset: "large.jpg", width: 1200}
  ];
  var bestImage = images[0];
  for (var i = 1; i < images.length; i++) {
    if (windowWidth >= images[i].width && images[i].width > bestImage.width) {
      bestImage = images[i];
    }
  }
  return bestImage.srcset;
}

var myImage = document.getElementById("my-image");
myImage.src = getBestImageSrcset();

在上述示例中,getBestImageSrcset() 函数会根据当前设备分辨率的宽度和一组备选的图片资源(包括 srcset 和宽度)计算出最佳图片资源,并返回其相对路径。接下来,通过 JavaScript 获取 img 元素并动态更新其 src 属性为最佳图片资源的相对路径。这样可以确保在不同设备的分辨率下都可以加载最合适的图片资源。

实际开发中需要根据具体情况进行优化和调整。这种方式也可能存在一定的兼容问题。

3.使用成熟的第三方库

  • Picturefill
  • next/image

4.使用CSS媒体查询:可以通过CSS的媒体查询来根据设备的屏幕尺寸和像素密度,为不同的设备设置不同的背景图片。

css 复制代码
.my-element {
  background-image: url('low-res.jpg');
}

@media only screen and (min-width: 768px) {
  .my-element {
    background-image: url('medium-res.jpg');
  }
}

@media only screen and (min-width: 1200px) {
  .my-element {
    background-image: url('high-res.jpg');
  }
}
  1. 使用JavaScript动态加载:通过JavaScript可以根据设备的特性和屏幕信息动态选择加载不同分辨率的图片。可以通过window.devicePixelRatio获取设备的像素密度,并根据需要动态创建<img>标签并设置对应的图片路径。
js 复制代码
var image = new Image();
var pixelRatio = window.devicePixelRatio;

if (pixelRatio >= 2) {
  image.src = 'high-res.jpg';
} else if (pixelRatio >= 1.5) {
  image.src = 'medium-res.jpg';
} else {
  image.src = 'low-res.jpg';
}

document.body.appendChild(image);

通过上述方式,可以根据不同设备的屏幕尺寸和像素密度,提供适合的图片分辨率,以优化用户在不同设备上的图片显示效果和加载性能。

webp格式图片

webp格式图片为什么高效

1.图片压缩算法:WebP采用了一种基于预测的压缩算法,可以识别和删除图片中的冗余信息,从而实现更好的压缩效果。这种算法比传统的JPEG和PNG压缩算法更高效。

2.支持有损和无损压缩:WebP可以同时支持有损和无损压缩,这意味着它可以在保持高质量的同时实现更小的文件大小。

3.支持alpha通道压缩:WebP支持alpha通道压缩,这意味着它可以在压缩过程中保留图像的透明度信息,并产生更小的文件大小。

4.支持动态图像:WebP还支持动态图像,这种格式可以在一个文件中包含多个帧,从而可以创建动画效果。

如何解决webp格式在浏览器中不兼容的问题

WebP 是一种高效的图片格式,它可以帮助减少图片文件的大小,从而提高网页的加载速度。然而,由于某些浏览器不支持 WebP 格式,使用 WebP 格式可能会导致一些兼容性问题。以下是几种解决 WebP 格式在浏览器中不兼容的方法:

1.使用图片格式检测:在服务器端检测浏览器是否支持 WebP 格式,如果支持,则使用 WebP 格式的图片,否则使用其他格式的图片。这可以通过服务器端的代码来实现。

2.使用 polyfill:Polyfill 是一种 JavaScript 库,可以让浏览器支持某些不支持的功能。有一些 polyfill 可以帮助浏览器在不支持 WebP 格式的情况下,使用 WebP 格式的图片。这些 polyfill 可以通过第三方库或者 CDN 来获取。

3.使用picture标签

html 复制代码
<!-- WebP 格式图片 -->
<picture>
    <source srcset="example.webp" type="image/webp">
    <img src="example.jpg" alt="WebP Image">
</picture>

<picture> 标签用于提供WebP格式的备用,只有在浏览器支持WebP时才会加载

服务器端如何检测浏览器是否支持 WebP 格式

服务器端可以通过检测浏览器的 User-Agent 字符串来判断浏览器是否支持 WebP 格式。具体方法如下:

1.检查 User-Agent 是否包含关键字"Chrome"、"Opera"、"Edge"等支持 WebP 格式的浏览器名称和版本号。

2.如果浏览器名称和版本号匹配支持 WebP 格式的浏览器,则向客户端返回 WebP 格式的图片。

3.如果浏览器名称和版本号不匹配支持 WebP 格式的浏览器,则向客户端返回其他格式的图片。

相关推荐
小小竹子8 分钟前
前端vue-实现富文本组件
前端·vue.js·富文本
万物得其道者成16 分钟前
React Zustand状态管理库的使用
开发语言·javascript·ecmascript
小白小白从不日白17 分钟前
react hooks--useReducer
前端·javascript·react.js
下雪天的夏风29 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom41 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan44 分钟前
electron react离线使用monaco-editor
javascript·react.js·electron
^^为欢几何^^1 小时前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
Hello-Mr.Wang1 小时前
vue3中开发引导页的方法
开发语言·前端·javascript
WG_171 小时前
C++多态
开发语言·c++·面试
鱼跃鹰飞1 小时前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先