135 lines
3.1 KiB
Vue
135 lines
3.1 KiB
Vue
|
|
<template>
|
||
|
|
<div
|
||
|
|
ref="ScaleBox"
|
||
|
|
v-dragbox
|
||
|
|
class="ScaleBox"
|
||
|
|
:style="{
|
||
|
|
width: boxWidth + 'px',
|
||
|
|
height: boxHeight + 'px'
|
||
|
|
}"
|
||
|
|
>
|
||
|
|
<slot v-if="inited" />
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { createDebounce } from '@/utils'
|
||
|
|
|
||
|
|
export default {
|
||
|
|
name: 'ScaleBox',
|
||
|
|
props: {
|
||
|
|
/**
|
||
|
|
* 使用父元素的宽高计算缩放比例
|
||
|
|
*/
|
||
|
|
useParentSize: {
|
||
|
|
type: Boolean,
|
||
|
|
default: () => {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
width: {
|
||
|
|
type: Number,
|
||
|
|
default: () => {
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
},
|
||
|
|
height: {
|
||
|
|
type: Number,
|
||
|
|
default: () => {
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
},
|
||
|
|
// width/height
|
||
|
|
screenRatio: {
|
||
|
|
type: Array,
|
||
|
|
default: () => {
|
||
|
|
return []
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
emits: ['inited'],
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
boxHeight: 0,
|
||
|
|
boxWidth: 0,
|
||
|
|
inited: false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
mounted() {
|
||
|
|
this.setScale()
|
||
|
|
window.addEventListener('resize', createDebounce(this.setScale, 300))
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
getRatio() {
|
||
|
|
const ratioValue = this.useParentSize ? this.$el.parentElement.clientWidth / this.$el.parentElement.clientHeight : window.innerWidth / window.innerHeight
|
||
|
|
let result = 0
|
||
|
|
let minDiff = Infinity
|
||
|
|
for (const ratio of this.screenRatio) {
|
||
|
|
if (Math.abs(ratio - ratioValue) < minDiff) {
|
||
|
|
result = ratio
|
||
|
|
minDiff = Math.abs(ratio - ratioValue)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
},
|
||
|
|
setScale() {
|
||
|
|
// 获取到缩放比例,设置它
|
||
|
|
|
||
|
|
let { width, height } = this
|
||
|
|
const ratio = this.getRatio()
|
||
|
|
if (ratio && width && !height) {
|
||
|
|
height = width / ratio
|
||
|
|
}
|
||
|
|
if (ratio && !width && height) {
|
||
|
|
width = ratio * height
|
||
|
|
}
|
||
|
|
this.boxHeight = height
|
||
|
|
this.boxWidth = width
|
||
|
|
const wh = (this.useParentSize ? this.$el.parentElement.clientHeight : window.innerHeight) / height
|
||
|
|
const ww = (this.useParentSize ? this.$el.parentElement.clientWidth : window.innerWidth) / width
|
||
|
|
|
||
|
|
const scale = ww < wh ? ww : wh
|
||
|
|
if (this.$refs.ScaleBox) {
|
||
|
|
this.$refs.ScaleBox.style.setProperty('--scale', scale)
|
||
|
|
}
|
||
|
|
this.$nextTick(() => {
|
||
|
|
let clientHeight = this.$el.clientHeight
|
||
|
|
let clientWidth = this.$el.clientWidth
|
||
|
|
const timer = setInterval(() => {
|
||
|
|
if (this.$el.clientHeight && this.$el.clientWidth) {
|
||
|
|
if (this.$el.clientHeight === clientHeight && this.$el.clientWidth === clientWidth) {
|
||
|
|
if (!this.inited) {
|
||
|
|
this.$nextTick(() => {
|
||
|
|
this.$emit('inited')
|
||
|
|
})
|
||
|
|
}
|
||
|
|
this.inited = true
|
||
|
|
clearInterval(timer)
|
||
|
|
} else {
|
||
|
|
clientHeight = this.$el.clientHeight
|
||
|
|
clientWidth = this.$el.clientWidth
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, 1000)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
#ScaleBox {
|
||
|
|
--scale: 1;
|
||
|
|
}
|
||
|
|
.ScaleBox {
|
||
|
|
position: absolute;
|
||
|
|
transform: scale(var(--scale)) translate(-50%, -50%);
|
||
|
|
transform-origin: 0 0;
|
||
|
|
left: 50%;
|
||
|
|
top: 50%;
|
||
|
|
transition: 0.3s;
|
||
|
|
z-index: 999;
|
||
|
|
// background: rgba(255, 0, 0, 0.3);
|
||
|
|
}
|
||
|
|
</style>
|