Explorar el Código

Image: fix ssr and object-fit compatibility. (#15346)

* fix: Image is not defined during ssr

Delay loadImage to mounted hook and add $isServer check

* fix(image): simulate object-fit behavior to compatible with IE11 and other browsers which not suppor

fix #15278

* fix: image inline-flex with overflow will still extend its parent element
Simona hace 6 años
padre
commit
340da2ff61
Se han modificado 2 ficheros con 86 adiciones y 13 borrados
  1. 76 13
      packages/image/src/main.vue
  2. 10 0
      packages/theme-chalk/src/image.scss

+ 76 - 13
packages/image/src/main.vue

@@ -11,7 +11,8 @@
       class="el-image__inner"
       :src="src"
       :alt="alt"
-      :style="{ 'object-fit': fit }">
+      :style="imageStyle"
+      :class="{ 'el-image__inner--center': alignCenter }">
   </div>
 </template>
 
@@ -21,6 +22,16 @@
   import { isString, isHtmlElement } from 'element-ui/src/utils/types';
   import throttle from 'throttle-debounce/throttle';
 
+  const isSupportObjectFit = () => document.documentElement.style.objectFit !== undefined;
+
+  const ObjectFit = {
+    NONE: 'none',
+    CONTAIN: 'contain',
+    COVER: 'cover',
+    FILL: 'fill',
+    SCALE_DOWN: 'scale-down'
+  };
+
   export default {
     name: 'ElImage',
 
@@ -38,24 +49,42 @@
       return {
         loading: true,
         error: false,
-        show: !this.lazy
+        show: !this.lazy,
+        imageWidth: 0,
+        imageHeight: 0
       };
     },
 
+    computed: {
+      imageStyle() {
+        const { fit } = this;
+        if (!this.$isServer && fit) {
+          return isSupportObjectFit()
+            ? { 'object-fit': fit }
+            : this.getImageStyle(fit);
+        }
+        return {};
+      },
+      alignCenter() {
+        return !this.$isServer && !isSupportObjectFit() && this.fit !== ObjectFit.FILL;
+      }
+    },
+
     watch: {
-      src: {
-        handler(val) {
-          this.show && this.loadImage(val);
-        },
-        immediate: true
+      src(val) {
+        this.show && this.loadImage();
       },
       show(val) {
-        val && this.loadImage(this.src);
+        val && this.loadImage();
       }
     },
 
     mounted() {
-      this.lazy && this.addLazyLoadListener();
+      if (this.lazy) {
+        this.addLazyLoadListener();
+      } else {
+        this.loadImage();
+      }
     },
 
     beforeDestroy() {
@@ -63,17 +92,21 @@
     },
 
     methods: {
-      loadImage(val) {
+      loadImage() {
+        if (this.$isServer) return;
+
         // reset status
         this.loading = true;
         this.error = false;
 
         const img = new Image();
-        img.onload = this.handleLoad.bind(this);
+        img.onload = e => this.handleLoad(e, img);
         img.onerror = this.handleError.bind(this);
-        img.src = val;
+        img.src = this.src;
       },
-      handleLoad(e) {
+      handleLoad(e, img) {
+        this.imageWidth = img.width;
+        this.imageHeight = img.height;
         this.loading = false;
         this.$emit('load', e);
       },
@@ -117,6 +150,36 @@
         off(_scrollContainer, 'scroll', _lazyLoadHandler);
         this._scrollContainer = null;
         this._lazyLoadHandler = null;
+      },
+      /**
+       * simulate object-fit behavior to compatible with IE11 and other browsers which not support object-fit
+       */
+      getImageStyle(fit) {
+        const { imageWidth, imageHeight } = this;
+        const {
+          clientWidth: containerWidth,
+          clientHeight: containerHeight
+        } = this.$el;
+
+        if (!imageWidth || !imageHeight || !containerWidth || !containerHeight) return {};
+
+        const vertical = imageWidth / imageHeight < 1;
+
+        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 vertical ? { width: 'auto' } : { height: 'auto' };
+          case ObjectFit.COVER:
+            return vertical ? { height: 'auto' } : { width: 'auto' };
+          default:
+            return {};
+        }
       }
     }
   };

+ 10 - 0
packages/theme-chalk/src/image.scss

@@ -7,11 +7,21 @@
 }
 
 @include b(image) {
+  position: relative;
   display: inline-block;
+  overflow: hidden;
 
   @include e(inner) {
     @extend %size;
     vertical-align: top;
+
+    @include m(center) {
+      position: relative;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      display: block;
+    }
   }
 
   @include e(placeholder) {