CSS 多按钮根据半圆弧度排列

需求

多个按钮根据弧度,延边均匀排列。

实现

HTML

  • 分两级;
  • 第一级,外层定义按钮的 compose-container 宽度;
  • 第二级,按钮集合,使用方法 styleBtn(index),根据索引计算;
html 复制代码
<div class="compose-container flex-style-01">
    <div class="btn" v-for="(item, index) in btnNum" :key="index" :style="styleBtn(index)">
    	{{ index }}
    </div>
</div>

CSS

  • compose-container:容器样式,使用 flex 布局,且设置 width
  • btn:按钮样式,无需设置 display
scss 复制代码
.compose-container {
    display: flex;
    width: 600px;
    height: 80px;
    margin: 50px 0;
    background: #409eff;
    .btn {
        width: 50px;
        height: 50px;
        line-height: 50px;
        background: #aec0d1;
        border-radius: 50%;
        text-align: center;
        vertical-align: middle;
    }
}

JavaScript

  • 如下方法是计算按钮 translateY 值;
  • 代码第 2 行,传参是按钮的索引值,从 0 开始;
  • 代码第 4 行,按钮的总个数;
  • 代码第 6 行,按钮垂直高度间隔,单位 px
  • 代码第 8 行,整体抬高,也可不设置;
  • 代码第 12 - 27 行,区分偶数和奇数,中心点计算;
  • 代码第 29 行,设置属性 translateY
js 复制代码
// 按钮弧度高度
function styleBtn(index) {
    // 总个数
    const totalNum = 8;
    // 间隔高度,单位 px
    const gap = 8;
    // 整体抬高
    const raiseHeight = 28;

    let translateY = ''

    if(totalNum%2 === 0) { // 偶数
        const centerIdx = totalNum / 2;
        const n = centerIdx - index;
        // > 0 左侧,<= 右侧
        translateY = (n > 0) ? n * gap : (Math.abs(n)+1) * gap;
    }
    else { // 奇数
        const centerIdx = Math.floor(totalNum / 2);
        const n = centerIdx - index;
        if(centerIdx === index) {// 中心点
            translateY = gap;
        }
        else {
            translateY = (Math.abs(n)+1) * gap;
        }
    }
    
    return { transform: `translateY(${(translateY - raiseHeight)}px)` }
}

效果

偶数

  • 按钮个数为偶数,中间两个按钮在同一条水平线上;
  • 样式一,justify-content: center,根据按钮的 margin 属性设置间隔;
  • 样式二:justify-content: space-between,两端对齐,按钮之间的空间平均分配;
  • 样式三:justify-content: space-around,按钮两侧空间相等,但首尾按钮与容器边缘的空间是按钮之间空间的一半;
  • 样式四:justify-content: space-evenly,所有间距(包括首尾按钮与容器边缘的间距)都相等;

奇数

  • 按钮个数为奇数,中间一个按钮最高,其它左右两侧在同一条水平线上;
  • justify-content 设置同上;

完整代码

vue 复制代码
<template>
    <div>
        <h4>样式一:justify-content: center, 且设置btn margin </h4>
        <div class="compose-container flex-style-01">
            <div class="btn" v-for="(item, index) in btnNum" :key="index" :style="styleBtn(index)">
                {{ index }}
            </div>
        </div>

        <h4>样式二:justify-content: space-between</h4>
        <div class="compose-container flex-style-02">
            <div class="btn" v-for="(item, index) in btnNum" :key="index" :style="styleBtn(index)">
                {{ index }}
            </div>
        </div>

        <h4>样式三:justify-content: space-around</h4>
        <div class="compose-container flex-style-03">
            <div class="btn" v-for="(item, index) in btnNum" :key="index" :style="styleBtn(index)">
                {{ index }}
            </div>
        </div>

        <h4>样式四:justify-content: space-evenly</h4>
        <div class="compose-container flex-style-04">
            <div class="btn" v-for="(item, index) in btnNum" :key="index" :style="styleBtn(index)">
                {{ index }}
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';

const btnNum = ref(7);

// 按钮弧度高度
function styleBtn(index) {
    // 总个数
    const totalNum = btnNum.value;
    // 间隔高度,单位 px
    const gap = 8;
    // 整体抬高
    const raiseHeight = 28;

    let translateY = ''

    if(totalNum%2 === 0) { // 偶数
        const centerIdx = totalNum / 2;
        const n = centerIdx - index;
        // > 0 左侧,<= 右侧
        translateY = (n > 0) ? n * gap : (Math.abs(n)+1) * gap;
    }
    else { // 奇数
        const centerIdx = Math.floor(totalNum / 2);
        const n = centerIdx - index;
        if(centerIdx === index) {// 中心点
            translateY = gap;
        }
        else {
            translateY = (Math.abs(n)+1) * gap;
        }
    }
    
    return { transform: `translateY(${(translateY - raiseHeight)}px)` }
}
</script>

<style lang="scss" scoped>
.compose-container {
    display: flex;
    width: 600px;
    height: 80px;
    margin: 50px 0;
    background: #409eff;
    .btn {
        width: 50px;
        height: 50px;
        line-height: 50px;
        background: #aec0d1;
        border-radius: 50%;
        text-align: center;
        vertical-align: middle;
    }
}
.flex-style-01 {
    justify-content: center;
    .btn {
        margin: 0 6px;
    }
}
.flex-style-02 {
    justify-content: space-between;
}
.flex-style-03 {
    justify-content: space-around;
}
.flex-style-04 {
    justify-content: space-evenly;
}
</style>
相关推荐
ℋᙚᵐⁱᒻᵉ鲸落1 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙2 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
前端snow3 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上4 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈4 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香4 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest4 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士6 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见6 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen6 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试