AspectRatio 宽高比
锁定子元素宽高比的容器(基于 CSS aspect-ratio),常用于图片 / 视频 / 嵌入帧占位。
基础用法
ratio 是一个数字(宽 / 高)。容器宽度跟随父元素,高度由 CSS aspect-ratio 计算。
背景
16 / 9video cover
<script setup lang="ts">
import { CfAspectRatio } from '@chufix-design/vue';
</script>
<template>
<div class="aspect-demo aspect-demo--basic">
<CfAspectRatio :ratio="16 / 9">
<div class="aspect-demo__hero">
<span class="aspect-demo__ratio">16 / 9</span>
<span class="aspect-demo__label">video cover</span>
</div>
</CfAspectRatio>
</div>
</template>
<style scoped>
.aspect-demo {
width: min(100%, 420px);
}
.aspect-demo__hero {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.35rem;
border: 1px solid oklch(80% 0.08 230 / 45%);
border-radius: var(--r-8);
background:
radial-gradient(circle at 18% 18%, oklch(85% 0.12 195 / 70%), transparent 32%),
linear-gradient(135deg, oklch(66% 0.16 224), oklch(58% 0.18 284));
color: white;
box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 18%);
}
.aspect-demo__ratio {
font-family: var(--font-mono);
font-size: 1.75rem;
font-weight: var(--w-semibold);
line-height: 1;
}
.aspect-demo__label {
font-size: var(--t-12);
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.78;
}
</style> <script setup>
import { CfAspectRatio } from '@chufix-design/vue';
</script>
<template>
<div class="aspect-demo aspect-demo--basic">
<CfAspectRatio :ratio="16 / 9">
<div class="aspect-demo__hero">
<span class="aspect-demo__ratio">16 / 9</span>
<span class="aspect-demo__label">video cover</span>
</div>
</CfAspectRatio>
</div>
</template>
<style scoped>
.aspect-demo {
width: min(100%, 420px);
}
.aspect-demo__hero {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.35rem;
border: 1px solid oklch(80% 0.08 230 / 45%);
border-radius: var(--r-8);
background:
radial-gradient(circle at 18% 18%, oklch(85% 0.12 195 / 70%), transparent 32%),
linear-gradient(135deg, oklch(66% 0.16 224), oklch(58% 0.18 284));
color: white;
box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 18%);
}
.aspect-demo__ratio {
font-family: var(--font-mono);
font-size: 1.75rem;
font-weight: var(--w-semibold);
line-height: 1;
}
.aspect-demo__label {
font-size: var(--t-12);
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.78;
}
</style> <CfAspectRatio ratio={16 / 9}>
<div>16 / 9 内容</div>
</CfAspectRatio> <CfAspectRatio ratio={16 / 9}>
<div>16 / 9 内容</div>
</CfAspectRatio> 常用比例
视频、卡片缩略图、人像头像、短视频 … 比例的视觉对照。
背景
21 / 9影院
16 / 9视频
4 / 3旧屏
1 / 1方形
3 / 4人像
9 / 16短视频
<script setup lang="ts">
import { CfAspectRatio } from '@chufix-design/vue';
const ratios = [
{ ratio: 21 / 9, label: '21 / 9', hint: '影院', tone: 'blue' },
{ ratio: 16 / 9, label: '16 / 9', hint: '视频', tone: 'cyan' },
{ ratio: 4 / 3, label: '4 / 3', hint: '旧屏', tone: 'amber' },
{ ratio: 1, label: '1 / 1', hint: '方形', tone: 'green' },
{ ratio: 3 / 4, label: '3 / 4', hint: '人像', tone: 'pink' },
{ ratio: 9 / 16, label: '9 / 16', hint: '短视频', tone: 'orange' },
];
</script>
<template>
<div class="aspect-ratios">
<div v-for="r in ratios" :key="r.label" class="aspect-ratios__item">
<CfAspectRatio :ratio="r.ratio">
<div class="aspect-ratios__card" :data-tone="r.tone">
<strong>{{ r.label }}</strong>
<span>{{ r.hint }}</span>
</div>
</CfAspectRatio>
</div>
</div>
</template>
<style scoped>
.aspect-ratios {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
gap: 0.85rem;
width: min(100%, 720px);
}
.aspect-ratios__item {
min-width: 0;
}
.aspect-ratios__card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.25rem;
border: 1px solid var(--line-1);
border-radius: var(--r-8);
background: var(--bg-1);
color: var(--fg-1);
box-shadow: var(--shadow-1);
}
.aspect-ratios__card strong {
font-family: var(--font-mono);
font-size: var(--t-14);
line-height: 1;
}
.aspect-ratios__card span {
font-size: var(--t-12);
color: var(--fg-2);
}
.aspect-ratios__card[data-tone='blue'] { background: linear-gradient(135deg, oklch(95% 0.03 225), oklch(88% 0.05 265)); }
.aspect-ratios__card[data-tone='cyan'] { background: linear-gradient(135deg, oklch(95% 0.04 195), oklch(89% 0.05 225)); }
.aspect-ratios__card[data-tone='amber'] { background: linear-gradient(135deg, oklch(96% 0.04 78), oklch(90% 0.06 52)); }
.aspect-ratios__card[data-tone='green'] { background: linear-gradient(135deg, oklch(95% 0.04 148), oklch(89% 0.05 176)); }
.aspect-ratios__card[data-tone='pink'] { background: linear-gradient(135deg, oklch(96% 0.04 340), oklch(90% 0.06 15)); }
.aspect-ratios__card[data-tone='orange'] { background: linear-gradient(135deg, oklch(96% 0.04 62), oklch(90% 0.06 36)); }
</style> <script setup>
import { CfAspectRatio } from '@chufix-design/vue';
const ratios = [
{ ratio: 21 / 9, label: '21 / 9', hint: '影院', tone: 'blue' },
{ ratio: 16 / 9, label: '16 / 9', hint: '视频', tone: 'cyan' },
{ ratio: 4 / 3, label: '4 / 3', hint: '旧屏', tone: 'amber' },
{ ratio: 1, label: '1 / 1', hint: '方形', tone: 'green' },
{ ratio: 3 / 4, label: '3 / 4', hint: '人像', tone: 'pink' },
{ ratio: 9 / 16, label: '9 / 16', hint: '短视频', tone: 'orange' },
];
</script>
<template>
<div class="aspect-ratios">
<div v-for="r in ratios" :key="r.label" class="aspect-ratios__item">
<CfAspectRatio :ratio="r.ratio">
<div class="aspect-ratios__card" :data-tone="r.tone">
<strong>{{ r.label }}</strong>
<span>{{ r.hint }}</span>
</div>
</CfAspectRatio>
</div>
</div>
</template>
<style scoped>
.aspect-ratios {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
gap: 0.85rem;
width: min(100%, 720px);
}
.aspect-ratios__item {
min-width: 0;
}
.aspect-ratios__card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.25rem;
border: 1px solid var(--line-1);
border-radius: var(--r-8);
background: var(--bg-1);
color: var(--fg-1);
box-shadow: var(--shadow-1);
}
.aspect-ratios__card strong {
font-family: var(--font-mono);
font-size: var(--t-14);
line-height: 1;
}
.aspect-ratios__card span {
font-size: var(--t-12);
color: var(--fg-2);
}
.aspect-ratios__card[data-tone='blue'] { background: linear-gradient(135deg, oklch(95% 0.03 225), oklch(88% 0.05 265)); }
.aspect-ratios__card[data-tone='cyan'] { background: linear-gradient(135deg, oklch(95% 0.04 195), oklch(89% 0.05 225)); }
.aspect-ratios__card[data-tone='amber'] { background: linear-gradient(135deg, oklch(96% 0.04 78), oklch(90% 0.06 52)); }
.aspect-ratios__card[data-tone='green'] { background: linear-gradient(135deg, oklch(95% 0.04 148), oklch(89% 0.05 176)); }
.aspect-ratios__card[data-tone='pink'] { background: linear-gradient(135deg, oklch(96% 0.04 340), oklch(90% 0.06 15)); }
.aspect-ratios__card[data-tone='orange'] { background: linear-gradient(135deg, oklch(96% 0.04 62), oklch(90% 0.06 36)); }
</style> <CfAspectRatio ratio={21 / 9}>…</CfAspectRatio>
<CfAspectRatio ratio={16 / 9}>…</CfAspectRatio>
<CfAspectRatio ratio={4 / 3}>…</CfAspectRatio>
<CfAspectRatio ratio={1}>…</CfAspectRatio>
<CfAspectRatio ratio={3 / 4}>…</CfAspectRatio>
<CfAspectRatio ratio={9 / 16}>…</CfAspectRatio> <CfAspectRatio ratio={21 / 9}>…</CfAspectRatio>
<CfAspectRatio ratio={16 / 9}>…</CfAspectRatio>
<CfAspectRatio ratio={4 / 3}>…</CfAspectRatio>
<CfAspectRatio ratio={1}>…</CfAspectRatio>
<CfAspectRatio ratio={3 / 4}>…</CfAspectRatio>
<CfAspectRatio ratio={9 / 16}>…</CfAspectRatio> 子元素自动撑满
子节点会被 position: absolute; inset: 0 撑满。常见的 <img> / <video> / <iframe> 默认 width: 100%; height: 100%; object-fit: cover,开箱即用。
背景
media fills the frameimg / video / iframe
<script setup lang="ts">
import { CfAspectRatio } from '@chufix-design/vue';
</script>
<template>
<div class="aspect-media">
<CfAspectRatio :ratio="16 / 9">
<div class="aspect-media__frame">
<div class="aspect-media__sky" />
<div class="aspect-media__mountain aspect-media__mountain--back" />
<div class="aspect-media__mountain aspect-media__mountain--front" />
<div class="aspect-media__caption">
<strong>media fills the frame</strong>
<span>img / video / iframe</span>
</div>
</div>
</CfAspectRatio>
</div>
</template>
<style scoped>
.aspect-media {
width: min(100%, 480px);
}
.aspect-media__frame {
position: relative;
overflow: hidden;
border: 1px solid var(--line-1);
border-radius: var(--r-8);
background: linear-gradient(180deg, oklch(92% 0.04 220), oklch(82% 0.06 190));
box-shadow: var(--shadow-1);
}
.aspect-media__sky {
position: absolute;
top: 16%;
left: 14%;
width: 3rem;
height: 3rem;
border-radius: 999px;
background: oklch(98% 0.05 86 / 86%);
}
.aspect-media__mountain {
position: absolute;
right: -8%;
bottom: -20%;
width: 72%;
height: 68%;
border-radius: 22% 0 0 0;
transform: rotate(35deg);
transform-origin: bottom right;
}
.aspect-media__mountain--back {
right: 12%;
bottom: -28%;
background: oklch(68% 0.09 205 / 78%);
}
.aspect-media__mountain--front {
background: oklch(48% 0.12 214 / 88%);
}
.aspect-media__caption {
position: absolute;
left: 1rem;
bottom: 1rem;
display: inline-flex;
flex-direction: column;
gap: 0.1rem;
padding: 0.55rem 0.7rem;
border: 1px solid oklch(100% 0 0 / 24%);
border-radius: var(--r-6);
background: oklch(20% 0.04 250 / 72%);
color: white;
backdrop-filter: blur(8px);
}
.aspect-media__caption strong {
font-size: var(--t-13);
line-height: 1.2;
}
.aspect-media__caption span {
font-family: var(--font-mono);
font-size: var(--t-11);
opacity: 0.72;
}
</style> <script setup>
import { CfAspectRatio } from '@chufix-design/vue';
</script>
<template>
<div class="aspect-media">
<CfAspectRatio :ratio="16 / 9">
<div class="aspect-media__frame">
<div class="aspect-media__sky" />
<div class="aspect-media__mountain aspect-media__mountain--back" />
<div class="aspect-media__mountain aspect-media__mountain--front" />
<div class="aspect-media__caption">
<strong>media fills the frame</strong>
<span>img / video / iframe</span>
</div>
</div>
</CfAspectRatio>
</div>
</template>
<style scoped>
.aspect-media {
width: min(100%, 480px);
}
.aspect-media__frame {
position: relative;
overflow: hidden;
border: 1px solid var(--line-1);
border-radius: var(--r-8);
background: linear-gradient(180deg, oklch(92% 0.04 220), oklch(82% 0.06 190));
box-shadow: var(--shadow-1);
}
.aspect-media__sky {
position: absolute;
top: 16%;
left: 14%;
width: 3rem;
height: 3rem;
border-radius: 999px;
background: oklch(98% 0.05 86 / 86%);
}
.aspect-media__mountain {
position: absolute;
right: -8%;
bottom: -20%;
width: 72%;
height: 68%;
border-radius: 22% 0 0 0;
transform: rotate(35deg);
transform-origin: bottom right;
}
.aspect-media__mountain--back {
right: 12%;
bottom: -28%;
background: oklch(68% 0.09 205 / 78%);
}
.aspect-media__mountain--front {
background: oklch(48% 0.12 214 / 88%);
}
.aspect-media__caption {
position: absolute;
left: 1rem;
bottom: 1rem;
display: inline-flex;
flex-direction: column;
gap: 0.1rem;
padding: 0.55rem 0.7rem;
border: 1px solid oklch(100% 0 0 / 24%);
border-radius: var(--r-6);
background: oklch(20% 0.04 250 / 72%);
color: white;
backdrop-filter: blur(8px);
}
.aspect-media__caption strong {
font-size: var(--t-13);
line-height: 1.2;
}
.aspect-media__caption span {
font-family: var(--font-mono);
font-size: var(--t-11);
opacity: 0.72;
}
</style> <CfAspectRatio ratio={16 / 9}>
<img src="cover.jpg" alt="封面" />
</CfAspectRatio>
<CfAspectRatio ratio={16 / 9}>
<iframe src="https://www.youtube.com/embed/..." allowFullScreen />
</CfAspectRatio> <CfAspectRatio ratio={16 / 9}>
<img src="cover.jpg" alt="封面" />
</CfAspectRatio>
<CfAspectRatio ratio={16 / 9}>
<iframe src="https://www.youtube.com/embed/..." allowFullScreen />
</CfAspectRatio> API
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ratio | number | 16 / 9 | 宽 / 高比。常用值:21/9 16/9 4/3 1 3/4 9/16 |
反馈与讨论
AspectRatio 宽高比 的讨论