MENU

为 Vuepress 增加图片放大功能的几种方法

2020 年 07 月 30 日 • 阅读: 2854 • 前端

Vuepress 文档中引入图片时,由于某些图片内容密集,必须放大才能看清。虽然可以通过拆分图片解决,但不免有些麻烦。搜索后发现,社区中有两种方案与此相关。其一是 medium-zoom 插件,它虽然支持放大,但默认占满全屏,无法手动调节倍数。另一种是引入 fancybox,它支持倍数调节,为此有人专门编写了 VSCode 插件,通过快捷键,转换当前 Markdown 文件里的图片标签。这种方式也有缺点,不仅要逐个文件手动转换,而且不符合 Markdown 语法。

由此便确立了目标,通过 Vuepress 的某种机制,自动 将文件里的 Markdown 图片标签转为 fancybox 可识别的 a 标签。

运行时

查阅文档后发现,可以将插件代码注入 Vuepress 生命周期,在浏览器端扫描图片标签并转换。

具体实现如下:首先新建 docs/.vuepress/plugins/fancybox/mixin.js 文件,写入如下代码:

export default {
    mounted() {
        this.update();
    },
    updated() {
        this.update();
    },
    methods: {
        update() {
            document.querySelectorAll("p>img").forEach(img => {
                img.parentElement.innerHTML = `<a data-fancybox href="${img.src}" content="${img.alt}">${img.outerHTML}</a>`;
            });
        }
    }
};

随后,在相同文件夹下新建 index.js 文件写入如下代码:

const { path } = require("@vuepress/shared-utils");

module.exports = (options, ctx) => ({
    clientRootMixin: path.resolve(__dirname, "mixin.js")
});

最后,在 docs/.vuepress/config.js 中引入 插件 和必要的 jscss 文件。

const STATIC_CDN = '//s0.pstatp.com/cdn/expire-1-M/'

module.exports = {
    head: [
        ['script', { src: STATIC_CDN + 'jquery/3.4.0/jquery.min.js' }],
        ['script', { src: STATIC_CDN + 'fancybox/3.5.7/jquery.fancybox.min.js' }],
        ['link', { rel: 'stylesheet', href: STATIC_CDN + 'fancybox/3.5.7/jquery.fancybox.min.css' }]
    ],
    plugins: [require('./plugins/fancybox')],
}

编译时

另一种方法是在编译期完成上述步骤,方法是编写 Markdown 插件 改变渲染规则,注入 fancybox 标签。但是由于相对路径的图片会被 Webpack 重新打包,而这发生在 Markdown 渲染之后,造成路径错误。因此必须修改 Webpackvue-loader,让其把 fancybox 标签的 src 属性一并改变。具体编码如下:

首先新建 docs/.vuepress/plugins/fancybox/index.js 文件,写入如下代码:

const PUBLIC_SUFFIX = "/docs/.vuepress/public";

module.exports = md => {
    md.renderer.rules.image = (tokens, idx) => {
        const token = tokens[idx];
        const srcIndex = token.attrIndex("src");
        const url = token.attrs[srcIndex][1].replace(PUBLIC_SUFFIX, "");
        const caption = md.utils.escapeHtml(token.content);
        return `<a data-fancybox href="${url}" content="${caption}">
                    <img src="${url}" alt="${caption}" />
                </a>`;
    };
};

module.exports.webpack = (config, isServer) => {
    const inlineLimit = 10000;
    config.module
        .rule("images")
        .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
        .use("url-loader")
        .loader("url-loader")
        .options({
            limit: inlineLimit,
            name: `assets/img/[name].[hash:8].[ext]`
        });
    config.module
        .rule("vue")
        .uses.store.get("vue-loader")
        .store.get("options").transformAssetUrls = {
            video: ["src", "poster"],
            source: "src",
            img: "src",
            image: ["xlink:href", "href"],
            a: "href"
        };
};

随后在 docs/.vuepress/config.js 中引入插件。

const STATIC_CDN = '//s0.pstatp.com/cdn/expire-1-M/'

module.exports = {
    head: [
        ['script', { src: STATIC_CDN + 'jquery/3.4.0/jquery.min.js' }],
        ['script', { src: STATIC_CDN + 'fancybox/3.5.7/jquery.fancybox.min.js' }],
        ['link', { rel: 'stylesheet', href: STATIC_CDN + 'fancybox/3.5.7/jquery.fancybox.min.css' }]
    ],
    markdown: {
        extendMarkdown: md => md.use(require('./plugin/markdown-it-remove-image-path-prefix'))
    },
    chainWebpack: require('./plugin/markdown-it-remove-image-path-prefix').webpack
}

使用 Vue 组件

上面两种方法都要手动引入 jscss。如需使用 npm 包,可编写单独的 Vue 组件,直接在 Markdown 中引入。不过那样,VSCode 将无法直接预览。

此外,对于处于 docs/.vuepress/public/ 目录中的文件,Webpack 不会重新打包。并且,它是 Vuepress 静态资源的默认根路径,比如 docs/.vuepress/public/1.jpgMarkdown 中写成 /1.jpg 才能被 Vuepress 找到。

我们编写文档时,通常都是以 docs 上级作为项目目录,这将导致 VSCode 无法本地预览。通过使用上文提到的第二种方式,我们可以在 Markdown 中编写正常路径,比如 docs/.vuepress/public/1.jpg,在编译时通过插件将前缀去掉,保证本地预览和编译的正确性。

最后,第二种方式还扩充了相对路径中 Webpack 可识别的 webp 图片格式。即,文档中可引入 md 同级目录下的 webp 图片。具体规则见方式二代码。

TG 大佬群 QQ 大佬群

返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

已有 1 条评论
  1. fds fds     Windows 10 /    Google Chrome

    @(钱币)

0:00