<template>
|
<uni-image v-on="$listeners">
|
<div
|
ref="content"
|
:style="style"
|
/>
|
<v-uni-resize-sensor
|
v-if="mode === 'widthFix' || mode === 'heightFix'"
|
ref="sensor"
|
@resize="_fixSize()"
|
/>
|
</uni-image>
|
</template>
|
<script>
|
function fixNumber (number) {
|
// fix: 解决 Chrome 浏览器上某些情况下导致 1px 缝隙的问题
|
if (typeof navigator && navigator.vendor === 'Google Inc.' && number > 10) {
|
number = Math.round(number / 2) * 2
|
}
|
return number
|
}
|
|
export default {
|
name: 'Image',
|
props: {
|
src: {
|
type: String,
|
default: ''
|
},
|
mode: {
|
type: String,
|
default: 'scaleToFill'
|
},
|
// TODO 懒加载
|
lazyLoad: {
|
type: [Boolean, String],
|
default: false
|
},
|
draggable: {
|
type: Boolean,
|
default: false
|
}
|
},
|
data () {
|
return {
|
originalWidth: 0,
|
originalHeight: 0,
|
originalStyle: { width: '', height: '' },
|
contentPath: ''
|
}
|
},
|
computed: {
|
ratio () {
|
return this.originalWidth && this.originalHeight ? this.originalWidth / this.originalHeight : 0
|
},
|
style () {
|
let size = 'auto'
|
let position = ''
|
const repeat = 'no-repeat'
|
|
switch (this.mode) {
|
case 'aspectFit':
|
size = 'contain'
|
position = 'center center'
|
break
|
case 'aspectFill':
|
size = 'cover'
|
position = 'center center'
|
break
|
case 'widthFix':
|
case 'heightFix':
|
size = '100% 100%'
|
break
|
case 'top':
|
position = 'center top'
|
break
|
case 'bottom':
|
position = 'center bottom'
|
break
|
case 'center':
|
position = 'center center'
|
break
|
case 'left':
|
position = 'left center'
|
break
|
case 'right':
|
position = 'right center'
|
break
|
case 'top left':
|
position = 'left top'
|
break
|
case 'top right':
|
position = 'right top'
|
break
|
case 'bottom left':
|
position = 'left bottom'
|
break
|
case 'bottom right':
|
position = 'right bottom'
|
break
|
default:
|
size = '100% 100%'
|
position = '0% 0%'
|
break
|
}
|
|
return {
|
'background-image': this.contentPath ? `url("${this.contentPath}")` : 'none',
|
'background-position': position,
|
'background-size': size,
|
'background-repeat': repeat
|
}
|
}
|
},
|
watch: {
|
src (newValue, oldValue) {
|
this._loadImage()
|
},
|
mode (newValue, oldValue) {
|
if (oldValue === 'widthFix' || oldValue === 'heightFix') {
|
this._resetSize()
|
}
|
if (newValue === 'widthFix' || newValue === 'heightFix') {
|
this._fixSize()
|
}
|
},
|
contentPath (val) {
|
if (!val && this.__img) {
|
this.__img.remove()
|
delete this.__img
|
}
|
}
|
},
|
mounted () {
|
this.originalStyle.width = this.$el.style.width || ''
|
this.originalStyle.height = this.$el.style.height || ''
|
this._loadImage()
|
},
|
beforeDestroy () {
|
this._clearImage()
|
},
|
methods: {
|
_fixSize () {
|
if (this.ratio) {
|
const $el = this.$el
|
if (this.mode === 'widthFix') {
|
const width = $el.offsetWidth
|
if (width) {
|
$el.style.height = fixNumber(width / this.ratio) + 'px'
|
}
|
} else if (this.mode === 'heightFix') {
|
const height = $el.offsetHeight
|
if (height) {
|
$el.style.width = fixNumber(height * this.ratio) + 'px'
|
}
|
}
|
}
|
window.dispatchEvent(new CustomEvent('updateview'))
|
},
|
_resetSize () {
|
this.$el.style.width = this.originalStyle.width
|
this.$el.style.height = this.originalStyle.height
|
},
|
_resetData () {
|
this.originalWidth = 0
|
this.originalHeight = 0
|
this.contentPath = ''
|
},
|
_loadImage () {
|
const realImagePath = this.$getRealPath(this.src)
|
if (realImagePath) {
|
const img = this._img = this._img || new Image()
|
img.onload = $event => {
|
this._img = null
|
const width = this.originalWidth = img.width
|
const height = this.originalHeight = img.height
|
|
this._fixSize()
|
|
this.contentPath = realImagePath
|
img.draggable = this.draggable
|
if (this.__img) {
|
this.__img.remove()
|
}
|
this.__img = img
|
this.$el.appendChild(img)
|
|
this.$trigger('load', $event, {
|
width,
|
height
|
})
|
}
|
img.onerror = $event => {
|
this._img = null
|
this._resetData()
|
// 与微信小程序保持一致,保留之前样式
|
// this._resetSize()
|
this.$trigger('error', $event, {
|
errMsg: `GET ${this.src} 404 (Not Found)`
|
})
|
}
|
img.src = realImagePath
|
} else {
|
this._clearImage()
|
this._resetData()
|
// 与微信小程序保持一致,保留之前样式
|
// this._resetSize()
|
}
|
},
|
_clearImage () {
|
const img = this._img
|
if (img) {
|
img.onload = null
|
img.onerror = null
|
this._img = null
|
}
|
}
|
}
|
}
|
</script>
|
<style>
|
uni-image {
|
width: 320px;
|
height: 240px;
|
display: inline-block;
|
overflow: hidden;
|
position: relative;
|
}
|
|
uni-image[hidden] {
|
display: none;
|
}
|
|
uni-image>div {
|
width: 100%;
|
height: 100%;
|
}
|
|
uni-image>img {
|
-webkit-touch-callout: none;
|
-webkit-user-select: none;
|
-moz-user-select: none;
|
display: block;
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
opacity: 0;
|
}
|
|
uni-image>.uni-image-will-change {
|
will-change: transform;
|
}
|
</style>
|