文章目录
- 1. 实现效果
- 2. 签到设置7天布局
- 2.1 实现代码
- 3 签到设置15天布局
- 3.1 思路分享
- 4 完整demo代码
- 5. 总结
1. 实现效果
实现一个签到活动的h5页面布局,需求如下
- 签到活动天数可配置,可配置7天,15天,30天等默认天数
- 要求展示2行进行展示,天数过多的时候进行横向滚动展示
- 要求默认第一版页面,首屏展示4个
- 要求比如是最后一个签到日期,最后一天签到日期的盒子默认占据两行进行展示
2. 签到设置7天布局
1.设置签到日期一共为7天,第一排展示4个,第二排展示3个,最后一个占两列布局
这个比较简单
2.1 实现代码
<div class="sign"><divv-for="(item, index) in arrLength":class="['item',index + 1 == arrLength && 'large']"><div class="day">第 {{ index + 1 }} 天</div></div>
</div>
<script setup lang="ts">
const arrLength = ref(7);
</script>
.sign {display: grid;grid-template-columns: repeat(4, 1fr);grid-gap: 8px;.item {/* 最后一个占两行 */&.large {grid-column: span 2;}}
}
3 签到设置15天布局
问题:当我们设置天数为15天时候,grid布局因为自适应问题,导致并没有横向滚动
const arrLength = ref(15);
3.1 思路分享
想要两排布局,超出横向滚动,其实就是需要动态计算 grid-template-columns
的值
比如是7天,那么 grid-template-columns
的值就应该是 repeat(4, 1fr)
比如是15天,那么 grid-template-columns
的值就应该是 repeat(8, 1fr)
so,通过一个 gridColumns
去计算值根据 Math.ceil(arrLength.value / 2)
获取
<script setup lang="ts">
const arrLength = ref(15);
const gridColumns = ref("");onMounted(async () => {// 设置一排分为多少列// Math.ceil([15/2]) = 8 结果,就是每列8个 repeat(8, 1fr)gridColumns.value = `repeat(${Math.ceil(arrLength.value / 2)}, 1fr)`;
});
</script>
<div class="sign" :style="{ gridTemplateColumns: gridColumns }">
</div>
好,在看看效果
嗯,虽然设置了两排布局了,但是并没用滚动,grid布局因为自适应问题,导致并没有横向滚动
,所以必须也控制每个盒子的宽度
每个盒子的宽度 = 容器的宽度 / 4
so,继续撸代码,style 动态设置每个盒子的宽度
<div:style="{width:arrLength == index + 1 &&arrLength % 2 == 1? `${(signContainerWidth / 4 - 16) * 2 + 8}px`: `${signContainerWidth / 4 - 16}px`}"
><div class="day">第 {{ index + 1 }} 天</div>
</div>
<script setup lang="ts">
const signContainerRef = ref<DIVElement>(null);
const signContainerWidth = ref(0);onMounted(async () => {// 获取容器宽度signContainerWidth.value = signContainerRef.value? signContainerRef.value.offsetWidth: 0;
});
</script>
好,在看看效果
,终于可以了
4 完整demo代码
<template><div class="body"><div class="sign-container" ref="signContainerRef"><div class="title">签到功能</div><div class="sign" :style="{ gridTemplateColumns: gridColumns }"><divv-for="(item, index) in arrLength":class="['item',index + 1 == arrLength && arrLength % 2 == 1 && 'large']":style="{width:arrLength == index + 1 &&arrLength % 2 == 1? `${(signContainerWidth / 4 - 16) * 2 + 8}px`: `${signContainerWidth / 4 - 16}px`}"><div class="day">第 {{ index + 1 }} 天</div></div></div></div></div>
</template><script setup lang="ts">
const signContainerRef = ref<DIVElement>(null);
const arrLength = ref(15);
const signContainerWidth = ref(0);
const gridColumns = ref("");onMounted(async () => {// 设置一排分为多少列// Math.ceil([15/2]) = 8 结果,就是每列8个 repeat(8, 1fr)gridColumns.value = `repeat(${Math.ceil(arrLength.value / 2)}, 1fr)`;// 获取宽度signContainerWidth.value = signContainerRef.value? signContainerRef.value.offsetWidth: 0;
});
</script><style lang="scss" scoped>
.body {background-size: 100% 100%;min-height: 100vh;background: #7c8ac3;position: relative;.title {font-weight: bold;font-size: 24px;text-align: center;margin: 10px auto;}.sign-container {width: 100vw;position: absolute;top: 50%;transform: translateY(-50%);padding: 12px;z-index: 2;.sign {margin-top: 12px;background: #191a1e;border-radius: 4px;padding: 8px;display: grid;grid-template-columns: repeat(4, 1fr);grid-gap: 8px;overflow-x: scroll;.item {height: 113px;background: #876868;border-radius: 5px;position: relative;.day {font-size: 12px;font-family: PingFang SC, PingFang SC-Regular;text-align: center;color: #fff;line-height: 113px;margin-top: 8px;}/* 最后一个占两行 */&.large {grid-column: span 2;}}}}
}
</style>
5. 总结
虽然这个功能看上去比较简单,但是着仅仅是一个简单的demo,还要很多细节处理判断,比如
- 当天已签到时,展示已签到图标
- 当天未签到时,金币添加光圈旋转并且左右晃动动画
- 签到天数为8天时候,进去后默认横向滚动到第8天动画实现