简简单单写个九宫格抽奖组件(一)

2,615 阅读2分钟

前言

最近,开发的微信小程序接到一个九宫格抽奖的需求,写的过程感觉还蛮有趣的,就写成组件,顺便记录下心理路程。开发前思考了下,大致分成以下几个模块:布局动画以及事件。技术栈是 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>

至此,就完成了九宫格的布局。

未完待续...

参考文献

www.jc2182.com/css/css-gri…