Less的图片管理

开头

从18年8月开始,我开始了我的野生前端职业生涯。不管是自学期间还是工作之后,网上的文章对我的学习帮助良多。所以我想开始分享一些我没在网上看到过的知识。

图片载体

通常我们展示图片是用html的标签或者css的background-image属性。

由于安卓手机会对点击的进行放大显示,所以本人喜欢用background-image多一点。

安卓点击默认放大的解决方法:css可以用pointer-events:none;js可用e.preventDefault()

当然这两种载体还有其他区别,可以自行去搜索,下面就只用background-image来作为图片的载体。

自动获取图片宽高

不知道有没有人和我一样,工作中写的页面大多数是通过图片堆砌起来的,不论是通过一些开发辅助工具(pxcook等),都要查看每张图片的宽高,所以自从我看到了Less中有获取图片宽高的函数后,我就和Less擦出了火花。

    .logo {
        width: image-width("logo.png");
        height: image-height("logo.png");
    }
    
    编译成
    
    .logo {
        width: 10px;
        height: 20px;
    }
复制代码

先说下这个函数的缺点,用多了之后会使项目的编译速度变慢(包括热更新),如果你的电脑本身就慢的话不推荐用。

1、首先要配置rem和把函数封装成mixin

移动端中的适配直接使用px单位是不行的,所以我们要进一步的改写

    @designWidth: 750;
    html {
        //因为移动端已经兼容了vw和calc,所以不再需要用js来配置rem
        font-size: calc(10000vw / @designWidth)
    }
    .image-size(@url) {
        width: unit(image-width(@url) / 100, rem);
        height: unit(image-height(@url) / 100, rem);
    }
复制代码

2、配置background-image的mixin

首先我们的图片都要放在路径是src/assets/img/的文件下,而不是和组件放在一起

    .contain {
        background-size: contain;
        background-repeat: no-repeat;
        background-position: center;
    }
    .image-size(@url) {
        width: unit(image-width(@url) / 100, rem);
        height: unit(image-height(@url)/ 100, rem);
    }
    .bg-contain (@url) {
        // 路径是从项目的根目录开始的
        .image-size('./src/assets/img/@{url}');
        // 这里使用webpack来解析图片模块
        background-image: url('~@/assets/img/@{url}');
        // 使用继承减少生成的css代码量
        &:extend(.contain); 
    }
    
    使用如下
    
    .logo {
        .bg-contain('logo.png');
    }
    
    .homeLogo {
        .bg-contain('home/logo.png');
    }
    
复制代码

3、进一步减少bg-contain()的代码量

在进一步改写之前我们先来了解下Less的List Functions。

    @paths: 'home', 'index';
    value: extract(@paths, 1);
    
    编译成
    
    value: 'home';
复制代码

很明显这就是以1为开始下标的数组,并且可以通过extract()函数取值。


有三处可以减少我们的代码量

  • 图片处于的文件夹目录
  • 图片的格式
  • 参数差异化兼容
    @paths: '/img';
    .bg-contain (@name, @arg1: 'png', @arg2: 1) {
        // 暂时没想到更漂亮的方法来兼容参数的差异化
        // 有想到的话请务必告诉我
        @suffix1: if(isstring(@arg1), @arg1, '');
        @suffix2: if(isstring(@arg2), @arg2, '');
        @pathIndex1: if(isnumber(@arg1), @arg1, 0);
        @pathIndex2: if(isnumber(@arg2), @arg2, 0);
        @suffix: @suffix1 + @suffix2;
        @pathIndex: @pathIndex1 + @pathInde2;
        @path: extract(@paths, @pathIndex);
        .image-size('./src/assets/@{path}/@{name}.@{suffix}');
        background-image: url('~@/assets/@{path}/@{name}.@{suffix}');
        &:extend(.contain); 
    }
    
    使用如下
    
    // home.vue
    @paths: '/img/home';
    .logo {
        .bg-contain('logo');
    }
    .gif {
        .bg-contain('logo', 'gif');
    }
    .test {
        .bg-contain('logo', 'gif', 1);
    }
    
    // index.vue
    @paths: '/img/index', '/img/common';
    .logo {
        .bg-contain('logo');
    }
    .commonLogo {
        .bg-contain('logo', 2);
    }
复制代码

4、用遍历来减少代码量

我用的循环可不是loop when这种那么恶心的东西,而是用Less v3.7.0 版本的新特性each()来进行遍历。

首先让我们来看一下怎么用each()来减少代码量

    @imgs: logo, avatar;
    each(@imgs, {
        .bg-@{value} {
            .bg-contain(@value);
        }
    )
复制代码

不知道你们有没有和我一样看到这段后感觉很,不过这里要注意一下。

当我们要用遍历出来的@value来作为选择器时,我们的数组里放的元素是不能带引号的,不过我们可以通用Less的e()函数来去引号。

因为在Less中带引号的是Value类型,而不带引号的是Node类型,只有Node类型可以用来作为选择器使用。感兴趣的话可以看下Less的源码。

自动引入图片

上面的代码还是要在css里写图片名字然后在html里写上相应的class,感觉写的有点不舒服。不知道你们有没有想到怎么连css都不写就能用图片呢。

我先说下我的思路:

  • 获取图片路径和规范化图片的class命名。
  • 把图片路径和类名分别定义成全局变量引入。
  • 在less中遍历数组,配置类名和引入图片资源。

是不是很简单?

当然这种方式是有缺点的,就是不能动态地更新自动引入的图片,所以如果用这种方法来自动引入图片的话,你需要在yarn serve前就把要用到的图片给放在图片目录下。


下面我就用 Vue + Node + Less 来做自动引入图片。

这里我用 vue-cli 3.0 来生成项目,然后在项目根目录下配置 vue.config.js 文件。

1、定义遍历函数

2、生成Less的全局变量对象

3、输出配置

4、遍历全局数组生成类

5、关于class的作用域限制

你当然可以使用上面的前置字符串来限制class的作用域,使用如下:

    <div class="bg-logo"/>
复制代码

我这里还有一种我喜欢的方式来限制class的作用域,不过肯定不是人人都喜欢的方式,首先看看怎么使用:

    // html版
    <bg logo/>
    
    // pug版
    bg(logo)
复制代码

极简主义,有没有!!!


首先我们知道这个标签是不存在的,对于这种标签浏览器是会把它看成inline-block,所以我们要把display属性定义为block。(这里我没有看HTML的规范,所以如果说的不准确的话请大家指出)。

    bg {
        display: block;
    }
复制代码

然后如果直接在 vue 里的