寺明/

如何实现文章图片 自动排版 + 懒加载?

如何实现文章图片 自动排版 + 懒加载?

要实现自动排版,原理其实不难,参考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保存。我们只需要把以上代码中的whsrc整合即可。

把以上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索取。

点击这里跳转到本站简介.

留下一条评论

暂无评论