<template> <div class="el-image"> <slot v-if="loading" name="placeholder"> <div class="el-image__placeholder"></div> </slot> <slot v-else-if="error" name="error"> <div class="el-image__error">{{ t('el.image.error') }}</div> </slot> <img v-else class="el-image__inner" :src="src" :alt="alt" :style="{ 'object-fit': fit }"> </div> </template> <script> import Locale from 'element-ui/src/mixins/locale'; import { on, off, getScrollContainer, isInContainer } from 'element-ui/src/utils/dom'; import { isString, isHtmlElement } from 'element-ui/src/utils/types'; import throttle from 'throttle-debounce/throttle'; export default { name: 'ElImage', mixins: [Locale], props: { // src 圖片源,同原生 string src: String, // fit 確定圖片如何適應容器框,同原生 object-fit string fill / contain / cover / none / scale-down fit: String, // lazy 是否開啟懶加載 boolean — false lazy: Boolean, // scroll-container 開啟懶加載后,監聽 scroll 事件的容器 string / HTMLElement — 最近一個 overflow 值為 auto 或 scroll 的父元素 scrollContainer: [String, HTMLElement], // alt 原生 alt string alt: String }, data() { return { loading: true, error: false, show: !this.lazy }; }, watch: { // 監聽src變化,加載新圖片 src: { handler(val) { this.show && this.loadImage(val); }, immediate: true }, // show變化,加載新圖片 show(val) { val && this.loadImage(this.src); } }, mounted() { // 初始化如果有lazy,執行滾動監聽 this.lazy && this.addLazyLoadListener(); }, beforeDestroy() { this.lazy && this.removeLazyLoadListener(); }, methods: { // 加載圖片 loadImage(val) { // reset status this.loading = true; this.error = false; const img = new Image(); img.onload = this.handleLoad.bind(this); img.onerror = this.handleError.bind(this); img.src = val; }, // load 圖片加載成功觸發 (e: Event) handleLoad(e) { this.loading = false; this.$emit('load', e); }, // error 圖片加載失敗觸發 (e: Error) handleError(e) { this.loading = false; this.error = true; this.$emit('error', e); }, // 如果滾動元素還在當前組件內,移除監聽 handleLazyLoad() { if (isInContainer(this.$el, this._scrollContainer)) { this.show = true; this.removeLazyLoadListener(); } }, // 添加滾動監聽 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檢測一次加載完畢事件移除監聽 this._lazyLoadHandler = throttle(200, this.handleLazyLoad); // 綁定到滾動元素,監聽元素滾動事件 on(_scrollContainer, 'scroll', this._lazyLoadHandler); // 首次執行 this.handleLazyLoad(); } }, // 移除滾動監聽 removeLazyLoadListener() { const { _scrollContainer, _lazyLoadHandler } = this; if (this.$isServer || !_scrollContainer || !_lazyLoadHandler) return; off(_scrollContainer, 'scroll', _lazyLoadHandler); this._scrollContainer = null; this._lazyLoadHandler = null; } } }; </script>