Typecho 主题开发 - 图片懒加载
图片懒加载,也可以称为延迟加载。简单来说就是默认只加载可视区内的图片,可视区外的图片默认不加载,只有图片进入可视区才加载。这里说的可视区就是你能看到的区域。对于带宽较低的服务器来说,同时加载多张图片可能会导致图片长时间无法加载出,图片懒加载就是只加载用户能看到的区域的图片,对于流量计费的服务器和用户来说,图片懒加载也可以节省流量。
浏览器检测到 img
的 src
是一个 HTTP 链接就会发送请求加载图片,先要做的就是去除 img
的 src
,还可以给 img
增加一个 data-src
的自定义属性用来存放图片地址,data-src
属性里的图片地址浏览器不会请求。
最近我给我的两个 Typecho 主题 MWordStar 和 Facile 都加入了图片懒加载,这里就来简单写一下 Typecho 主题的图片懒加载。
编写 PHP 函数
Typecho 生成的 HTML 文章内容的 img
是包含 src
的,下面编写一个函数把 src
替换为 data-src
,并且 data-src
里要包含图片地址:
function replaceImgSrc($content) {
$pattern = '/<img(.*?)src(.*?)=(.*?)"(.*?)">/i';
$replacement = '<img$1data-src$3="$4"$5 class="load-img">';
return preg_replace($pattern, $replacement, $content);
}
上面的 replaceImgSrc
函数可以把所有的 img
的 src
替换为 data-src
,还会给 img
加入一个 load-img
的 class
。
上面的 replaceImgSrc
函数可以直接放到 Typecho 主题的 functions.php
调用,传入需要替换的 HTML 文章内容,返回替换后的 HTML 内容。
Typecho 主题输出文章内容可以调用 $this->content()
方法,这个方法会直接输出文章内容。也可以直接读取 $this->content
属性,这样就不会输出文章内容。
下面使用上面编写的 replaceImgSrc
函数来替换和输出文章内容:
<?php echo replaceImgSrc($this->content); ?>
$this->content
可以在 post.php
文章页和 page.php
独立页面使用。
CSS 样式
一般文章内的图片宽高都不固定,CSS 基本都是使用 max-width: 100%
来设置文章内的图片尺寸 ,在图片加载完成之前,宽度就是 alt
描述文字的宽度,高度也是 alt
描述文字的高度。
下面给待加载的图片设置默认样式:
.load-img {
width: 100%;
height: auto;
aspect-ratio: 16/9;
background-color: #CCCCCC;
display: block;
}
上面的宽度是 100%,aspect-ratio: 16/9
是设置宽高比为 16 比 9,其中 aspect-ratio
属性不支持 IE 系列浏览器,如果需要支持 IE 可以通过媒体查询来设置高度。
我在替换图片 src
的时候给图片加了一个 load-img
的 class
,这个 class
就是用来设置样式和检测加载图片的,加载完成后删除 load-img
的 class
就可以直接去除待加载的图片默认样式。
JavaScript 加载图片
JS 需要做的就是监听滚动条,当图片进入可视区时给图片设置 src
。
jQuery:
// 监听 document 的滚动条
$(document).on('scroll', function () {
$('.load-img').each(function () {
// 如果文章内的 img 进入可视区就加载图片
if (
$(this).offset().top < $(document).scrollTop() + window.innerHeight &&
$(this).offset().top + $(this).height() > $(document).scrollTop()
) {
// 如果 img 不包含 src 就加载图片
if ($(this).attr('src') === undefined) {
$(this).attr('src', $(this).attr('data-src'));
}
}
});
});
// 如果页面加载完成时有图片在可视区就直接加载图片
$('.load-img').each(function () {
if ($(this).offset().top < window.innerHeight) {
$(this).attr('src', $(this).attr('data-src'));
}
});
// 文章图片加载完成后删除 load-img 的 class
$('.load-img').on('load', function () {
$(this).removeClass('load-img');
});
下面是详细说明:
- 给
document
添加scroll
滚动条事件来监听document
的滚动条 - 判断所有包含
load-img
的class
的img
是否进入可视区 - 如果
img
进入可视区就继续判断img
是否包含src
,只有img
不包含src
才继续加载 - 把
img
的data-src
传给src
上面是滚动加载图片的部分。
除了滚动加载外,页面加载完成也需要判断一下可视区是否包含图片,如果有图片在可视区就直接加载。
图片加载完成后还需要去除默认样式,也就是删除 load-img
的 class
,load
事件会在图片加载完成后触发。
原生 JavaScript:
const imgList = document.querySelectorAll('.load-img'); // 通过 .load-img 获取图片
// 监听 document 的滚动条
document.addEventListener('scroll', () => {
for (let i = 0;i < imgList.length;i ++) {
// 判断图片是否进入可视区
if (
imgList[i].offsetTop < document.documentElement.scrollTop + window.innerHeight &&
imgList[i].offsetTop + imgList[i].offsetHeight > document.documentElement.scrollTop
) {
// 如果文章内的 img 进入可视区就继续判断 img 是否包含 src
if (imgList[i].getAttribute('src') === null) {
// 如果 img 不包含 src 就加载图片
imgList[i].setAttribute('src', imgList[i].getAttribute('data-src'));
}
}
}
});
// 如果页面加载完成时有图片在可视区就直接加载图片
for (let i = 0;i < imgList.length;i ++) {
if (imgList[i].offsetTop < window.innerHeight) {
imgList[i].setAttribute('src', imgList[i].getAttribute('data-src'));
}
}
// 文章图片加载完成后删除 load-img 的 class
for (let i = 0;i < imgList.length;i ++) {
imgList[i].addEventListener('load', ev => {
ev.target.classList.remove('load-img');
});
}
原生 JS 的步骤和判断方法和 jQuery 是一样的。
如果不需要兼容 IE 浏览器的话,也可以使用 forEach
来遍历元素,兼容 IE 的话就只能使用 for
循环来遍历元素,Babel 不会转换 forEach
。
classList
是一个用来操作元素 class
的 API,IE10 和 11 都可以使用,详细的 classList
使用和兼容老 IE 的方法可以看 JavaScript 操作元素的 class 。
测试
如果你测试加载的图片在本地,或者是服务器速度较快的话,图片出现的一瞬间可能就已经加载完成了。要想观察图片加载过程就需要对浏览器进行限速,下面简单写一下 Chrome 浏览器使用开发者工具限速的方法:
在开发者工具的主菜单打开 更多工具
子菜单,选择 网络状况
打开网络状况面板,如下:
把 网络节流
设置为 低速3G
,选中 停用缓存
,如下:
刷新网页后,网页和图片的加载都会变得很慢,可以用来测试图片懒加载。关闭开发者工具就能恢复正常。
版权声明:本文为原创文章,版权归 Mr. Ma's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/916/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。
现在直接给img图片加上decoding="async" loading="lazy",这俩就好了,效果还挺不错的。过来感谢下你的后端代码。给src改为data-src的代码。🤣
好的 谢谢大哥
ajax 牵引问题比较多。还是挺麻烦的。
Typecho pjax 好像后评论不会主动刷新局部,点击回复会返回到网站顶部
我的博客没有用到 pjax,Typecho pjax 相关的问题我目前也不是太了解,你可以到 Typecho pjax 相关的文章去问一下作者。
问了,他们都不在 唉,,加成功了pjax,但是图片懒加载不会重新刷新,一直是加载前的图片 不会返回正确地址
把图片懒加载的 JS 放到 pjax 成功后的回调函数里试一下
回调函数太多了,看不懂 唉,加在后面没反应 怎么办
你可以把我这里的图片懒加载相关的代码删了,然后使用别人写好的图片懒加载插件试一下,我看 http://typecho-fans.github.io/plugins/TESTORE.html 这个网站就有很多图片懒加载插件。
好
现在可以了 但是有点小bug,时灵时不灵 , 大哥 能在出个Typecho的pjax教程跟代码吗
Typecho pjax 的有很多人写过,你可以在谷歌上搜一下,例如这篇 https://www.ihewro.com/archives/354/ 或者 https://eriri.ink/archives/typecho-jquery-pjax.html
https://img1.imgtp.com/2022/10/05/wOSWFVKs.png 就是这样的 上传图床了,他不会移除事件 我试了好多次
你用的代码是 jQuery 还是原生 JS 的?检查一下 JS 代码是否有问题
一直显示加载中的图片 不返回真实图片 为啥 ,我点这个图片放大显示的是真实图片, 点开图片 能返回真实图片 就页面图片显示一直是加载中的图片
滚动一下页面看能否正常加载,还有查看一下控制台是否有报错
不正常,不会返回真实图片地址
还有个问题,我不达到那样的效果 ,单纯图片懒加载,使用load-img的class图片加载完了 不会自动去除属性 图片被限制了高宽
图片加载完成后 load-img 的 class 会被删除,你检查一下图片加载完成后是否包含 load-img 的 class。我的图片宽高之类的样式都是写在 load-img class 里的,load-img 被删除后样式也就不存在了。
function replaceImgSrc($content) {
}
我要在img标签后面添加 data-fancybox="gallery"属性 使得图片放大,我是在img标签后面添加<a href="$4" data-fancybox="gallery"/> 但是$4输出的不是该图片链接 我该怎么改 求教一下
你要给 img 插入一个 data-fancybox="gallery" 的自定义属性,是吧?
对,不能直接在img标签后面插入,要加入a标签然后插入
你是需要在 img 外面包一层 a 标签还是在 img 后面放一个 a 标签?
我要在img标签包一个a标签里面获取图片链接href 这种怎么改呢
后面放a标签,<a href="$4" data-fancybox="gallery"/> 这样的 就是href= 输出图片链接 这个应该怎么改,我写$4输出的不是图片链接
你需要在 img 后面插入一个 a链接,链接的 href 就是图片的 src 地址是吧?你需要通过 PHP 还是前端 JS 来实现?
data-fancybox="gallery" 我用这个属性加入a标签就能实现了 但是 目前根据您的这篇文章 图片懒加载正则匹配的 ,我加入了用href="$4"获取图片地址错误 怎么改能获取输出图片地址 点开图片放大 并图片懒加载
我的这个正则表达式会同时匹配 src 后面的 alt 和 title,你的这个需求需要用回调函数把地址提取出来,我调整了一下替换函数,你试一下:
而且不知道为啥,这样添加的a标签 data-fancybox="gallery"属性图片点开不会放大,之前我改也是匹配到了表情图片 但是我点开文章图片 会放大成匹配到的表情图片
这样a标签输出的是文章内的表情图片,没有输出该图片的链接
不行,这样输出的是文章表情图片了,这里不支持图片评论,我截图上传图床 图片链接:https://img1.imgtp.com/2022/10/02/Rjs4gNyy.png 这样的
我测试了一下,可以插入链接的啊,访问 https://s1.ax1x.com/2022/10/02/xKIkBF.jpg 查看,我上面的代码 链接的 href 双引号和后面的单引号之间有一个空格,你可以把空格删了试试看。
https://img1.imgtp.com/2022/10/02/qUtp9QgG.png 看 输出的不是该图片链接 是表情图片链接 而且还是文章第一个表情的图片链接,而且 a标签的data-fancybox="gallery"也不管用了 点了图片没反应
空格删了还是不行,a标签获取的是表情图片 不是这张图的图片链接https://img1.imgtp.com/2022/10/02/X72G6GPk.png
我的可以正常插入对应地址的链接,你的可能和表情插件之类的有冲突
那怎么办
选择通过 JS 插入链接,或者放弃
通过js怎么弄
用 JS 在文章内的图片后面插入一个链接,链接的 href 就是前面图片的 src
$pattern = '/\<img.?src\=\"(.?)\"1*>/i';
我之前这样https://img1.imgtp.com/2022/10/02/cstSTd3a.png可以获取图片链接 但是表情的class属性被匹配没了 表情变成图片很大了 https://img1.imgtp.com/2022/10/02/DAkrxnpW.png 这样的效果,怎么样给他改成表情匹配出来有class属性,还有 data-src 和class="load-img" 有懒加载效果呢 大哥
我知道为什么图片不能点开放大原因了,我搞错了,是需要在img标签外搞一个a标签包着img,a表情里面获取图片链接href 这种怎么改呢
img 外面包链接:
我不用表情的文章 确实能获取到图片链接,但是点图片 会跳转打开图片 不会直接打开
获取的还是表情图片链接
我测试可以正常获取 https://s1.ax1x.com/2022/10/02/xKLtZ6.jpg ,你的如果是表情插件之类的冲突的话就没办法了
不是这个问题,是直接不显示图片, 还有个问题,方便留个联系方式 请教下您吗
点击图片放大的
你可以直接回复我邮件
我在上面评论发了,这种怎么改呢 能懒加载 还能图片放大
有些显示 有些不显示
如果你是使用浏览器限速测试或是服务器带宽较低的话,后面的图片要等到前面的图片加载完成后才会加载,即便后面的图片进入可视区也要等前面的图片加载完成。
不是这个问题,是直接不显示图片, 还有个问题,方便留个联系方式 请教下您吗
不是这个问题,是直接不显示图片, 还有个问题,方便留个联系方式 请教下您吗