注册

uniapp适配android、ios的引导页、首页布局

uniapp适配Android、Ios的引导页和首页布局



真是很久没来掘金写文章了,最近一直在学习Nest和Next这些后端知识,忙只是一方面,更多的还是懒吧。其实今年来北京工作之后,完全独立挑大梁来写app收获还是蛮多的,但是我一般忙完就完事,着急学习自己的东西去,没有把工作中遇到的一些问题及时总结。这点感觉很不好,以后尽量把工作中遇到的有价值的问题总结下来,也算是给自己这段时间工作的复习,也能锻炼自己的表达能力。



引导页


原型图和需求


微信截图_20240722143529.png



需求大致是这样:一共有三页,每页有2-3组图片,产品想要炫酷的视觉效果



我接收到需求后,首先想的是gif图,于是让UI帮我做了一张12帧的gif,大家来感受一下效果


01.gif



不知道大家感受怎么样,放到手机来模拟的时候有些模糊、有些卡顿,且占用空间很大,一张12帧的图片已经20M+,
整个应用不过才30M的情况下,绝对接受不了这种情况,于是我就放弃的gif,想要用代码来实现。



思路


留给我的开发时间并不多,只有半天,自己本身css能力一般,按照gif这样估计最多做出来一页,所以我和产品决定阉割掉一部分动效,做三页。



  • UI负责把每条图片列表切图给我
  • 引导页用swiper实现,这样页面切换动画可以省时间
  • 第一页水平做动画两两一组,交替实现动画
  • 第二页垂直做动画,交替实现
  • 第三页原图和AI图在一个父盒子下,原图动态改变宽度来实现交替播放
  • 每页文字和按钮通过position:fixed置底
  • 最后一页手动加上滑动事件,可以不点击按钮进入首页

代码实现



  • template布局

<view class="swiperLayout">
<swiper
:current="current"
class="swiper"
duration="350"
@change="change"
:indicator-active-color=" '#FFF272' "
:indicator-color="'#ccc'"
indicator-dots="true"
>

<swiper-item class="swiperItem">
<view class="itemLayout">
<image
class="img an1"
src="@/static/guide/guide1_1.png"
mode="scaleToFill"
/>

<image
class="img an2"
src="@/static/guide/guide1_2.png"
mode="scaleToFill"
/>

<image
class="img an1"
src="@/static/guide/guide1_3.png"
mode="scaleToFill"
/>

<image
class="img an2"
src="@/static/guide/guide1_4.png"
mode="scaleToFill"
/>

<view class="buttonBox">
<view class="title">海量模板</view>
<view class="button" @click="next(1)">下一步</view>
</view>
</view>
</swiper-item>
<swiper-item class="swiperItem">
<view class="itemLayout">
<view class="guide2Box">
<image
class="img2 an3"
src="@/static/guide/guide2_1.png"
mode="scaleToFill"
/>

<image
class="img2 an4"
src="@/static/guide/guide2_2.png"
mode="scaleToFill"
/>

<image
class="img2 an3"
src="@/static/guide/guide2_3.png"
mode="scaleToFill"
/>

</view>
<view class="buttonBox">
<view class="title">5000+云端照片存储</view>
<view class="button" @click="next(2)">下一步</view>
</view>
</view>
</swiper-item>
<swiper-item
class="swiperItem"
@touchstart="handlerStart($event)"
@touchmove="handerMove($event)"
>

<view class="itemLayout">
<view class="guide3">
<-- img3动态改变自己的宽度,来实现动画效果 -->
<image
class="img3 an5 z"
src="@/static/guide/guide3_1.png"
mode="aspectFill"
/>

<image
class="img4 "
src="@/static/guide/guide3_2.png"
mode="heightFix"
/>

</view>
<view class="buttonBox">
<view class="title">高清照片,无水印无广告</view>
<view class="button" @click="toIndex">继续</view>
</view>
</view>
</swiper-item>
</swiper>
</view>


  • css部分

.swiper {
width: 100vw;
height: 100vh;
background: #000;
.swiperItem {
.itemLayout {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 60rpx;
.img {
width: 220vw;
height: 35vw;
margin: 20rpx 0 0rpx 0;
}
.img2 {
width: 30vw;
height: 256vw;
}
.title {
color: $themeColor;
margin-top: 40rpx;
text-align: center;
font-size: 36rpx;
font-weight: 600;
margin-bottom: 40rpx;
}
.button {
background: $themeColor;
color: #000;
height: 88rpx;
line-height: 88rpx;
width: 88%;
text-align: center;
border-radius: 48rpx;
font-size: 32rpx;
font-weight: 600;
}
}
.guide2Box{
display: flex;
justify-content: space-evenly;
width: 100%;
overflow: hidden;
height: 70vh;
}
}
}

// 动画1 执行三秒 匀速 无限次 镜像执行
.an1 {
animation: guide1 3s linear infinite alternate-reverse ;
}

// 水平X轴正向
@keyframes guide1 {
from {
transform: translateX(0);
}
50% {
transform: translateX(200rpx);
}
to {
transform: translateX(400rpx);
}
}

.an2 {
animation: guide2 3s linear infinite alternate-reverse ;
}
// 水平X轴负向
@keyframes guide2 {
from {
transform: translateX(0);
}
50% {
transform: translateX(-200rpx);
}
to {
transform: translateX(-400rpx);
}
}

.an3 {
animation: guide3 3s linear infinite alternate-reverse ;
}
// 水平正向 但是起始点要给负数 不然会有空缺的部分
@keyframes guide3 {
from {
transform: translateY(-500rpx);
}
50% {
transform: translateY(-250rpx);
}
to {
transform: translateY(0rpx);
}
}

.an4 {
animation: guide4 3s linear infinite alternate-reverse ;
}
// 水平负向
@keyframes guide4 {
from {
transform: translateY(0);
}
50% {
transform: translateY(-250rpx);
}
to {
transform: translateY(-500rpx);
}
}
.buttonBox{
position: fixed;
bottom: 120rpx;
width: 80vw;
display: flex;
flex-direction: column;
align-items: center;
z-index: 999;
}
// 最后一页动画 父盒子开启相对定位
.guide3{
position: relative;
width: 100%;
height: 100%;
// 两张图片都开始绝对定位 一左一右分布
.img3{
position: absolute;
top: 0;
left: 0;
height: 147vw;
border-right: 12rpx solid #fff;
}
.img4{
position: absolute;
top: 0;
right: 0;
height: 147vw;
}
}
// img3 缩小自己的宽度来实现动画
.an5 {
animation: changeImg 2s linear infinite alternate-reverse;
}
@keyframes changeImg {
from {
width: 0%;
}
to {
width: 100%;
}
}

.z{
z-index: 99;
}


  • js部分

data() {
return {
current: 0,
// 触摸事件用到的数据
touchInfo: {
touchX: "",
touchY: "",
},
};
},
methods: {
next(num) {
this.current = num;
},
change(e) {
this.current = e.detail.current;
},
toIndex() {
uni.switchTab({ url: "/pages/index/index" });
},
handlerStart(e) {
let { clientX, clientY } = e.changedTouches[0];
this.touchInfo.touchX = clientX;
this.touchInfo.touchY = clientY;
},
handerMove(e) {
let { clientX, clientY } = e.changedTouches[0];
let diffX = clientX - this.touchInfo.touchX,
diffY = clientY - this.touchInfo.touchY,
absDiffX = Math.abs(diffX),
absDiffY = Math.abs(diffY),
type = "";
if (absDiffX > 50 && absDiffX > absDiffY) {
type = diffX >= 0 ? "right" : "left";
}
if (absDiffY > 50 && absDiffX < absDiffY) {
type = diffY < 0 ? "up" : "down";
}
if(type === 'left'){
this.toIndex()
}
},
},

最终效果


动画2.gif


首页布局


原型图和需求



  • 画风

微信截图_20240722144340.png



  • 贴纸

微信截图_20240722143558.png



  • 换脸

微信截图_20240722144351.png



上面三图均为UI设计。首页的模板接口截止到目前(7.22)一共三种类型:styler(画风)、sticker(贴纸)、face_swap(换脸),本来按照UI的设计来看,每个分类的样式应该是固定写死的,我只需要v-for去不同的组件就可以,正当我写了一半时,很快老板的需求又下来:每个分类可能会杂糅在一起。说白了就是某个分类里可能既有画风、又有换脸、又有贴纸



思路



  • 分析需求


在一个父组件中渲染所有的数据,根据不同的type 进入不同的子组件,三个子组件分别对应画风、贴纸、换脸,其中贴纸数据中有一个mode字段,根据mode展示轮播、九宫格、一大八小的布局,这其中一大八小最不好实现。



一大八小的布局



  • 将数据中的九张模板图片进行分组(剔除第一张,因为第一张要做“一大”),分为两组布局是上下分布(display:flex)实现,同时将第一张和分组的view盒子的父元素也要开启display:flex
  • 编译到chrome调试 看html结构

Snipaste_2024-07-22_15-25-39.png



  • 代码

 <scroll-view class="scroll_view" scroll-x="true">
<image
class="img"
:src="sceneItem.json_content.cover_image_list[0].path"
mode="scaleToFill"
/>

<view>
<view
class="Item_2"
v-for="(Item, index) in columnData"
:key="index"
>

<view v-for="item in Item" :key="item.id">
<image
class="ss"
:src="item.path"
mode="scaleToFill"
/>

</view>
</view>
</view>

</scroll-view>
...
computed:{
columnData() {
if (this.sceneItem.json_content.display_mode === "2") {
const setData = this.sceneItem.json_content.cover_image_list.filter(
(item, index) => index > 0
);
const resultArray = setData.reduce(
(acc, cur, index) => {
const targetIndex = index % 2;
acc[targetIndex].push(cur);
return acc;
},
Array.from(Array(2), () => [])
);
return resultArray;
}
},
}
...
::v-deep .uni-scroll-view-content {
display: flex;
}
.scroll_view {
white-space: nowrap;
.img {
min-width: 324rpx;
height: 324rpx;
border-radius: 24rpx;
margin-right: 24rpx;
}
.Item {
display: inline-block;
.img {
width: 324rpx;
height: 324rpx;
border-radius: 24rpx;
margin-right: 24rpx;
}
}
.Item_2 {
display: flex;
.ss {
width: 158rpx;
height: 158rpx;
margin-right: 12rpx;
border-radius: 16rpx;
}
}
}

实现效果


动画3.gif


作者:你听得到11
来源:juejin.cn/post/7394005582774960182

0 个评论

要回复文章请先登录注册