Simple Vue Image Components

by Anurak Sumranphan

HTML

<div id="app">
  <v-image
    src="https://cdn.vuetifyjs.com/images/cards/desert.jpg"
    :aspect-ratio="1.77"
  >
  </v-image>
</div>

CSS

body {
  background: #1c2128;
  padding: 20px;
  font-family: Helvetica, Arial, sans-serif;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  width: 300px;
  transition: all 0.2s;
}

.v-image {
  background-color: #000000;
  background-position: center center;
  background-size: cover;
  background-repeat: no-repeat;
}

Vue

function convertToUnit (size, unit = 'px') {
	return `${size}${unit}`
}

Vue.component('v-image', {
  props: {
    src: String,
    lazySrc: String,
    height: [Number, String],
    width: [Number, String],
    minWidth: [Number, String],
    minHeight: [Number, String],
    maxWidth: [Number, String],
    maxHeight: [Number, String],
    aspectRatio: Number
  },

  data: function () {
    return {
      tag: 'div',
      calculatedRatio: null
    }
  },
  
  computed: {
    styles: function () {
      let styles = {}

			if (this.src) styles.backgroundImage = 'url(' + this.src + ')'
      if (this.height) styles.height = convertToUnit(this.height)
      if (this.width) styles.width = convertToUnit(this.width)
      if (this.minWidth) styles.minWidth = convertToUnit(this.minWidth)
      if (this.minHeight) styles.minHeight = convertToUnit(this.minHeight)
      if (this.maxWidth) styles.maxWidth = convertToUnit(this.maxWidth)
      if (this.maxHeight) styles.maxHeight = convertToUnit(this.maxHeight)

      if (this.calculatedRatio) {
      	styles.paddingBottom = this.calculatedRatio
      }
      
      return styles
    },
    calculateRatio: function () {
    	const aspectWidth = this.$parent.$el.offsetWidth
    	const aspectHeight = aspectWidth / this.aspectRatio
      const percent = (aspectHeight / aspectWidth) * 100

      return `${percent}%`
    }
  },
  
  mounted () {
    this.$nextTick(function () {
    	this.calculatedRatio = this.calculateRatio
    });
  },

  render: function (h) {
    const data = {
      class: 'v-image',
      style: this.styles
    }

    return h(this.tag, data, this.$slots.default)
  }
})

new Vue({
  el: "#app"
})