亲宝软件园·资讯

展开

element image组件

Adsionli 人气:0

elementui-image组件的组成

elementuiimage模块主要是有main.vue,image-viewer.vue两个文件组成的,其最外层的main.vue是image组件的主体文件,image-viewer.vue文件主要为点击图片后的大图预览。

elementui-image组件的功能

elementui-image的功能实现

fit的样式实现

fit样式主要是包含5种形式,分别是:fill, none, contain, scale-down,cover

object-fit属性在有些低版本的浏览器上是不被支持的,所以需要自己处理以上的五种显示形式,下面会进行整理书写

object-fit属性用于指定应如何调整 imgvideo 的大小以适合其容器。该属性告诉内容以多种方式填充容器;例如“保持纵横比”或“拉伸并占用尽可能多的空间”。具体的图片展示效果如下图:

根据MDN-CSS的解释,以上5种形式的对应解释如下

contain:

被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比。 整个对象在填充盒子的同时保留其长宽比,因此如果宽高比与框的宽高比不匹配,该对象将被添加==黑边==。

cover:

被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配,该对象将被剪裁以适应内容框。

fill:

被替换的内容正好填充元素的内容框。整个对象将完全填充此框。如果对象的宽高比与内容框不相匹配,那么该对象将被拉伸以适应内容框。

none:

被替换的内容将保持其原有的尺寸。

scale-down:

内容的尺寸与 nonecontain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些。

那么在image组件中,为了实现fit的动态配置,image组件中是由父组件传入fit的属性值,如果不传入的话,会选择默认的fill,来对图片进行处理。

同时由于部分低版本浏览器平台中,是不支持object-fit的,所以image组件对其进行了处理,通过一个computed数据imageStyle来控制image的样式。同时对于不支持的浏览器平台,也自定义了一份类object-fit的样式来进行兼容。

具体兼容代码如下:

//通过这段代码来判断是否支持当前浏览器,如果不支持就返回false,支持返回true
const isSupportObjectFit = () => document.documentElement.style.objectFit !== undefined;
//定义枚举类,用于对应fit的几种形式
const ObjectFit = {
    NONE: 'none',
    CONTAIN: 'contain',
    COVER: 'cover',
    FILL: 'fill',
    SCALE_DOWN: 'scale-down'
};
/**
* 兼容代码,它的实际实现也很简单,实际就是处理图片的长宽比,来控制图片的显示格式
*/
getImageStyle(fit) {
    const { imageWidth, imageHeight } = this;
    const {
        clientWidth: containerWidth,
        clientHeight: containerHeight
    } = this.$el;

    if (!imageWidth || !imageHeight || !containerWidth || !containerHeight) return {};

    const imageAspectRatio = imageWidth / imageHeight;
    const containerAspectRatio = containerWidth / containerHeight;

    if (fit === ObjectFit.SCALE_DOWN) {
        const isSmaller = imageWidth < containerWidth && imageHeight < containerHeight;
        fit = isSmaller ? ObjectFit.NONE : ObjectFit.CONTAIN;
    }

    switch (fit) {
        case ObjectFit.NONE:
            return { width: 'auto', height: 'auto' };
        case ObjectFit.CONTAIN:
            return (imageAspectRatio < containerAspectRatio) ? { width: 'auto' } : { height: 'auto' };
        case ObjectFit.COVER:
            return (imageAspectRatio < containerAspectRatio) ? { height: 'auto' } : { width: 'auto' };
        default:
            return {};
    }
}
/**
* 图片的长宽获取实际是在mounted种获取到的,通过新建一个Image类,然后监控imageLoad是否成功,如果成功了,就可以读取图片的长宽。
*/
loadImage() {
    if (this.$isServer) return;

    // reset status
    this.loading = true;
    this.error = false;

    const img = new Image();
    img.onload = e => this.handleLoad(e, img);
    img.onerror = this.handleError.bind(this);

    // bind html attrs
    // so it can behave consistently
    Object.keys(this.$attrs)
        .forEach((key) => {
        const value = this.$attrs[key];
        img.setAttribute(key, value);
    });
    img.src = this.src;
}
handleLoad(e, img) {
    this.imageWidth = img.width;
    this.imageHeight = img.height;
    this.loading = false;
    this.error = false;
}
//computed属性imageStyle
imageStyle() {
    const { fit } = this;
    if (!this.$isServer && fit) {
        return isSupportObjectFit()
            ? { 'object-fit': fit }
        : this.getImageStyle(fit);
    }
    return {};
}

通过上述的代码,就可以设置图片的fit属性了,如果说浏览器不支持,也可通过自定义widthheight来实现类fit的样式。虽然说实现起来的步骤比较多,但是这样可以为大部分浏览器平台提供很好的适配和兼容,是一种很好的处理方式。

图片懒加载的实现方式

​ 图片懒加载的实现实际上用了节流器来完成,通过设置延迟器,避免在第一时间加载图片,等待一个指定的时间后加载图片,达到图片懒加载的实现。具体的防抖和节流的实现,这里就先不赘述了,等到之后出一片博文之后,来在这里添加一条链接。

​ 同时这里的图片懒加载也是基于Scroll的事件监听进行实现,然后加入throttle之后,设置等待时间为200ms,来实现show的延迟加载。

​ 具体实现代码如下:

addLazyLoadListener() {
    //判断是否是服务端渲染,如果是服务端渲染就无法使用懒加载,因为服务端渲染是把所有标签全部渲染完成后输出的
    if (this.$isServer) return;

    const { scrollContainer } = this;
    let _scrollContainer = null;

    if (isHtmlElement(scrollContainer)) {
        _scrollContainer = scrollContainer;
    } else if (isString(scrollContainer)) {
        _scrollContainer = document.querySelector(scrollContainer);
    } else {
        _scrollContainer = getScrollContainer(this.$el);
    }

    if (_scrollContainer) {
        this._scrollContainer = _scrollContainer;
        //这里来设置节流,等待时间为200ms,这样就不会实时出发scroll滚动事件。
        this._lazyLoadHandler = throttle(200, this.handleLazyLoad);
        on(_scrollContainer, 'scroll', this._lazyLoadHandler);
        this.handleLazyLoad();
    }
}

​ 实现还是比较简单的,但是这里的很多东西感觉很值得学习,无论是节流还是滚动事件的设置。作为现在的新手的我来说是值得学习的,又可以学习使用节流的场景,也可以学习节流与事件合并使用的技巧。

图片加载失败和加载中的可配置的实现

这里的图片加载失败和加载中的可配置实现,其实都是基于slot进行实现的,也就是vue中插槽,并且这里使用了具名插槽的使用。插槽的使用还是比较简单的,这里就放一下vue官网对插槽的介绍:vue插槽

图片预览的功能模块实现(image-view.vue文件)

先放一张图,通过这个图里的内容来介绍预览功能

​ 图片预览模块的功能实现就比较好玩了,主要分为上面说的几个方面,现在就来说一下这些方面:

关于elementui管理事件的一些解读

elementui的事件管理都是通过一个柯里化的方法来进行的,就是src/utils/dom.js下的on和off方法,这两个方法写的十分巧妙,可以通过判断是不是服务端渲染的代码,返回不同的事件绑定的代码,具体实现如下:

export const on = function (isServer) {
    if (!isServer && document.addEventListener) {
        return function (element, event, handle) {
            if (element && event) {
                element.addEventListener(event, handle, false)
            }
        }
    } else {
        return function (element, event, handle) {
            if (element && event) {
                element.attachEvent(`on${event}`, handle)
            }
        }
    }
}

export const off = function (isServer) {
    if (!isServer && document.addEventListener) {
        return function (element, event, handle) {
            if (element && event) {
                element.removeEventListener(event, handle, false)
            }
        }
    } else {
        return function (element, event, handle) {
            if (element && event) {
                element.detachEvent(`on${event}`, handle)
            }
        }
    }
}

虽然这对绑定事件来说会变得十分简单,但是还是有一个关键的问题就是我无法知道我当前事件是否被消除,所以很想知道有没有很好的事件管理机制来负责处理这些被创建的事件,然后再不需要在使用这些的事件时候,去处理掉这些事件,这也是之后需要我去探索的内容。

总结

element-ui的image组件的源码阅读是我第一个仔细分析的它运行的每一步的一个组件,之前虽然也看过了两个比较复杂模块的源码 table以及 tree的实现,但是却没有这次这么仔细地去分析他的每一个步骤以及页面的布局设计。在本次阅读的过程中,学到了怎么去优化频繁交互的处理方法,以及防抖和节流的使用

加载全部内容

相关教程
猜你喜欢
用户评论