transform: scale(x, y)
通过监测window 的resize 事件,在resize 事件中,实时的获取浏览器视口的宽和高,用视口的宽、高度与UI 设计稿的宽、高度进行比较,得出实际缩放值。
// 实现方案:保持和设计稿比例
// 大屏中有图片背景、包含各种点击事件,但没有地图组件
<template>
<div id="screen"></div>
</template>
<script>
export default {
mounted() {
this.setScale()
window.onresize = this.debounce(this.setScale, 10)
},
methods: {
debounce: (fn, t) => {
const delay = t || 500
let timer
return function() {
const args = arguments
if (timer) {
clearTimeout(timer)
}
const context = this
timer = setTimeout(() => {
timer = null
fn.apply(context, args)
}, delay)
}
},
// 获取放大缩小比例
getScale() {
const w = window.innerWidth / 1920
const h = window.innerHeight / 1000
return [w, h]
},
// 设置比例
setScale() {
const bigScreen = document.querySelector('#screen')
bigScreen.style.transform = `scale(${this.getScale()[0]},${this.getScale()[1]})`
bigScreen.style.transformOrigin = '(0, 0)'
}
}
}
</script>
<style lang="scss" scoped>
#screen {
z-index: 999;
top: 0;
right: 0;
left: 0;
position: fixed;
display: block;
height: 1080px;
width: 1920px;
transform-origin: 0 0;
background: url(../../../assets/base/img/screen_base.png) no-repeat;
background-size: 100% 100%;
}
</style>
// 类似方案:思路一样
<template>
<div class="Scale" ref="Scale" :style="{
width: width + 'px',
height: height + 'px',
}">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'scale-container',
props: {
width: {
type: Number,
default: () => 1920
},
height: {
type: Number,
default: () => 1080
}
},
data () {
return {}
},
mounted () {
this.setScale()
window.addEventListener('resize', this.debounce(this.setScale))
},
methods: {
getScale () {
const { width, height } = this
const wh = window.innerHeight / height
const ww = window.innerWidth / width
return { scaleX: ww, scaleY: wh }
},
setScale () {
const { scaleY, scaleX } = this.getScale()
if (this.$refs.Scale) {
this.$refs.Scale.style.setProperty('--scaleX', scaleX)
this.$refs.Scale.style.setProperty('--scaleY', scaleY)
}
},
debounce (fn, delay) {
const delays = delay || 200
let timer
return function () {
const th = this
const args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function () {
timer = null
fn.apply(th, args)
}, delays)
}
}
}
}
</script>
<style lang="less">
.Scale {
position: absolute;
transform: scale(var(--scaleX), var(--scaleY)) translate(-50%, -50%);
display: flex;
flex-direction: column;
transform-origin: 0 0;
left: 50%;
top: 50%;
transition: 0.3s;
z-index: 999;
}
</style>
【总结】
优势
- 兼容性更好
- 不会引起重排(回流),只会发生重绘
劣势
scale 缩放有两种方式,一种是缩放不保持U I设计稿比例缩放,另一种是保持UI 设计稿比例缩放
不保持UI 设计稿比例缩放
- 对于同一比例的分辨率适配,对于不同比例的分辨率,scale 缩放会直接导致页面有压缩。
- 对于antd 组件库中的Select 的弹出层位置也会出现很大偏差
- 如果地图是高德地图或者其他GIS 地图的话,会导致鼠标点击地图的点有位置偏差,此种情况下,采用对地图再次应用scale,地图的scale 值与body 身上的scale 值相乘等于1即可
保持UI 设计稿比例缩放
- 如果地图是高德地图或者其他GIS 地图的话,会导致鼠标点击地图的点有位置偏差。(目前没找到完美解决方案)
- 对于antd 组件库中的Select 的弹出层位置也会出现很大偏差
v-scale-screen - transform
v-scale-screen使用css属性transform 实现缩放效果,进行等比例计算,达到等比例缩放的效果,同时也支持铺满全屏,宽度等比,高度等比,等自适应方案
npm install v-scale-screen -save
vue2 中使用插件导入,在main.js 中引入
// main.js
import VScaleScreen from 'v-scale-screen'
Vue.use(VScaleScreen)
vue3 以组件导入
<v-scale-screen width="1920" height="1080">
<div>
<v-chart>....</v-chart>
<v-chart>....</v-chart>
<v-chart>....</v-chart>
<v-chart>....</v-chart>
<v-chart>....</v-chart>
</div>
</v-scale-screen>
<script>
import VScaleScreen from 'v-scale-screen'
export default {
components:{
VScaleScreen
}
}
</script>
【分析】
- 上下左右会有留白
- 地图组件,坐标位置会有问题 => 使用ifame引入组件
rem 适配
1、安装 postcss-pxtorem 插件,在webpack 打包的时候,将css 中的px 自动转成rem。
npm install postcss postcss-loader postcss-pxtorem --save
2、项目根目录下创建postcss.config.js 文件
module.exports = {
plugins: [
[
'postcss-preset-env',
{
// autoprefixer: {
// grid: true,
// },
},
],
[ 'postcss-pxtorem', { rootValue: 100, propList: ['*'],
},
],
],
}
3、修改webpack 配置,编译sass/less 的时候,新增postcss-loader,注意:postcss-loader 需要配置在css-loader 之前执行
{
loader: require.resolve('postcss-loader'),
}
4、动态修改html 标签fontSize 的大小
// 封装一个根据屏幕尺寸自动改变html 的font-size 大小的函数
const htmlDOM = document.getElementsByTagName('html')[0]
const designWidth = 1920 // 设计稿的宽度 --- 已知
const designHtmlFontSize = 100 // 设计稿上面的html font-size的大小 --- 已知
let actualHtmlFontSize // 实际html font-size的大小 --- 未知
function setRootFontSize() {
const viewportWidth = document.documentElement.clientWidth || document.body.clientWidth // 实际浏览器视口的宽度 ---- 已知
actualHtmlFontSize = viewportWidth * designHtmlFontSize / designWidth // 3个已知条件,1个未知条件
htmlDOM.style.fontSize = actualHtmlFontSize + 'px'
}
setRootFontSize()
window.addEventListener('resize', setRootFontSize)
【总结】
优势: 自适应流畅,实时根据浏览器视口的宽度变化而适应。开发者书写样式的时候,无需考虑单位转换问题。
劣势:会受到浏览器fontSize 最小为12px 的影响,同时也会受到windows 系统自带的缩放影响,根据实际使用来看rem 布局在web端只适配不同分辨率但是同一宽高比,并且windows 系统缩放为100% 的情况。
所以在web端 采用rem 布局不太合适,移动端采用rem 布局问题是不大的。
zoom 适配 - zoom
export const setBodyZoom = () => {
const t = window.screen.width
let zoomNum = t / 1920 * 10
zoomNum = Math.round(zoomNum) / 10
document.body.style.zoom = zoomNum
}
【参考资料】