npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

uni-lattice-lottery

v1.0.0

Published

uni-app cli工具下的九宫格,大转盘,老虎机抽奖组件

Readme

uni-lattice-lottery

uni-app(Vue 3)抽奖组件集合:九宫格 / 列表 / 大转盘 / 老虎机 四种常见样式,开箱即用。

适用于 uni-app CLI / HBuilderX 工程,跨端兼容(H5、微信小程序、支付宝小程序、App 等)。组件全部使用 viewimagetext 等 uni-app 内置元素与 rpx 单位实现,不依赖任何浏览器专属 DOM API。


目录


安装

npm install uni-lattice-lottery

或在 uni-app 项目内直接将 packages/lib 目录复制到工程的 components/uni_modules/ 下。

组件零运行时依赖,dependencies 为空,体积小、引入即用。


快速上手

以九宫格为例:

<template>
  <view class="page">
    <LotteryGrid
      ref="lotteryRef"
      :list="prizeList"
      btn-text="开始抽奖"
      @submit="onSubmit"
      @start="onStart"
      @end="onEnd"
    />
  </view>
</template>

<script>
import LotteryGrid from 'uni-lattice-lottery/lib/LotteryGrid.vue'

export default {
  components: { LotteryGrid },
  data() {
    return {
      prizeList: [
        { id: 1, label: '一等奖', image: '/static/prize1.png' },
        { id: 2, label: '二等奖', image: '/static/prize2.png' },
        { id: 3, label: '三等奖', image: '/static/prize3.png' },
        { id: 4, label: '谢谢参与', image: '/static/prize4.png' },
        { id: 5, label: '优惠券', image: '/static/prize5.png' },
        { id: 6, label: '再来一次', image: '/static/prize6.png' },
        { id: 7, label: '积分 100', image: '/static/prize7.png' },
        { id: 8, label: '神秘大奖', image: '/static/prize8.png' }
      ]
    }
  },
  methods: {
    onSubmit() {
      // 1. 请求服务端获取中奖序号(推荐做法,避免前端伪造)
      const luckyIndex = Math.floor(Math.random() * this.prizeList.length)
      // 2. 调用组件 ref 触发动画
      this.$refs.lotteryRef.go(luckyIndex)
    },
    onStart() {
      console.log('抽奖开始')
    },
    onEnd({ index, prize }) {
      uni.showToast({ title: `恭喜获得:${prize.label}`, icon: 'none' })
    }
  }
}
</script>

核心流程:

  1. 点击按钮 → 组件触发 submit 事件。
  2. 业务侧 → 在 submit 中向服务端请求真实中奖序号。
  3. 调用 ref.go(index) → 组件播放抽奖动画。
  4. 动画结束 → 组件触发 end 事件,回传 { index, prize }

通用约定

奖品列表数据结构

四个组件共享同一份 list 结构(字段全部可选,但建议至少提供 labelimage):

[
  {
    id: 1,           // 业务标识,可选
    label: '一等奖', // 文案
    image: 'xxx.png' // 图片地址
    // 其它自定义字段也会原样回传到 end 事件的 prize 中
  }
]

通用事件

| 事件名 | 触发时机 | 回调参数 | | -------- | ------------------------- | --------------------------------------------------------- | | submit | 用户点击「抽奖」按钮 | 无(Turntable / SlotMachineSlotMachine 无此事件)| | start | 调用 go() 抽奖动画启动 | 无 | | end | 抽奖动画结束 | 单奖品组件:{ index, prize };老虎机:Array<{index, prize}> |

通过 ref 调用方法

所有组件都暴露 go(index) 方法,业务方通过 this.$refs.xxx.go(index) 触发动画:

  • LotteryGrid / LotteryList / Turntablego(index: number),参数为单个中奖序号
  • SlotMachinego(indexes: number[]),参数为长度等于 colCount 的序号数组

动画进行中重复调用 go() 会被忽略并打印 warning,无需业务方加锁。


组件 API

LotteryGrid 九宫格

经典九宫格抽奖:3×3 布局,中间是抽奖按钮,外圈 8 个奖品按顺序高亮。

Props

| 字段 | 类型 | 默认值 | 说明 | | ------------- | ------ | -------- | ------------------------------------------------------------------- | | list | Array | [] | 奖品列表,长度不足 8 会自动补「谢谢参与」,超过 8 会截断到 8 | | btnText | String | '抽奖' | 中央按钮文案 | | circleTimes | Number | 3 | 抽奖前先空转的圈数,越大动画越久 | | velocity | String | 'speed'| 速度模式:'speed' 启停时有缓动;其它任意值表示匀速 |

Events

| 名称 | 参数 | | -------- | ------------------ | | submit | — | | start | — | | end | { index, prize } |

Methods

  • go(index: number):序号从 0 开始,0..7。

示例

<LotteryGrid
  ref="grid"
  :list="prizeList"
  :circle-times="4"
  velocity="speed"
  btn-text="立即抽奖"
  @submit="onSubmit"
  @end="onEnd"
/>

LotteryList 列表抽奖

平铺列表样式,奖品从左到右、从上到下顺序高亮。底部独立按钮。

Props

| 字段 | 类型 | 默认值 | 说明 | | ------------- | ------ | -------- | --------------------- | | list | Array | [] | 奖品列表,长度任意 | | btnText | String | '抽奖' | 按钮文案 | | circleTimes | Number | 3 | 空转圈数 | | velocity | String | 'speed'| 速度模式,同九宫格 |

Events / Methods

LotteryGrid 完全一致。

示例

<LotteryList
  ref="listRef"
  :list="prizeList"
  :circle-times="2"
  @submit="onSubmit"
  @end="onEnd"
/>

Turntable 大转盘

经典圆盘抽奖:使用 CSS transform: rotate() + cubic-bezier 缓动,动画时长固定 6 秒。

转盘背景图与按钮图由业务侧提供,组件只负责旋转与定位。

Props

| 字段 | 类型 | 默认值 | 说明 | | ---------- | ------- | ------ | -------------------------------------------------------------------- | | list | Array | [] | 奖品列表,长度任意;扇区角度 = 360 / list.length | | tableBg | String | '' | 必填,转盘背景图地址 | | tableBtn | String | '' | 必填,中央按钮图地址 | | skew | Boolean | false| 初始角度是否偏半个扇区(适合扇区切割线在 12 点方向的转盘图) |

Events

| 名称 | 参数 | 说明 | | -------- | ------------------ | --------------------- | | submit | — | 点击中央按钮 | | start | — | 旋转开始 | | end | { index, prize } | 6 秒动画结束后触发 |

Methods

  • go(index: number):序号从 0 开始。

示例

<Turntable
  ref="turntableRef"
  :list="prizeList"
  table-bg="/static/turntable-bg.png"
  table-btn="/static/turntable-btn.png"
  :skew="true"
  @submit="onSubmit"
  @end="onEnd"
/>

背景图制作建议:把 N 个扇区均分画在一张正方形图上。组件只负责旋转,最终定位由 360 / list.length 自动计算。如果扇区中线不是从 12 点开始而是切割线从 12 点开始,请把 skew 设为 true


SlotMachine 老虎机

多列滚动老虎机:每列独立旋转,按列依次启动、依次停止。

此组件没有内置按钮,业务方需要在外部放置按钮并调用 ref.go(indexes)

Props

| 字段 | 类型 | 默认值 | 说明 | | ------------- | ------ | ------ | -------------------------------------------------------------------- | | list | Array | [] | 单列奖品池 | | colCount | Number | 3 | 列数 | | concatCount | Number | 4 | 内部列表拼接份数(最少 2),数值越大滚动距离越长,动画感越强 | | moveTime | Number | 4 | 每列动画时长(秒),列与列启动间隔为 0.5 秒 |

Events

| 名称 | 参数 | 说明 | | -------- | ----------------------------- | ---------------------------- | | start | — | 第一列开始滚动 | | end | Array<{ index, prize }> | 所有列停下后触发,长度 = colCount |

注意:老虎机不触发 submit 事件,因为没有内置按钮。

Methods

  • go(indexes: number[]):参数长度必须等于 colCount,每个元素是该列的中奖序号(0..list.length-1)。

示例

<template>
  <view>
    <SlotMachine
      ref="slotRef"
      :list="symbolList"
      :col-count="3"
      :concat-count="6"
      :move-time="3"
      @end="onSlotEnd"
    />
    <button @click="draw">抽奖</button>
  </view>
</template>

<script>
import SlotMachine from 'uni-lattice-lottery/lib/SlotMachine.vue'

export default {
  components: { SlotMachine },
  data() {
    return {
      symbolList: [
        { id: 1, label: '🍒', image: '/static/cherry.png' },
        { id: 2, label: '🍋', image: '/static/lemon.png' },
        { id: 3, label: '🍇', image: '/static/grape.png' },
        { id: 4, label: '⭐', image: '/static/star.png' }
      ]
    }
  },
  methods: {
    draw() {
      // 三列分别落到序号 2、2、2(即三个一样:🍇🍇🍇 中奖)
      this.$refs.slotRef.go([2, 2, 2])
    },
    onSlotEnd(results) {
      const labels = results.map((r) => r.prize.label).join(' ')
      uni.showToast({ title: `结果:${labels}`, icon: 'none' })
    }
  }
}
</script>

LotteryEngine 引擎说明

LotteryGridLotteryList 共享同一个核心引擎 lib/common/lottery-engine.js,负责:

  • 列表预处理(九宫格自动补足 / 截断为 8 项)
  • 动画速度曲线(speed 模式启动加速 / 减速缓动)
  • 帧驱动的 tick(高亮序号迁移)
  • 命中目标后回调 onend

如果需要自行实现样式或与现有 UI 深度结合,可以直接使用引擎:

import LotteryEngine from 'uni-lattice-lottery/lib/common/lottery-engine.js'

const engine = new LotteryEngine({
  type: 'grid',          // 'grid' 或 'list'
  list: prizeList,
  circleTimes: 3,
  velocity: 'speed',
  onstart: () => {},
  ontick: (list, listIndex) => {
    // listIndex:当前高亮的奖品下标
  },
  onend: ({ index, prize }) => {
    // 命中时回调
  }
})

// 触发动画
engine.go(2)

// 动态更新奖品池
engine.updateList(newList)

// 销毁(组件 unmount 时调用)
engine.dispose()

工具函数

lib/common/utils.js 提供以下导出:

| 函数 | 说明 | | ----------------- | ------------------------------------------ | | getRandom(a, b) | 返回 [a, b] 之间的整数 | | getCode() | 生成 6 位字母数字混合短码(用作实例命名) | | isArray(v) | 数组判断 | | padGridList(l) | 把列表填充 / 截断到 8 项 | | isValidIndex(i, len) | 校验下标是否落在 [0, len) 内 |


常见问题

1. 中奖序号应该由谁决定?

强烈建议由服务端决定。前端只负责动画展示。常见时序:

点击按钮 → @submit 事件 → 调用后端接口 → 拿到 luckyIndex → ref.go(luckyIndex) → @end 显示结果

如果中奖序号在前端硬编码或随机生成,用户可以通过抓包 / 调试改写概率。

2. 为什么动画进行中再点没反应?

组件内部维护 isMoving / drawing / going 状态,动画期间忽略重复调用并打印 warning,是有意行为,避免动画错乱。如需自定义按钮禁用样式,可以监听 start / end 事件自行控制。

3. 九宫格奖品不到 8 个 / 超过 8 个怎么办?

组件内部使用 padGridList

  • 不足 8 个 → 自动追加 { label: '谢谢参与' }
  • 超过 8 个 → 自动截断到前 8 项

4. 大转盘最终落在哪个角度?

组件根据 360 / list.length 计算单扇区角度,每次至少旋转 6 圈再加上目标偏移;动画时长固定 6 秒。如发现最终落点和预期不符,多半是 背景图扇区与序号方向不对应:默认序号 0 是从 12 点位置 顺时针 第一个扇区。把 skew 设为 true 可以补偿"切割线在 12 点"的背景图。

5. 老虎机能否每列用不同的奖品池?

当前版本所有列共享同一个 list。如果需要每列不同的奖品池,可以在外层包一层组件分别渲染多个 SlotMachine(每个 colCount=1)并各自调用 go([index])

6. 跨端兼容性

| 端 | 状态 | | ------------- | -------- | | H5 | ✅ | | 微信小程序 | ✅ | | 支付宝小程序 | ✅ | | 抖音小程序 | ✅ | | App(Vue) | ✅ |

组件未使用 document / window 等浏览器专属 API,CSS 动画基于 transform,全端可用。


License

Apache-2.0