前言
最近,开发的微信小程序接到一个九宫格抽奖的需求,写的过程感觉还蛮有趣的,就写成组件,顺便记录下心理路程。开发前思考了下,大致分成以下几个模块:布局
、动画
以及事件
。技术栈是 uni-app + ts + less。
布局
首先编写template
,渲染格子列表options
以及抽奖按钮raffle
,凸显激活状态:
<template>
<view class="turntable">
<view
class="turntable__item"
:class="[curIdx === idx ? 'turntable__item--active' : null]"
v-for="(item, idx) in options"
:key="idx"
>
索引:{{ idx }}
</view>
<view class="turntable__raffle" @click="raffle">抽奖</view>
</view>
</template>
然后编写 less,由于九宫格奇特的结构,我想到 grid
布局,于是一顿操作猛如虎:
.turntable {
width: 630rpx;
height: 630rpx;
margin: 24rpx auto 76rpx;
padding: 21rpx;
background: rgba(255, 255, 255, 0.3);
display: grid;
grid-template-columns: 184rpx 184rpx 184rpx;
grid-template-rows: 184rpx 184rpx 184rpx;
grid-row-gap: 18rpx;
grid-column-gap: 18rpx;
&__raffle {
background: #ffda44;
grid-area: ~"2 / 2";
}
&__item {
background: #fff;
opacity: 0.3;
&--active {
opacity: 1;
}
}
}
接下来要处理格子的排序问题,实现顺时针
或者逆时针
跳动,这时候需要用到 grid
布局中的 grid-area
属性,这个属性表示在网格布局中指定网格项的大小和位置,比如:
/* 使“item1”从第2行第1列开始,并跨越2行3列: */
.item1 {
grid-area: 2 / 1 / span 2 / span 3;
}
由此,我定义左上角第一个格子为起点,其坐标为(1,1)
,然后如果是顺时针
跳动,则沿着边框依次执行 向右跳
--> 向下跳
--> 向左跳
--> 向上跳
等操作,需要注意边界的判断,于是等到如下的计算属性gridAreas
:
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
type Direction = "up" | "down" | "left" | "right";
type Rotation = "clockwise" | "anticlockwise";
const ROW = 3; // 行数
const COL = 3; // 列数
const CLOCKWISE_DIRECTIONS: Direction[] = ["right", "down", "left", "up"]; // 顺时针转向的路径
const ANTICLOCKWISE_DIRECTIONS: Direction[] = ["down", "right", "up", "left"]; // 逆时针转向的路径
@Component
export default class extends Vue {
/**
* 跳动方向,默认顺时针
*/
@Prop({ default: "clockwise" }) rotation: Rotation = "clockwise";
/**
* 跳动路径
*/
private get directions() {
return this.rotation === "clockwise"
? CLOCKWISE_DIRECTIONS
: ANTICLOCKWISE_DIRECTIONS;
}
/**
* 奖品grid-area值
*/
private get gridAreas() {
let x = 1;
let y = 1;
return this.directions.reduce((pre, item) => {
switch (item) {
case "up":
while (y > 1) {
pre.push(`${y}/${x}`);
y--;
}
y = y < 1 ? 1 : y;
break;
case "down":
while (y < ROW) {
pre.push(`${y}/${x}`);
y++;
}
y = y > COL ? COL : y;
break;
case "left":
while (x > 1) {
pre.push(`${y}/${x}`);
x--;
}
x = x < 1 ? 1 : x;
break;
case "right":
while (x < COL) {
pre.push(`${y}/${x}`);
x++;
}
x = x > ROW ? ROW : x;
break;
}
return pre;
}, [] as string[]);
}
}
为turntable__item
补充style
属性:
<view class="turntable__item" :style="{ gridArea: gridAreas[idx] }" ...></view>
至此,就完成了九宫格的布局。
未完待续...