什么是固定宽高比容器?

定义:
无论浏览器窗口或页面元素如何变化,容器的宽度和高度永远保持固定的比例,如16:9。

应用场景:
如大屏自适应,内部元素对容器宽高比依赖很高的复杂模块布局等。

纯CSS实现固定宽高比的容器

原理

  • padding-top的值为百分比时,最终padding-top的计算值 = 百分比 * 当前元素宽度的计算值,从而通过padding-top将容器撑开。
  • 为容器添加样式 box-sizing: border-box; 则容器的高度 = padding-top的计算值,从而实现容器高度根据容器的宽度按百分比自适应变化。
  • 使用“子绝父相”,让容器中的内容完全覆盖容器。

cssRatioContainer.vue

<template>
<div class="box" :style="ratioStyle">
<div class="content" :style="contentStyle">
<slot></slot>
</div>
</div>
</template>

<script>export default {
props: {
ratio: {
type: Number,
default: 1,
},
overflow: {
type: String,
default: "auto",
},
overflowX: {
type: String,
},
overflowY: {
type: String,
},
},
computed: {
ratioStyle() {
let ratio = (1 / this.ratio) * 100;
if (ratio == null || Number.isNaN(ratio)) {
ratio = 100;
}
if (typeof ratio !== "string" || !ratio.endsWith("%")) {
ratio = ratio + "%";
}
return `padding-top: ${ratio};`;
},

contentStyle() {
const styles = [];
if (this.overflow) {
styles.push("overflow:" + this.overflow);
}
if (this.overflowX) {
styles.push("overflow-x:" + this.overflowX);
}
if (this.overflowY) {
styles.push("overflow-y:" + this.overflowY);
}
return styles.join(";");
},
},
};</script>

<style scoped>.box {
position: relative;
box-sizing: border-box;
}
.content {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}</style>

JS实现固定宽高比的容器

原理

  • 通过js获取容器的宽度计算值后,按宽高比计算出容器的高度,并赋值给容器。

jsRatioContainer.vue

<template>
<div id="jsRatioContainerBox" class="box" :style="contentStyle">
<slot></slot>
</div>
</template>

<script>export default {
props: {
ratio: {
type: Number,
default: 1,
},
overflow: {
type: String,
default: "auto",
},
overflowX: {
type: String,
},
overflowY: {
type: String,
},
},
data() {
return {
myHeight: "",
};
},
computed: {
contentStyle() {
const styles = [];
if (this.myHeight) {
styles.push("height:" + this.myHeight);
}
if (this.overflow) {
styles.push("overflow:" + this.overflow);
}
if (this.overflowX) {
styles.push("overflow-x:" + this.overflowX);
}
if (this.overflowY) {
styles.push("overflow-y:" + this.overflowY);
}
return styles.join(";");
},
},
mounted() {
this.setHeight();
window.addEventListener("resize", () => {
this.setHeight();
});
},
watch: {
ratio() {
this.setHeight();
},
},
methods: {
// 根据宽高比和当前宽度,设置最终的高度
setHeight() {
this.$nextTick(() => {
let myDom = document.getElementById("jsRatioContainerBox");
let width = window.getComputedStyle(myDom)["width"];
this.myHeight =
Number(width.replace("px", "")) * (1 / this.ratio) + "px";
});
},
},
};</script>

<style scoped>.box {
width: 100%;
}</style>

使用范例

vue组件封装——固定宽高比的容器(2种方法:纯CSS实现 + JS实现)_宽高

<template>
<div class="father">
<h2>CSS实现固定宽高比的容器</h2>
<CssRatioContainer
ref="CssRatioContainer"
id="CssRatioContainer"
:ratio="ratioWidth / ratioHeight"
>
<div class="itemBox" v-for="item in 100" :key="item">段落{{ item }}</div>
</CssRatioContainer>
<p>容器的宽度为:{{ mySize.width }}</p>
<p>容器的高度为:{{ mySize.height }}</p>
<label>容器的宽高比:</label>
<input
class="inputStyle"
type="number"
v-model="ratioWidth"
@input="getSize"
/>

<input
class="inputStyle"
type="number"
v-model="ratioHeight"
@input="getSize"
/>
<h2>JS实现固定宽高比的容器</h2>
<JsRatioContainer :ratio="ratioWidth / ratioHeight">
<div class="itemBox" v-for="item in 100" :key="item">段落{{ item }}</div>
</JsRatioContainer>
</div>
</template>
<script>import CssRatioContainer from "./cssRatioContainer.vue";
import JsRatioContainer from "./jsRatioContainer.vue";
export default {
components: { CssRatioContainer, JsRatioContainer },
mounted() {
this.getSize();
window.addEventListener("resize", () => {
this.getSize();
});
},
methods: {
getSize() {
this.$nextTick(() => {
let myDom = document.getElementById("CssRatioContainer");
let myComputedStyle = window.getComputedStyle(myDom);
this.$set(this.mySize, "height", myComputedStyle.height);
this.$set(this.mySize, "width", myComputedStyle.width);
});
},
},

data() {
return {
mySize: {},
ratioWidth: 6,
ratioHeight: 1,
};
},
};</script>
<style scoped>.father {
max-width: 1200px;
margin: 20px;
}
.itemBox {
background: red;
border: 1px solid yellow;
}
.inputStyle {
width: 60px;
text-align: center;
}</style>