响应式的几种解决方案——媒体查询、flex、grid、多列布局、瀑布流和数据可视化屏幕的缩放处理
@media媒体查询
媒体查询入门指南
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
@media screen and (max-width: 400px) {
.container {
width: 300px;
height: 300px;
border: 1px solid aqua;
}
}
@media screen and (min-width: 500px) and (max-width: 700px) {
.container {
width: 500px;
height: 500px;
border: 1px solid rgb(30, 193, 65);
}
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
</style>
</head>
<body>
<div class="container">
<img src="./yinghua.jpg" />
</div>
<script>
let textDom = document.createElement("div");
let wid = window.innerWidth;
textDom.textContent = "现在的屏幕宽度是:" + wid + "px";
document.body.appendChild(textDom);
window.addEventListener("resize", function () {
wid = window.innerWidth;
textDom.textContent = "现在的屏幕宽度是:" + wid + "px";
});
</script>
</body>
</html>
媒体查询就是根据不同的屏幕大小编写一套对应的css。
在bootstrap的源码中,可以看到非常多的媒体查询使用
js实现响应式
监听resize
事件,根据屏幕宽度设置css,比如:
<template>
<div :style="{ width: innerW < 500 ? '400px' : '500px' }" class="inner-box"></div>
</template>
<script setup>
const innerW = ref(window.innerWidth);
const onResize = (e) => {
innerW.value = window.innerWidth;
};
onMounted(() => {
window.addEventListener("resize", onResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", onResize);
})
</script>
<style scoped lang="less">
.inner-box {
height: 200px;
background-color: aqua;
}
</style>
meta标签
<!-- 对移动设备开启响应式 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
MDN原文:
name
name
和content
属性可以一起使用,以名 - 值对的方式给文档提供元数据,其中 name 作为元数据的名称,content 作为元数据的值。 在标准元数据名称中查看 HTML 规范等规范中定义的标准元数据名称。
content
此属性包含
http-equiv
或name
属性的值,具体取决于所使用的值。
作用:控制视口的尺寸和缩放。width=device-width
设置视口宽度为设备的宽度,initial-scale=1.0
设置初始缩放比例为 1。
flex和组件库Row/Col栅格的响应式
Element-plus、iView、Ant-Design-Vue等组件库都会有Grid栅格
这一栏,右侧分类有响应式布局
在AntDV的栅格一栏,F12查看元素设置,其利用的了@media
媒体查询和flex
缩放
响应式布局
参照 Bootstrap 的 响应式设计,预设六个响应尺寸:
xs
sm
md
lg
xl
xxl
。
bootstrap中文网-布局-栅格
关于flex: 0 0 25%;
MDN-flex
flex: 0 0 25%
是 flex
布局中的一种简写属性,分别设置了 flex-grow
、flex-shrink
和 flex-basis
。下面解释各个部分的含义:
1. flex-grow
(元素的放大比例)
-
含义:当容器中有剩余空间时,
flex-grow
定义了项目如何分配这个剩余空间。 -
取值
:可以是任何非负数(0、1、2 等)。
0
:表示项目不放大,容器中有剩余空间时,元素不会变大。1
:表示元素按比例分配剩余空间。如果多个元素的flex-grow
值为 1,它们会平等地分配容器中的剩余空间。如果一个元素的flex-grow
为 2,另一个为 1,则前者分配的剩余空间是后者的两倍。
2. flex-shrink
(元素的缩小比例)
-
含义:当容器空间不足时,
flex-shrink
决定了项目如何缩小以适应容器的尺寸。 -
取值
:可以是任何非负数。
0
:元素不会缩小,超出容器部分可能会溢出。1
:元素按比例缩小。当容器空间不足时,flex-shrink
为 1 的元素将按比例缩小。如果有两个元素,一个flex-shrink
为 2,另一个为 1,则前者缩小得更多。
3. flex-basis
(元素的初始大小)
- 含义:指定了元素的初始大小(可以是长度、百分比等)。这决定了元素在分配空间之前的基础宽度或高度(取决于
flex-direction
)。 - 取值:可以是具体的尺寸(如
px
、%
等)或auto
。auto
:元素的初始大小是内容的大小或根据宽度/高度属性来确定。- 具体值(如
25%
、100px
):明确设置元素的初始大小,容器的剩余空间将在分配之前根据这个大小计算。
注意:
-
默认值是
0 1 auto
,即默认不放大,允许缩小,初始大小由内容决定。 -
flex: 1 1 auto;
自动放大、缩小,宽度由内容决定 -
flex: 100px;
实际为flex: 1 1 100px
-
当两个兄弟元素设置为:
.box-1 { flex: 200px; } .box-2 { flex: 100px; }
那么两个元素的宽度差始终为:200px - 100px = 100px,
如果是百分比,宽度差为则为:(20% - 10%)* 父元素宽度
栅格布局
阮一峰 CSS Grid 网格布局教程
Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。Grid 布局远比 Flex 布局强大。
自动填充方案
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px 1fr));
有时,单元格的大小是固定的,但是容器的大小不确定。如果希望每一行(或每一列)容纳尽可能多的单元格,这时可以使用auto-fill
关键字表示自动填充。
grid+media
@media (max-width: 400px) {
.box {
grid-template-columns: 1fr 1fr;
}
}
多列布局
MDN-多列布局
column-count: 3;
column-gap: 10px;
column-width: 100px;
和栅格布局很相似,但是栅格布局能够更精确的控制行列
瀑布流布局
column
多列布局实现:
<style>
.box {
column-count: 3;
column-gap: 10px;
}
img {
width: 100%;
}
@media (min-width: 768px) {
.box {
column-count: 4;
}
}
</style>
<div class="box">
<img src="./yinghua.jpg" alt="" />
<img src="./tianye.jpg" alt="" />
<img src="./green.jpg" alt="" />
<img src="./green.jpg" alt="" />
<img src="./1.jpg" alt="" />
<img src="./2.jpg" alt="" />
<img src="./3.jpg" alt="" />
<img src="./4.jpg" alt="" />
<img src="./5.jpg" alt="" />
<img src="./6.jpg" alt="" />
</div>
grid
栅格布局实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Masonry Layout</title>
<style>
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* 动态列宽 */
grid-gap: 20px; /* 网格间距 */
grid-auto-rows: 10px; /* 定义自动行高的小单位 */
}
.grid-item {
background-color: lightblue;
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
line-height: 1.5;
}
/* 通过行跨度实现不同高度的效果 */
.grid-item:nth-child(odd) {
grid-row: span 20; /* 偶数项跨越更多行 */
}
.grid-item:nth-child(even) {
grid-row: span 10; /* 奇数项跨越较少行 */
}
</style>
</head>
<body>
<div class="grid-container">
<div class="grid-item">1. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
<div class="grid-item">2. Proin ac orci eu erat volutpat aliquam.</div>
<div class="grid-item">3. Integer feugiat diam nec metus cursus, et sagittis mauris laoreet.</div>
<div class="grid-item">4. Nulla facilisi.</div>
<div class="grid-item">5. Donec sollicitudin.</div>
<div class="grid-item">6. Maecenas scelerisque.</div>
<div class="grid-item">7. Vestibulum et justo sit amet est auctor hendrerit.</div>
<div class="grid-item">8. Integer feugiat diam nec metus cursus.</div>
</div>
</body>
</html>
栅格布局的第二种实现:
注意:masonry目前只在火狐浏览器支持
.box {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
grid-template-rows:masonry;
}
img {
width: 100%;
display: block;
}
图片的响应式方案
除了上述几种方案,图片还独特的方式。
-
img
标签<img src="./yinghua.jpg" srcset="./tianye.jpg 1000w, ./green.jpg 800w" sizes="(max-width: 400px) 200px, (max-width: 500px) 300px" />
- 其中
800w
意义张图片的宽度是 800 像素。
- 其中
-
picture
标签<picture> <source media="(max-width: 400px)" srcset="./tianye.jpg"></scource> <source media="(max-width: 600px)" srcset="./green.jpg"></scource> <img src="./yinghua.jpg" /> </picture>
其实这两种实现方案也是“媒体查询“,只不过换了一种写法
字体的响应式方案和特殊的单位
根据实际情况,采用媒体查询和rem
、vw
等方式实现
font-size: 3rem
font-size: 4vw
- 为了不让字体在屏幕较窄的情况下变的特别小,可以改进为:
font-size: calc(2rem + 4vw)
- 为了不让字体在屏幕较窄的情况下变的特别小,可以改进为:
rem
:相对于根元素(<html>
)的字体大小,即**html
元素的 font-size
**。它不会受到父元素字体大小的影响。默认情况下,浏览器的根元素字体大小通常是 16px
,但你可以修改。
html {
font-size: 16px;
}
.child {
font-size: 2rem; /* 2rem = 2 * 16px = 32px */
}
数据可视化中的zoom缩放
获取地址栏中的z
这个参数,以此改变缩放方式
/**
* 获取地址栏参数
* @param {*} name
* @returns
*/
export const getUrlKey = (name) => {
return (
decodeURIComponent(
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(
location.href
) || [, ''])[1].replace(/\+/g, '%20')
) || null
)
}
/** 缩放屏幕 */
export const zoom = () => {
let k = getZoom()
if (getUrlKey('z')) {
document.body.style.transform = `scale(${k[0]},${k[1]})`
} else {
let z1 = Math.min(...k)
document.body.style.transform = `scale(${z1})`
}
}
/**
* 获取缩放比例
* @returns {number}
*/
function getZoom() {
const [W, H] = [window.innerWidth, window.innerHeight]
const realk = W / H
let k1, k2
k1 = H / config.sHeight
k2 = W / config.sWidth
return [k2, k1]
}
app.vue
window.addEventListener('resize', () => {
zoom()
})
.less
@zoom: true; // 定义一个布尔变量,用来控制不同的响应布局模式
@sWidth: 1920; // 设计稿的宽度,单位是px
@sHeight: 1080; // 设计稿的高度,单位是px
@remHeight: @sHeight*100/@sWidth; // 通过宽高比例计算出rem单位下的高度
// 定义一个mixin函数 .fontRoot,根据传入的 @a 值切换字体大小设置
.fontRoot (@a) when (@a = true) {
font-size: unit((@sWidth/100),px); // 当 @a = true 时,设置字体大小为设计稿宽度的 1/100
}
.fontRoot (@a) when (@a = false) {
font-size: 1vw; // 当 @a = false 时,字体大小设置为视口宽度的1%
}
// 定义一个mixin函数 .wandh,根据传入的 @a 值切换宽高设置
.wandh (@a) when (@a = true) {
width: ~"@{sWidth}px"; // 当 @a = true 时,设置宽度为设计稿宽度
height: ~"@{sHeight}px"; // 设置高度为设计稿高度
}
.wandh (@a) when (@a = false) {
width: 100rem; // 当 @a = false 时,设置宽度为 100rem,适应不同分辨率
height: ~"@{remHeight}rem"; // 高度通过 rem 值计算,保持设计稿比例
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
width: 100vw; // 设置html宽度为视口宽度
height: 100vh; // 设置html高度为视口高度
.fontRoot(@zoom); // 根据 @zoom 的值调用 .fontRoot 函数来设置字体大小
background-color: rgb(0, 0, 0); // 背景色为黑色
}
body {
display: flex; // 使用flex布局
position: relative;
overflow-y: hidden; // 隐藏竖向滚动条
overflow-x: hidden; // 隐藏横向滚动条
width: 100%; // body的宽度设置为 100%
height: 100%; // body的高度设置为 100%
user-select: none; // 禁止文本选择
background-color: rgb(0, 0, 0) !important; // 强制设置背景色为黑色
align-items: center; // 垂直方向居中
justify-content: center; // 水平方向居中
color: white; // 文本颜色为白色
}
#app {
margin: 0 auto; // 水平居中
.wandh(@zoom); // 根据 @zoom 的值调用 .wandh 函数来设置宽高
}
.router-main-box {
position: relative; // 位置相对定位
.wandh(@zoom); // 根据 @zoom 的值调用 .wandh 函数来设置宽高
}