大屏自适应方案
整体缩放
利用已有的工具vue3-scal等对大屏进行整体缩放,该方案很好,但不适用带有地图的项目,缩放后地图点击事件获取的坐标等会因为缩放产生偏移
局部缩放
仅针对某个容器盒子进行缩放,使用自定义指令的方案,使用起来方便快捷
typescript
import type { App, DirectiveBinding } from 'vue';
/**
* 缩放指令(设计稿固定1920*1080)
* @directive 根据1920*1080设计稿自动缩放(v-scale)
* @directive 支持多种基准点配置和缩放模式
* @directive 默认无参数设计稿缩放,左上角基准点 会有压缩变形
* @directive 参数ratio 固定比例缩放 value 为缩放比例
* @directive 参数origin 按照设计稿比例,以高度比例缩放或者以宽度比例缩放 value 为 'X' 'Y' 默认是 'Y', 例如,浏览器高度小于设计稿高度 value 为 'Y'
*
* 修饰符 top-right top-center bottom-left bottom-right bottom-center center ,用于缩放后的盒子固定到哪个位置显示
*
* @example
* v-scale - 默认设计稿缩放,左上角基准点
* v-scale.top-right - 设计稿缩放,右上角基准点
* v-scale:ratio="0.5" - 固定比例缩放
* v-scale:origin.top-right="'Y'" - 无压缩缩放
*/
export function scaleDirective(app: App) {
// 设计稿尺寸常量
const DESIGN_WIDTH = 1920;
const DESIGN_HEIGHT = 1080;
// 统一的缩放指令
app.directive('scale', {
mounted(el: HTMLElement, binding: DirectiveBinding) {
// 获取基准点配置
const origin = getOriginFromBinding(binding);
// 根据参数判断缩放模式
if (binding.arg === 'ratio') {
// 固定比例缩放模式
const ratio = binding.value || 1;
el.style.transform = `scale(${ratio})`;
el.style.transformOrigin = origin;
el.style.willChange = 'transform';
} else if (binding.arg === 'origin') {
// 无压缩变形缩放模式
const updateScale = () => {
const { innerWidth: vw, innerHeight: vh } = window;
// 计算缩放比例(固定1920*1080设计稿)
const scaleX = vw / DESIGN_WIDTH;
const scaleY = vh / DESIGN_HEIGHT;
const ratio = binding?.value === 'X' ? scaleX : scaleY;
el.style.transform = `scale(${ratio})`;
el.style.transformOrigin = origin;
el.style.willChange = 'transform';
};
// 初始缩放
updateScale();
// 监听窗口大小变化
window.addEventListener('resize', updateScale);
// 存储更新函数,便于卸载时移除监听器
(el as any)._scaleUpdate = updateScale;
} else {
// 默认设计稿缩放模式
const updateScale = () => {
const { innerWidth: vw, innerHeight: vh } = window;
// 计算缩放比例(固定1920*1080设计稿)
const scaleX = vw / DESIGN_WIDTH;
const scaleY = vh / DESIGN_HEIGHT;
// 应用缩放和基准点
el.style.transform = `scale(${scaleX}, ${scaleY})`; // 这种缩放可能会变形
// el.style.transform = `scale(${scaleY})`;// 保持宽高比缩放
el.style.transformOrigin = origin;
el.style.willChange = 'transform';
};
// 初始缩放
updateScale();
// 监听窗口大小变化
window.addEventListener('resize', updateScale);
// 存储更新函数,便于卸载时移除监听器
(el as any)._scaleUpdate = updateScale;
}
},
beforeUnmount(el: HTMLElement) {
// 清理事件监听器(仅设计稿缩放模式需要)
if ((el as any)._scaleUpdate) {
window.removeEventListener('resize', (el as any)._scaleUpdate);
}
}
});
}
/**
* 从绑定对象获取基准点配置
*/
function getOriginFromBinding(binding: DirectiveBinding): string {
// 从修饰符获取
const modifiers = binding.modifiers;
if (modifiers.topLeft || modifiers['top-left']) return 'top left';
if (modifiers.topRight || modifiers['top-right']) return 'top right';
if (modifiers.topCenter || modifiers['top-center']) return 'top center';
if (modifiers.bottomLeft || modifiers['bottom-left']) return 'bottom left';
if (modifiers.bottomRight || modifiers['bottom-right']) return 'bottom right';
if (modifiers.bottomCenter || modifiers['bottom-center']) return 'bottom center';
if (modifiers.center) return 'center';
// 默认左上角
return 'top left';
}
使用方法
typescript
<template>
// 这个会变形 因为缩放分别取的是高度和宽度的缩放比例
<div v-scale.top-left class="panel-left">
<PanelLeft />
</div>
// 这个不会变形
<div v-scale:origin.top-right class="panel-right">
<PanelRight />
</div>
</template>