vue
<template>
<div
ref="autoScaleBox"
class="auto-scale-box"
:style="style">
<slot></slot>
</div>
</template>
<script setup>
import {computed, nextTick, onBeforeUnmount, onMounted, ref, toRefs, watch} from "vue";
import {debounce} from './debounce.js';
const autoScaleBox = ref(null);
const defaultProps = defineProps({
/**
* 设计稿宽度
*/
width: {
type: [Number, String],
default: 1920,
},
/**
* 设计稿高度
*/
height: {
type: [Number, String],
default: 1080,
},
/**
* 缩放类型 1: 强制铺满 2: 按比例缩放 3: 适应宽度 4: 适应高度 默认值为1
*/
scaleType: {
type: Number,
default: 1,
validator(value) {
// The value must match one of these strings
return [1, 2, 3, 4].includes(value)
}
},
/**
* 自适应缩放防抖延迟时间(ms) 默认 100
*/
delay: {
type: Number,
default: 100,
},
/**
* 不被缩放的元素id
*/
reverseScalingIds: {
type: [Array, String],
default: null
},
/**
* 获取宽高的时候是否使用父元素的宽高 默认false
*/
parent: {
type: Boolean,
default: false
}
});
const props = toRefs(defaultProps)
const emit = defineEmits(['change'])
const transform = ref('')
const top = ref('')
const style = computed(() => {
const w = isNaN(props.width.value) ? props.width.value : props.width.value + 'px'
const h = isNaN(props.height.value) ? props.height.value : props.height.value + 'px'
return {
transform: transform.value,/*'scale(var(--scale)) translate(-50%, -50%)'*/
width: w,
height: h,
top: top.value,
}
});
/**
* 缩放类型
* @returns {string[]|number[]}
*/
function getScale() {
const designWidth = props.width.value
const designHeight = props.height.value
let docWidth = window.innerWidth
let docHeight = window.innerHeight
if (props.parent) {
if (!autoScaleBox.value) {
console.warn('元素未渲染');
return [1, 1]
}
// 获取父元素的宽高
const {width, height} = autoScaleBox.value.parentElement.getBoundingClientRect()
docWidth = width
docHeight = height
}
// 强制铺满
if (props.scaleType.value === 1) {
return [`${docWidth / designWidth},${docHeight / designHeight}`, `${designWidth / docWidth},${designHeight / docHeight}`]
}
// 按比例缩放
if (props.scaleType.value === 2) {
const wh1 = docHeight / designHeight
const wh2 = designHeight / docHeight;
const ww1 = docWidth / designWidth;
const ww2 = designWidth / docWidth;
return ww1 < wh1 ? [ww1, ww2] : [wh1, wh2];
}
// 适应宽度
if (props.scaleType.value === 3) {
return [docWidth / designWidth, designWidth / docWidth]
}
// 适应高度
if (props.scaleType.value === 4) {
return [docHeight / designHeight, designHeight / docHeight]
}
return [1, 1]
}
function setScale() {
let [scale, scale1] = getScale()
transform.value = `scale(${scale})`
// 如果是按比列适配则设置top
if (props.scaleType.value === 2) {
nextTick(() => {
setTimeout(() => {
top.value = (autoScaleBox.value.parentElement.getBoundingClientRect().height - autoScaleBox.value.getBoundingClientRect().height) / 2 + 'px'
}, 400)
})
} else {
top.value = '0'
}
// 将缩放比例传给父组件
emit('change', scale, scale1)
if (!props.reverseScalingIds.value) {
return
}
let reverseScalingIdNames = []
if (typeof props.reverseScalingIds.value === 'string') {
reverseScalingIdNames.push(props.reverseScalingIds.value)
} else {
reverseScalingIdNames = props.reverseScalingIds.value
}
reverseScalingIdNames.forEach(id => {
const element = document.querySelector(`#${id}`)
if (!element) {
return
}
/*
* 将地图容器按照全局的scale再还原回去,位置偏移问题解决,但是地图显示较大
获取地图宽度,根据scale计算地图当前实际应该显示的宽高像素值,直接修改地图的宽高属性。
即修改地图真实的宽高,来适配其他部分scale之后的大小。
* */
let w = props.width.value * scale
let h = props.height.value * scale
if (String(scale).split(',').length === 2) {
w = props.width.value * scale.split(',')[0]
h = props.height.value * scale.split(',')[1]
}
element.style.setProperty('width', `${w}px`)
element.style.setProperty('height', `${h}px`)
element.style.setProperty('transform', `scale(${scale1})`)
element.style.setProperty('transform-origin', '0 0')
})
}
const fn = debounce(setScale, props.delay)
watch(() => props, () => {
fn()
}, {deep: true})
onMounted(() => {
setScale();
// 添加窗口改变事件
window.addEventListener('resize', fn)
});
onBeforeUnmount(() => {
// 移除窗口改变事件
window.removeEventListener('resize', fn)
})
</script>
<style scoped>
.auto-scale-box {
position: absolute;
transform-origin: 0 0;
left: 0;
top: 0;
/*移除动画*/
/*transition: 0.4s;*/
}
</style><template>
<div
ref="autoScaleBox"
class="auto-scale-box"
:style="style">
<slot></slot>
</div>
</template>
<script setup>
import {computed, nextTick, onBeforeUnmount, onMounted, ref, toRefs, watch} from "vue";
import {debounce} from './debounce.js';
const autoScaleBox = ref(null);
const defaultProps = defineProps({
/**
* 设计稿宽度
*/
width: {
type: [Number, String],
default: 1920,
},
/**
* 设计稿高度
*/
height: {
type: [Number, String],
default: 1080,
},
/**
* 缩放类型 1: 强制铺满 2: 按比例缩放 3: 适应宽度 4: 适应高度 默认值为1
*/
scaleType: {
type: Number,
default: 1,
validator(value) {
// The value must match one of these strings
return [1, 2, 3, 4].includes(value)
}
},
/**
* 自适应缩放防抖延迟时间(ms) 默认 100
*/
delay: {
type: Number,
default: 100,
},
/**
* 不被缩放的元素id
*/
reverseScalingIds: {
type: [Array, String],
default: null
},
/**
* 获取宽高的时候是否使用父元素的宽高 默认false
*/
parent: {
type: Boolean,
default: false
}
});
const props = toRefs(defaultProps)
const emit = defineEmits(['change'])
const transform = ref('')
const top = ref('')
const style = computed(() => {
const w = isNaN(props.width.value) ? props.width.value : props.width.value + 'px'
const h = isNaN(props.height.value) ? props.height.value : props.height.value + 'px'
return {
transform: transform.value,/*'scale(var(--scale)) translate(-50%, -50%)'*/
width: w,
height: h,
top: top.value,
}
});
/**
* 缩放类型
* @returns {string[]|number[]}
*/
function getScale() {
const designWidth = props.width.value
const designHeight = props.height.value
let docWidth = window.innerWidth
let docHeight = window.innerHeight
if (props.parent) {
if (!autoScaleBox.value) {
console.warn('元素未渲染');
return [1, 1]
}
// 获取父元素的宽高
const {width, height} = autoScaleBox.value.parentElement.getBoundingClientRect()
docWidth = width
docHeight = height
}
// 强制铺满
if (props.scaleType.value === 1) {
return [`${docWidth / designWidth},${docHeight / designHeight}`, `${designWidth / docWidth},${designHeight / docHeight}`]
}
// 按比例缩放
if (props.scaleType.value === 2) {
const wh1 = docHeight / designHeight
const wh2 = designHeight / docHeight;
const ww1 = docWidth / designWidth;
const ww2 = designWidth / docWidth;
return ww1 < wh1 ? [ww1, ww2] : [wh1, wh2];
}
// 适应宽度
if (props.scaleType.value === 3) {
return [docWidth / designWidth, designWidth / docWidth]
}
// 适应高度
if (props.scaleType.value === 4) {
return [docHeight / designHeight, designHeight / docHeight]
}
return [1, 1]
}
function setScale() {
let [scale, scale1] = getScale()
transform.value = `scale(${scale})`
// 如果是按比列适配则设置top
if (props.scaleType.value === 2) {
nextTick(() => {
setTimeout(() => {
top.value = (autoScaleBox.value.parentElement.getBoundingClientRect().height - autoScaleBox.value.getBoundingClientRect().height) / 2 + 'px'
}, 400)
})
} else {
top.value = '0'
}
// 将缩放比例传给父组件
emit('change', scale, scale1)
if (!props.reverseScalingIds.value) {
return
}
let reverseScalingIdNames = []
if (typeof props.reverseScalingIds.value === 'string') {
reverseScalingIdNames.push(props.reverseScalingIds.value)
} else {
reverseScalingIdNames = props.reverseScalingIds.value
}
reverseScalingIdNames.forEach(id => {
const element = document.querySelector(`#${id}`)
if (!element) {
return
}
/*
* 将地图容器按照全局的scale再还原回去,位置偏移问题解决,但是地图显示较大
获取地图宽度,根据scale计算地图当前实际应该显示的宽高像素值,直接修改地图的宽高属性。
即修改地图真实的宽高,来适配其他部分scale之后的大小。
* */
let w = props.width.value * scale
let h = props.height.value * scale
if (String(scale).split(',').length === 2) {
w = props.width.value * scale.split(',')[0]
h = props.height.value * scale.split(',')[1]
}
element.style.setProperty('width', `${w}px`)
element.style.setProperty('height', `${h}px`)
element.style.setProperty('transform', `scale(${scale1})`)
element.style.setProperty('transform-origin', '0 0')
})
}
const fn = debounce(setScale, props.delay)
watch(() => props, () => {
fn()
}, {deep: true})
onMounted(() => {
setScale();
// 添加窗口改变事件
window.addEventListener('resize', fn)
});
onBeforeUnmount(() => {
// 移除窗口改变事件
window.removeEventListener('resize', fn)
})
</script>
<style scoped>
.auto-scale-box {
position: absolute;
transform-origin: 0 0;
left: 0;
top: 0;
/*移除动画*/
/*transition: 0.4s;*/
}
</style>js
/**
* 防抖函数
* @param fn 执行的函数
* @param delay 延迟时间
* @returns {(function(): void)|*}
*/
export function debounce(fn, delay) {
let timer;
return function () {
const th = this;
const args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
timer = null;
fn.apply(th, args);
}, delay);
};
}/**
* 防抖函数
* @param fn 执行的函数
* @param delay 延迟时间
* @returns {(function(): void)|*}
*/
export function debounce(fn, delay) {
let timer;
return function () {
const th = this;
const args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
timer = null;
fn.apply(th, args);
}, delay);
};
}
