CSS 圓形與橢圓形排版
- February 17, 2023
最近遇到了斜橢圓形 CSS 排版的需求,查詢網路上的文章,做圓形排版或繞圓動畫主要都是用 3D 的方式進行,但我的需求其實是 2D 的,設定的目標是把圓畫出來後,可以很輕易的將物體對圓繞著轉,花了一些時間思考跟測試,最後有了比較簡易的寫法,因此用此邊文章記錄下來。
以下會分別介紹圓形排版、橢圓形排版,以及排版後如何使圓上的物體繞著圓轉做動畫。
圓形排版
圓形排版非常簡單,只需要用 rotate
搭配 translate
即可實現,最後再用 rotate
將內容轉回來
為方便示範將所有內容寫在一起,於開發環境請自行切 component 並以 computed 計算.
1
<template>
<div class="circle">
<label for="count">
輸入個數:
<input
id="count"
min="1"
type="number"
place="輸入個數"
v-model="count"
/>
</label>
<label for="deg">
輸入起始角度:
<input
id="deg"
type="number"
step="30"
place="起始位置"
v-model="startDeg"
/>
</label>
<div class="example-circle">
<div
v-for="i in count"
:key="i"
class="example-circle__item"
:style="getStyle(i - 1)"
>
{{ i }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const count = ref(1);
const startDeg = ref(-90);
const getStyle = (index: number) => {
const calcDeg = startDeg.value + (index / count.value) * 360;
return {
transform: `
rotate(${calcDeg}deg)
translate(10rem)
rotate(${calcDeg * -1}deg)
`,
};
};
</script>
<style lang="scss" scoped>
input {
border: 1px solid;
border-radius: 4px;
padding: 4px;
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 24px;
}
.example-circle {
width: 20rem;
height: 20rem;
border: 1px solid;
border-radius: 50%;
margin: 0 auto;
position: relative;
&__item {
border: 1px solid;
width: 4rem;
height: 4rem;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
// for text
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
橢圓形排版
橢圓形排版稍微複雜一點,其實就是將圓形壓扁( scaleY(0.5)
),再把元素拉回來( scaleY(2)
),這個倍率可自行調整,壓扁 N (0.5)
倍 就要把元素拉回來 1/N (1/0.5=2)
倍。
1
<template>
<div class="ellipse">
<label for="count">
輸入個數:
<input
id="count"
min="1"
type="number"
place="輸入個數"
v-model="count"
/>
</label>
<label for="deg">
輸入起始角度:
<input
id="deg"
type="number"
step="30"
place="起始位置"
v-model="startDeg"
/>
</label>
<div class="example-ellipse">
<div
v-for="i in count"
:key="i"
class="example-ellipse__item"
:style="getStyle(i - 1)"
>
{{ i }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const count = ref(1);
const startDeg = ref(-90);
const getStyle = (index: number) => {
const calcDeg = startDeg.value + (index / count.value) * 360;
return {
transform: `
rotate(${calcDeg}deg)
translate(10rem)
rotate(${calcDeg * -1}deg)
scaleY(2)
`,
};
};
</script>
<style lang="scss" scoped>
input {
border: 1px solid;
border-radius: 4px;
padding: 4px;
margin-bottom: 20px;
}
label {
display: block;
}
.example-ellipse {
width: 20rem;
height: 20rem;
border: 1px solid;
border-radius: 50%;
margin: 0 auto;
position: relative;
transform: scaleY(0.5);
&__item {
border: 1px solid;
width: 4rem;
height: 4rem;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
// for text
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
動畫
這種排法有個好處是當你遇到一個很怪的橢圓形,你只需要對好橢圓形的形狀,靠著修改 rotate
就能確保他繞著圓上跑。
1
<template>
<div class="animation-ellipse">
<div class="animation-ellipse__item">1</div>
</div>
</template>
<script setup lang="ts"></script>
<style lang="scss" scope>
.animation-ellipse {
width: 20rem;
height: 20rem;
border: 1px solid;
border-radius: 50%;
margin: 0 auto;
position: relative;
transform: scaleY(0.5) skewY(35deg);
&__item {
border: 1px solid;
width: 4rem;
height: 4rem;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
transform: skewY(-35deg) rotate(-90deg) translate(10rem) rotate(90deg)
scaleY(2);
animation: 3s round linear infinite;
// for text
display: flex;
justify-content: center;
align-items: center;
}
@keyframes round {
from {
transform: rotate(-90deg) translate(10rem) rotate(90deg) skewY(-35deg)
scaleY(2);
}
to {
transform: rotate(270deg) translate(10rem) rotate(-270deg) skewY(-35deg)
scaleY(2);
}
}
}
</style>