如何实现文章图片 自动排版 + 懒加载?
要实现自动排版,原理其实不难,参考Typecho插件Autophotos的一段js。
(function () {
var base = 50;
$.each($('.photos'), function (i, photoSet) {
$.each($(photoSet).children(), function (j, item) {
var img = new Image();
img.src = $(item).find('img').attr('data-original');
img.onload = function () {
var w = parseFloat(img.width);
var h = parseFloat(img.height);
$(item).css('width', w * base / h + 'px');
$(item).css('flex-grow', w * base / h);
$(item).find('div').css('padding-top', h / w * 100 + '%');
};
});
});
})();
这段代码经过我人为修改,因为我同时使用了jQuery的lazyload插件,所以获取img的data-original属性作为img对象的src属性。然而,带来的一个问题是懒加载失效。
在页面开始加载时,以上代码遍历文章中所有img标签取每个图像的长宽数值,这个过程一定会涉及到图像的请求。
无论是否启用懒加载,这段代码中img对象都会主动下载url(data-original)。
我想到的第一个对策是每当Lazyload成功载入一张图片,触发一个事件,运行该代码。
但是lazyload似乎没有提供事件回调机制,况且即使能实现回调,在图片还未成功加载、长宽未知时,浏览器默认隐藏该img标签,用户根本不知道此处是否还有图片未成功加载,造成信息的遗漏。
这个问题可以通过设置占位图像解决,但是每成功加载一张图片,都要重新遍历所有img,效率低且占用高。
第二个对策是我目前认为不错的解决方法,即在文章真正发布之前先访问所有imgurls,预先获取图片的长宽属性,生成一段json保存。我们只需要把以上代码中的w
、h
和src
整合即可。
把以上js代码从php代码中删除,然后稍微处理一下,得到如下代码。
var ImgArray = [];
(function () {
var base = 50;
var total = 0;
var donesum = 0;
$.each($('.photos'), function (i, photoSet) {
$.each($(photoSet).children(), function (j, item) {
var img = new Image();
img.src = $(item).find('img').attr('data-original');
img.onload = function () {
var w = parseFloat(img.width);
var h = parseFloat(img.height);
var ImgMember = {
url: img.src,
width: w,
height: h
};
ImgArray.push(ImgMember);
donesum = donesum + 1;
console.log("current:" + (donesum / total).toFixed(2));
if (donesum === total) {
myFunction();
}
};
total = total + 1;
});
});
})();
function myFunction() {
console.log("ok! run 'copy(ImgArray); '");
}
把这段代码复制到console中直接运行,等待代码从0跑到1后运行copy(ImgArray)
就可以在剪贴板中得到整篇文章的图片宽高数据。
然后有两个选择,第一种是直接把这一段json嵌入到文章的html代码中,第二种是通过ajax动态请求接口返回。我不喜欢在html中嵌入大段js,所以我选择第二种办法。
先把返回的json写到文章的ImageMeta字段,然后修改插件的代码,增加一个接口如下用来返回ImageMeta。
public function action() {
if (!isset($_GET['cid']) || !ctype_digit($_GET['cid'])) {
header('Content-Type: text/plain');
echo 'console.log("Invalid Request");';
exit;
}
$db = Typecho_Db::get();
$sql = $db->select()->from('table.fields')
->where('cid = ?', $_GET['cid'])
->where('name = ?', 'ImageMeta')
->limit(1);
$ret = $db->fetchAll($sql);
if (count($ret) != 1) {
header('Content-Type: text/plain');
echo 'console.log("No Matched Record");';
exit;
}
$jsonArray = $ret[0]['str_value'];
echo 'ImgArray=' . $jsonArray. ';';
}
最后我需要修改前端代码进行适配,由于后端接口直接返回的就是一段标准javascript,所以直接通过eval运行返回的数据即可。最后的前端js代码应该如下。
var ImgArray = [];
$.ajax({
url: '/Maxomo?cid=' + document.querySelector('meta[name="cid"]').getAttribute('content'),
method: 'GET',
success: function (response) {
eval(response);
nextStep();
},
error: function (xhr, status, error) {
console.log('AJAX request failed:', error);
}
});
function nextStep(){
if (ImgArray.length !== 0) {
loadImgStyle();
}
}
function loadImgStyle() {
var base = 50;
$.each($('.photos'), function (i, photoSet) {
$.each($(photoSet).children(), function (j, item) {
imgsrc = $(item).find('img').attr('data-original');
var w,
h;
obj = false;
for (var i = 0; i < ImgArray.length; i++) {
if (ImgArray[i].url === imgsrc) {
w = ImgArray[i].width;
h = ImgArray[i].height;
obj = true;
break;
}
}
if (obj) {
$(item).css('width', w * base / h + 'px');
$(item).css('flex-grow', w * base / h);
$(item).find('div').css('padding-top', h / w * 100 + '%');
} else {
console.log("cannot find=" + imgsrc);
}
});
});
};
搞定!
由于我的Maxomo插件使用办法较为复杂,还需要修改主题模版代码,就不在此分享了。可以联系我的QQ索取。
点击这里跳转到本站简介.
暂无评论