前言
又来摸鱼了, 适配这个问题确实有点老生常谈了, 这里主要分享下我开发中常用的两种适配方式. 也算回馈下社区. 新手可学习, 老手随缘~
这边文章主要介绍如何高效率完成适配部分的代码编写, 追求的是效率, 看情况使用~
文末另付 vue 版本实现一份, 随缘使用~
常规的适配方案分析
通常来说, 适配的手段是绕不过这些的 (也有其他方式, 这里只简单介绍). 我印象里好像jQuery时代还有个适配方式, 忘了具体是啥了.
方案实现内容就不细说了, 分析下采用这些方案的常规操作和优缺点 (有一定的个人偏见, 随缘采纳吧~)
- viewport (vw/vh) 方案
- 一般用 vw/vh 需要借助设计工具(如: 蓝湖, figma)的转换功能, 单靠前端自己转(要疯~), 主要突出就一点, 还原度好, 但是开发(主要是调样式)麻烦.
- rem/em 方案
- 基本同上, 尤其是使用 px2rem 工具的时候, 以及 1px 问题无法解决.
- 媒体查询 + 通用样式
- 完美的网站适配方案, 还原度, 兼容性啥啥都好, 就一点 写起来麻烦, 需要花点功夫.
- 另外, 本质上来说 媒体查询是为每一个不同的设备重新实现一套样式. 此中花费的时间一言难尽 (time = n * 0.5, t: 开发1份的时间, n: 适配的场景)
- 缩放 (zoom/scale)
- 这篇文章主要介绍下这个
- 效率高, 还原度高, 能解决一些适配的问题(如: 1px 边框)
- 适用场景: 大屏、官网等
- 单纯用缩放其实坑很多的, 比如定位偏移、滚动条错位等. 而且, 适用场景上其实相较于上面是有些局限性的,但架不住他写的快啊~
详细介绍下为什么用缩放以及怎么用的问题
简介
借助css提供的缩放能力(zoom / scale), 对原始的网页进行缩放, 适应当前屏幕的宽度达到缩放效果.
在这个基础上, 再抽象成组件, 针对指定的block进行缩放, 增强一下适用场景. (毕竟一些场景下, 网页的某些部分是需要保持原始的尺寸或单独写适配的)
最终, 得出了一个支持按需使用的自动缩放组件 (见文末)
主要有几个特点,
- 开发效率高, 如果有原有代码, 改动范围比较小, 调试也方便. 毕竟不管屏幕尺寸如何, 始终保持与设计稿相同的比例. (再也不用担心UI走查了, 明确告诉他们写的跟他们设计稿的像素是一毛一样的, 有证据)
- 支持按需使用, 针对需要适配的部分组件使用即可.
- 牺牲了一定的用户体验 (比如: 用户没法再放大、缩小窗口。但也影响不大, 毕竟只有追求效率才会用这个方案, 要不早用 media 一个一个扣了)
为啥说这种方式效率高
我想应该没有别的方式能比无脑贴样式开发网页来的快吧(低代码, 设计稿转的那种除外), 另外, 就是调试方面, chrome devTool 里面的样式表跟你的原始样式是一样的, 不会像 vw / rem 这些需要找计算后的属性.
怎么用
Tips: 其实还有一种方式是, 直接修改 meta 标签进行缩放操作. 但这种方式, resize 需要刷新窗口才能重渲染, 暂且不提.
建议用我封装的 <AutoZoom> 组件, 主要是用缩放有一些坑, 直接贴代码了
<template>
<AutoZoom :design-width="isPortrait ? 360:920">
<your component />
...
</AutoZoom>
</template>
坑在哪
zoomchrome 支持, firefox 不支持zoom会导致 canvas 焦点错位, 主要表现在echarts图表类canvas组件光标焦点错误.translate:scale在firefox上会产生额外的滚动条 (我搞的组件里面捣鼓了下, 但不确定别人用会不会有问题, 用的时候注意下吧)
适用场景
大屏、官网等强UI还原度要求, 且弱交互场景.
就先写到这吧, 累了 .
VUE 实现 (vue3.x 按需自取)
前面废话了太多, 直接看代码吧
<script lang="ts" setup>
/**
* 自适应缩放视图容器
*
* @description 通过缩放方式处理不同设备的适配问题, 不同于rem方案, 这个方案使用 px 像素填充样式, 相对来说更加高效.
* @support 适用于 pc 端少交互类的看板应用, 适配条件遵循宽度优先原则, 保证与设计稿的宽度一致.
*
* @author libin<libin@persagy.com>
* @date 2023年10月27日 11:14:30
*/
import { ComputedRef, Ref, computed, onMounted, provide, ref } from 'vue'
import { useResizeObserver } from '@vueuse/core'
const props = defineProps<{
/**
* 设计稿宽度
*
* @default 1920 (pc)
* @default 750 (mobile, 2倍图)
*/
designWidth?: number
/**
* 最小缩放倍数
*
* @default 0.2
*/
minScaleSize?: number
/** 容器左右间距 (计算 AutoZoom 组件 未铺满页面x轴的情况) */
margin?: number
/**
* 容器顶部其他元素占用高度 (用来处理纵向滚动条过长问题)
*/
padding?: number
}>()
/** zoom 的缩放性能更好, 能用zoom就用zoom */
const zoomSupport: ComputedRef<boolean> = computed(() => {
const testEl = document.createElement('div')
const support: boolean = 'zoom' in testEl.style
return support
})
/** 初始属性 */
const initialValue: ComputedRef<{ designWidth: number; minScaleSize: number }> = computed(() => {
const inMobile: boolean = /mobile/.test(navigator.userAgent.toLowerCase())
const defaultDesignWidth: number = inMobile ? 750 : 1920
return {
designWidth: props.designWidth ?? defaultDesignWidth,
minScaleSize: props.minScaleSize ?? 0.2
}
})
/** 缩放率 */
const scale: Ref<number> = ref(1)
/** 容器实际渲染高度 */
const height: Ref<number> = ref(0)
/** 容器高度 */
const containerHeight: ComputedRef<string> = computed(() => {
return height.value ? height.value + 'px' : '100%'
})
/** 滚动区间高度 */
const scrollHeight: ComputedRef<string> = computed(() => {
return height.value ? height.value / scale.value + 'px' : 'auto'
})
/** 监听到浏览器尺寸变化时, 实时调整缩放倍率 */
const resize = (): void => {
console.log('重计算缩放率')
// @ 当前浏览器宽高 (如果距标准设计稿差2px以内, 使用设计稿像素)
const innerWidth: number =
Math.abs(window.innerWidth - initialValue.value.designWidth) < 2
? initialValue.value.designWidth
: window.innerWidth
// > 计算缩放比例
const scaleValue: number = (innerWidth - (props.margin ?? 0)) / initialValue.value.designWidth
// > 赋值
scale.value = scaleValue > initialValue.value.minScaleSize ? scaleValue : initialValue.value.minScaleSize
// > 计算容器高度
height.value = window.innerHeight - (props.padding ?? 0)
}
const el: Ref<HTMLElement | null> = ref(null)
/** 挂载监听事件 & 触发初次重绘 */
onMounted((): void => resize())
useResizeObserver([el, document.body], (): void => resize())
/** 暴露 patchResize 主动刷新缩放率方法 */
provide('patchResize', resize)
</script>
<template>
<!-- 自适应容器 -->
<div
:class="zoomSupport ? 'auto-zoom' : 'auto-scale'"
:style="{
'--design-width--': initialValue.designWidth + 'px',
'--scale--': scale,
height: containerHeight
}"
@resize="resize"
>
<div class="scroll" :style="{ height: scrollHeight }">
<slot />
</div>
</div>
</template>
<style lang="less" scoped>
.auto-zoom {
width: var(--webkit-fill-available, 100%);
flex-shrink: 1;
overflow: hidden;
:deep(*, :after, :before) {
box-sizing: content-box;
}
.scroll {
width: var(--design-width--, 1920px);
height: auto;
zoom: var(--scale--, 1);
transform-origin: left top;
overflow-y: auto;
}
:deep(canvas) {
zoom: calc(1 / var(--scale--, 1)) !important;
transform: scale(var(--scale--, 1)) !important;
transform-origin: left top;
}
}
.auto-scale {
width: var(--webkit-fill-available, 100%);
flex-shrink: 1;
overflow: hidden;
:deep(*, :after, :before) {
box-sizing: content-box;
}
.scroll {
width: var(--design-width--, 1920px);
height: auto;
transform: scale(var(--scale--, 1));
transform-origin: left top;
overflow-y: auto;
}
}
</style>