更新记录

1.0.21(2026-09-30) 下载此版本

  1. 新增 button-rotate-x / button-rotate-y:中心按钮主体图片可自定义转动中心的上下左右位置(相对图片中心偏移)
  2. 取值支持两种写法:|值| ≤ 1 视为占图片最长边的比例(图片会缩放到按钮尺寸,用比例可保持对准),其它按 px / rpx
  3. 轴心偏移只影响旋转轨迹,不改变图片静止位置;默认 0 时与之前行为完全一致(绕图片中心转),因此对已有用法无影响
  4. 适用场景:图片里的圆心不在图片几何中心时(如"圆在下方 + 向上箭头"的竖长图),把轴心对准圆心,抽奖时圆才是原地自转而不是绕图片中心画圈
  5. 示例页改用新增的竖长示例图 static/btn-long.png(128×192),并用 button-rotate-y="0.1875" 演示轴心对准圆心

平台兼容性

uni-app(4.87)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
√ √ √ √ √ √ - √ - - - -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × √ ×

fg-lucky-wheel 抽奖大转盘

组件名:fg-lucky-wheel

基于 Canvas 2D 绘制的抽奖大转盘组件,适用于营销抽奖、签到抽奖、积分兑换、盲盒抽奖等场景。

  • 一套代码全端运行:App(app-vue)、H5(含 PC 浏览器)、微信/支付宝/百度/字节/QQ/快手等小程序
  • 无层级问题:盘面、指针、中心按钮(含箭头/图片/文字)全部绘制在 canvas 内部,不依赖 DOM 叠加,各端层级表现绝对一致
  • 样式高度可定制:扇区配色(纯色 / 径向渐变)、文字颜色字号、分隔线、外圈、灯珠、指针、中心按钮均可配置
  • 背景可定制:盘面底色、盘面背景图(可设置透明度、是否跟随旋转)、组件容器背景
  • 奖品数量自适应:4 / 6 / 8 / 10 / 12 个奖项自动均分扇区,文字自动折行省略号
  • 文字排布可切换:默认横向排布(文字绕圆心旋转到与所在扇区顶边平行,按弦宽自动折行),也可 text-layout="radial" 沿半径排布;字号、颜色支持组件级与奖项级分别设置,奖品图排在文字下方且朝向与文字一致
  • 两种动画方式:rotate-mode="wheel" 转盘转动 + 外部指针指示;rotate-mode="button" 转盘静止 + 中心按钮内箭头旋转指向
  • 抽奖逻辑灵活:支持外部决定结果(start(index))或组件内按权重随机(random() / autoRandom)
  • easycom 自动注册:插件放入 uni_modules 后直接在页面使用 <fg-lucky-wheel> 标签

平台兼容性

平台 支持情况 说明
App(app-vue) ✅ HBuilderX 3.x,uni-app 编译模式
H5(浏览器 / 微信内置浏览器) ✅ 现代浏览器均可
微信小程序 ✅ 基础库 >= 2.9.0(Canvas 2D、canvas 同层渲染)
支付宝小程序 ✅ 基础库支持 Canvas 2D 即可
百度 / 字节 / QQ / 快手 等小程序 ✅ 基于 uni-app Canvas 2D 封装,随平台基础库支持
App-nvue ❌ nvue 不支持 Canvas 2D,请在 vue 页面使用

组件用 uni.createSelectorQuery().fields({ node: true }) 获取 Canvas 2D 节点,并用 canvas.requestAnimationFrame 逐帧重绘盘面(无需依赖 CSS transform 旋转原生组件),因此各端表现一致。初始化失败会通过 error 事件抛出并在控制台提示。

快速开始

插件放入 uni_modules 目录后(本项目已内置),页面可直接使用,无需 import:

基础用法(外部决定抽奖结果,推荐对接后端接口)

<template>
    <view class="page">
        <fg-lucky-wheel ref="wheel" :list="prizeList" :size="320" @wheel-click="onClick" @end="onEnd" />
    </view>
</template>

<script>
export default {
    data() {
        return {
            prizeList: [
                { id: 'p1', label: '一等奖 iPhone', weight: 1 },
                { id: 'p2', label: '二等奖 蓝牙耳机', weight: 5 },
                { id: 'p3', label: '谢谢参与', weight: 40 },
                { id: 'p4', label: '三等奖 保温杯', weight: 10 },
                { id: 'p5', label: '50 元优惠券', weight: 20 },
                { id: 'p6', label: '再来一次', weight: 24 }
            ]
        }
    },
    methods: {
        // 点击转盘中心按钮(组件不直接转动,等接口返回结果后再调用 start)
        onClick() {
            uni.showLoading({ title: '抽奖中' })
            uni.request({
                url: 'https://your-api.com/lottery/draw',
                method: 'POST',
                success: (res) => {
                    uni.hideLoading()
                    // 后端返回中奖的奖项 id(也可返回下标 index)
                    this.$refs.wheel.start(res.data.prizeId)
                },
                fail: () => uni.hideLoading()
            })
        },
        onEnd(payload) {
            // payload = { index, item }
            uni.showToast({ title: '抽中:' + payload.item.label, icon: 'none' })
        }
    }
}
</script>

组件内按权重随机(无需接口,适合演示 / 纯前端抽奖)

<fg-lucky-wheel ref="wheel" :list="prizeList" auto-random @end="onEnd" />

auto-random 为 true 时,点击中心按钮即由组件按 weight 权重随机并开始旋转;也可在任意时机手动调用 this.$refs.wheel.random()。

自定义样式与背景

<fg-lucky-wheel
    ref="wheel"
    :list="prizeList"
    :size="340"
    :colors="['#FF6B6B', '#FFA36B', '#FFD166', '#7BD389', '#5AC8FA', '#8E7CFF']"
    text-color="#FFFFFF"
    font-size="15"
    bg-color="#1B1436"
    bg-image="/static/wheel-bg.png"
    :bg-image-opacity="0.35"
    :bg-image-rotate="false"
    background="radial-gradient(circle at 50% 45%, #FFF3D0 0%, #FFE1A6 60%, #F7C86A 100%)"
    border-color="#7C5CFF"
    :border-width="14"
    divider-color="rgba(255,255,255,0.35)"
    pointer-color="#FFD166"
    button-color="#FFD166"
    button-text-color="#3A2E6E"
    button-text="GO"
    :beads="20"
    bead-color="#FFF3C8"
    bead-active-color="#FFB03A"
/>

两种动画方式

<!-- 方式一(默认):转盘整体转动,盘面上方的指针指示中奖扇区 -->
<fg-lucky-wheel ref="wheel" :list="prizeList" rotate-mode="wheel" />

<!-- 方式二:转盘静止,中心按钮内的箭头旋转指向中奖扇区,外部指针自动隐藏 -->
<fg-lucky-wheel
    ref="wheel"
    :list="prizeList"
    rotate-mode="button"
    button-color="#1B1D46"
    :button-image="arrowImage"
/>
  • rotate-mode="wheel":指针由 pointer* 系列属性控制 —— 颜色 / 尺寸 / 位置 / 图片,以及 pointer-inset(箭头与外圈的距离)。
  • pointer-inset 的取值规则:
    • 0 → 箭头紧贴外圈;
    • 0 < v < 1 → 视为占转盘直径的比例(如 0.08 即直径的 8%);
    • v >= 1 → 视为 px(如 24);
    • 字符串 → 支持带单位(如 '48rpx'、'20px'),rpx 会按屏幕自适应换算。
  • rotate-mode="button":中心按钮作为指针使用,按钮内会显示箭头(未设置 button-image 时自动绘制默认箭头),箭头默认朝上,旋转后指向中奖扇区;此时按钮文字不再显示,外部指针自动隐藏。
  • button-image 请自备箭头朝上的图片(方形或带指针的图片均可,组件会绕按钮中心旋转它)。

指定奖项 / 无中间过程直接停

// 转到下标 2 的奖项(3 秒、至少 3 圈)
this.$refs.wheel.start(2, { duration: 3000, turns: 3 })

// 按权重随机
this.$refs.wheel.random()

// 立即结束动画并停在目标奖项
this.$refs.wheel.stop()

// 回到初始角度
this.$refs.wheel.reset()

Props

数据与尺寸

属性 类型 默认值 说明
list Array [] 奖项列表。元素支持 String / Number / 对象:{ id, label, color, textColor, image, weight, disabled }
size Number/String 320 转盘直径。Number 视为 px,字符串支持 '600rpx' / '330px'

奖项对象字段:

  • id:奖项唯一标识,start('p1') 可直接按下标或 id 定位
  • label(别名 name / text):奖项文案,超长自动折行,默认最多 2 行、超出省略号
  • color:该扇区填充色,优先级高于 colors;传数组(如 ['#FF6B6B', '#FFD166'])时按径向渐变绘制
  • textColor:该奖项文字颜色,优先级高于 textColor 属性
  • fontSize:该奖项文字字号(px),优先级高于 fontSize 属性
  • image:该扇区奖品图片,需 showPrizeImage 为 true;建议用包内路径(/static/xx.png)或已配置域名的网络图 —— base64 在部分小程序基础库的 canvas 中不被支持
  • weight:抽奖权重,默认 1,0 或负数表示不可抽中
  • disabled:为 true 时半透明显示且不参与随机

文字排布与奖品图

<!-- 默认:文字横向排布,奖品图在扇区外侧、文字在内侧 -->
<fg-lucky-wheel :list="prizeList" text-layout="horizontal" :font-size="14" text-color="#B3401B" />

<!-- 文字沿半径方向排布(适合扇区窄、文案长) -->
<fg-lucky-wheel :list="prizeList" text-layout="radial" />
// 单个奖项可单独设置字号与颜色(优先级高于组件级 fontSize / textColor)
const prizeList = [
    { id: 'p1', label: '一等奖 iPhone', weight: 1, fontSize: 16, textColor: '#E8452C' },
    { id: 'p2', label: '二等奖 耳机', weight: 5, image: '/static/gift.png' },
    { id: 'p3', label: '谢谢参与', weight: 40, textColor: '#909399' }
]
  • 横向排布(默认):文字横向书写,并绕圆心旋转到与本扇区顶边(外缘切线)平行的方向 —— 正上方扇区呈水平、两侧扇区呈竖排、下方扇区文字倒置,整体像一圈贴着盘缘的文字环。文字贴着顶边向下逐行排列(不参与径向居中),disk-padding(圆盘内边距,默认 0 即贴边)控制文字与顶边的距离;文字宽度按该扇区在文字块最内侧一行处的弦宽自动折行,行数同时受径向与切向空间约束,避免溢出扇区。
    • 文字块的外边界 = 半径 × text-outer-radius(默认 0.9)再向内缩 disk-padding。想让文字更贴盘缘,把 text-outer-radius 设为 1 并调小 disk-padding 即可。
    • 若不想让下半侧的文字倒置,设置 text-flip(翻转后仍与顶边平行,只是方向相反)。
    • 有 image 时:文字贴着顶边在上、奖品图排在文字下方,并且图片的倾斜角度与文字保持一致(同处一个「切向」坐标系);两者间距用 prize-image-gap,图片大小用 prize-image-size。
  • 沿半径排布(text-layout="radial"):文字从圆心向外排列,text-upright 控制左半侧是否翻转。
  • 文字区域可通过 text-inner-radius / text-outer-radius(占半径比例)微调。
  • 文字大小与颜色:组件级用 font-size(支持 16 / '16px' / '32rpx')与 text-color;单个奖项可在奖项对象上用 fontSize / textColor 覆盖全局(示例中「一等奖 iPhone」即为 16px 红字)。中心按钮的文字另有 button-font-size(默认自动按按钮尺寸与文案长度计算)。

扇区样式

属性 类型 默认值 说明
colors Array ['#FFF6E0', '#FFE2AC'] 扇区配色循环数组,奖项自身 color 优先
textColor String #B3401B 扇区文字颜色
fontSize Number/String 12 扇区文字字号:16(px)/ '16px' / '32rpx';奖项级 fontSize 优先。中心按钮文字另用 buttonFontSize
fontWeight String bold 扇区文字字重,传空字符串使用默认字重
textInnerRadius Number 0.26 文字区域内边界(占半径比例 0~1)
textOuterRadius Number 0.9 文字区域外边界(占半径比例 0~1)
maxLines Number 2 单个奖项最多行数,超出以省略号结尾(横向排布时还会按扇区弦宽自动收敛)
textLayout String horizontal 文字排布:horizontal 横向书写(默认,绕圆心旋转到与本扇区顶边平行)/ radial 沿半径方向
diskPadding Number/String 0 仅 horizontal 生效:圆盘内边距 —— 文字与所在扇区顶边(外缘)的距离(0 = 贴边),px 或 '16rpx'
textGap Number/String 0 diskPadding 的旧名称,未设置 diskPadding 时生效(保留兼容)
textFlip Boolean false 仅 horizontal 生效:把文字会倒置的下半侧扇区翻转 180°(仍与顶边平行,仅方向相反)
textUpright Boolean true 仅 radial 排布生效:左半侧扇区文字自动翻转 180°,保证正向可读
startAngle Number -90 第 0 个奖项的起始边界角。默认 -90 让正上方正好是两个奖项的分界线,因此初始状态指针指向两个奖品中间;想让某个奖项正对指针,把它设成 -90 - 半个扇区 即可

背景

属性 类型 默认值 说明
bgColor String #FFFFFF 盘面底色(扇区未配色时的回退色),transparent 为透明
bgImage String '' 盘面背景图(绘制在扇区之上、文字之下),本地 / 网络地址均可
bgImageOpacity Number 1 背景图透明度(0~1),可做纹理叠加效果
bgImageRotate Boolean true 背景图是否跟随转盘旋转,false 时背景静止
background String '' 组件根容器背景(CSS 值,支持渐变 / 图片 url)

分隔线 / 外圈 / 灯珠

属性 类型 默认值 说明
dividerColor String rgba(255,255,255,0.85) 扇区分隔线颜色
dividerWidth Number 1 分隔线宽度(px),0 不绘制
borderColor String #F0A93B 外圈颜色
borderWidth Number 12 外圈宽度(px),0 不绘制
showBorder Boolean true 是否显示外圈;关闭时不绘制圆环,且扇区与灯珠直接铺到盘面边缘
beads Number 16 外圈灯珠数量,0 不绘制
beadSize Number 4 灯珠直径(px)
beadColor String #FFF3C8 灯珠亮色
beadActiveColor String #FFB03A 灯珠暗色 / 交替色
beadBlink Boolean false 非旋转状态是否持续闪烁(旋转时始终交替闪烁)

奖项图片 / 指针 / 中心按钮

属性 类型 默认值 说明
showPrizeImage Boolean true 是否绘制奖项图片
prizeImageSize Number/String 0.2 奖品图最长边尺寸:≤1 视为占半径比例(0.2 即半径的 20%),>1 或带单位按 px / rpx(40 / '80rpx');长图按原始宽高比绘制
prizeImageGap Number/String 4 仅 horizontal 生效:奖品图与上方文字的间距,px / '8rpx'
pointer Boolean true 是否显示指针(rotateMode="button" 时自动隐藏)
pointerPosition String top 指针位置:top / right / bottom / left
pointerColor String #E8452C 指针颜色
pointerWidth Number 24 指针底边宽(px)
pointerHeight Number 30 指针高(px)
pointerImage String '' 指针图片,设置后优先用图片(请准备尖端朝向盘心的图片);图片按原始宽高比缩放后居中放进 pointerWidth × pointerHeight 盒子,不会变形
pointerInset Number/String 0 箭头与外圈的距离:默认 0(箭头贴外圈);0 < v < 1 视为占直径比例,v >= 1 或字符串按 px/rpx 解析(24 / '48rpx')
showButton Boolean true 是否显示中心按钮
buttonText String 抽奖 按钮文案
spinningText String 抽奖中 旋转中的按钮文案
buttonColor String #E8452C 按钮背景色
buttonTextColor String #FFFFFF 按钮文字颜色
buttonSize Number 0.26 按钮直径占转盘直径比例(0.1~0.5)
buttonFontSize Number/String 0 按钮字号(px),0 表示按文案长度自适应
buttonImage String '' 主体图片(带向上箭头的圆形按钮图)。设置后不再绘制默认按钮,且抽奖时只有这一层旋转;图片按原始宽高比绘制,长图不会被压扁
buttonImageSize Number 1 主体图片最长边占按钮直径的比例,可 > 1(另一边按原始比例自动推算)
buttonImageX Number/String 0 主体图片横向位移(0 = 居中),px / '8rpx'
buttonImageY Number/String 0 主体图片纵向位移(负值向上)
buttonRotateX Number/String 0 主体图片旋转轴心相对图片中心的横向偏移
buttonRotateY Number/String 0 主体图片旋转轴心的纵向偏移(负值向上)

buttonRotateX/Y 支持两种写法:|值| ≤ 1 视为占图片最长边的比例(图片会按按钮尺寸缩放,用比例可保持对准,推荐),其它按 px / rpx。它只影响旋转轨迹,不改变图片静止时的位置 —— 当图片里的圆心不在图片几何中心时(例如"圆在下方 + 箭头向上"的竖长图),用它把轴心对准圆心,抽奖时圆才会原地自转而不是绕图片中心画圈。 | buttonShadowImage | String | '' | 底层阴影图片(位于最底层,不随旋转) | | buttonShadowSize | Number | 1.25 | 阴影图片最长边占按钮直径的比例 | | buttonShadowX | Number/String | 0 | 阴影横向偏移(px / '8rpx') | | buttonShadowY | Number/String | 0 | 阴影纵向偏移 | | buttonCenterImage | String | '' | 中间图片(位于最上层,不随旋转,默认居中) | | buttonCenterSize | Number | 0.5 | 中间图片最长边占按钮直径的比例 | | buttonCenterX | Number/String | 0 | 中间图片横向偏移(0 = 居中) | | buttonCenterY | Number/String | 0 | 中间图片纵向偏移 | | buttonArrow | Boolean | false | 是否绘制默认箭头(rotateMode="button" 且无图片时自动开启) | | buttonArrowColor | String | '' | 默认箭头颜色,空则取 buttonTextColor | | buttonArrowSize | Number | 0.6 | 默认箭头长度占按钮半径的比例(箭头位于按钮外侧,1 = 伸出一个按钮半径) |

中心按钮一律绘制在 canvas 内,两种形态:

① 默认按钮(未设置 button-image):底盘带立体高光渐变 + 内描边 + 投影;箭头从按钮外缘向外伸出并绕按钮中心旋转,根部被底盘盖住。按钮大小用 button-size、文字用 button-font-size / button-text-color、箭头用 button-arrow*。

② 三层自定义图片(设置了 button-image):此时不再绘制默认按钮,改为按下表三层叠加,抽奖时只有第 2 层旋转:

层 属性 是否旋转 尺寸 / 位置
1 底层阴影 button-shadow-image 否 button-shadow-size / button-shadow-x、button-shadow-y
2 主体(带向上箭头的圆按钮) button-image 是 button-image-size / button-image-x、button-image-y(位置)/ button-rotate-x、button-rotate-y(旋转轴心)
3 中间图 button-center-image 否 button-center-size / button-center-x、button-center-y(默认居中)

行为

属性 类型 默认值 说明
rotateMode String wheel 旋转对象:wheel 转盘转动(外部指针指示)/ button 中心按钮转动(按钮内箭头指示,外部指针自动隐藏)
duration Number 4000 旋转时长(ms)
turns Number 5 最少旋转圈数
easing String easeOutQuart 缓动:linear / easeOutQuad / easeOutCubic / easeOutQuart / easeOutQuint
offsetRange Number 0.15 落点在扇区内的随机偏移比例(0~0.9),0 表示严格落在扇区正中
autoRandom Boolean false 点击中心按钮后是否由组件内部按权重随机
debug Boolean false 打开后在控制台打印每张图片的加载结果(成功含尺寸 / 失败含原因),用于排查"图标不显示"
disabled Boolean false 禁用抽奖
haptic Boolean true 结束时震动反馈(设备不支持时自动忽略)

事件

事件名 参数 说明
wheel-click { spinning } 点击转盘中心按钮时触发;spinning 为 true 表示点击时正在旋转
start { index, item } 开始旋转时触发
end { index, item } 旋转结束(指针停稳)时触发
ready - 画布初始化完成时触发
error Error 画布初始化失败等异常时触发
image-error { url, error } 某张图片(奖项图 / 盘面背景图 / 指针与按钮图片)最终加载失败时触发,便于业务方兜底
reset - 调用 reset() 时触发

事件名用 wheel-click 而非 click,避免与原生点击事件在小程序 / Vue2 下重复触发。

方法(通过 ref 调用)

方法名 参数 返回值 说明
start(target, options) target:下标 / 奖项 id / 奖项对象;options:{ duration, turns } Promise<{ index, item } \| null> 旋转到指定奖项。正在旋转或找不到奖项时 resolve null 并触发 error
random(options) 同上 同上 按 weight 权重随机抽奖并旋转
stop() - Boolean 立即结束动画,停在目标奖项上;未在旋转时返回 false
reset() - - 回到初始角度(0°)并重绘
redraw() - - 重新加载图片并重绘盘面

组件 data 中的 spinning 可直接通过 ref 读取:this.$refs.wheel.spinning。

插槽

本组件不提供插槽。原因:小程序端 canvas 是同层渲染的原生组件,DOM 叠加内容会被盘面遮挡(不同基础库/机型表现不一致),因此指针、中心按钮(含箭头、图片、文字)全部绘制在 canvas 内部,保证所有平台层级绝对一致。自定义外观请使用 pointer* / button* 系列属性与 buttonImage / pointerImage 图片。

交互说明

  • 点击转盘任意位置(包含中心按钮)即触发 wheel-click 事件;auto-random 为 true 时组件内部直接按权重随机并开始旋转。
  • 旋转过程中点击只会在 wheel-click 事件里回调 { spinning: true },不会重复触发抽奖;disabled 为 true 时点击无任何响应。

常见问题

1. 控制台提示「未获取到 canvas 节点」?

  • 微信小程序请确认基础库 >= 2.9.0(manifest.json → mp-weixin → 基础库最低版本)
  • App 端请使用 app-vue 页面(nvue 不支持 Canvas 2D),HBuilderX >= 3.x
  • 组件默认在 mounted 后查询节点,失败会自动重试 3 次(每次间隔 150ms);若组件在弹窗 / v-if 中延迟渲染,节点就绪后即可正常初始化

2. 指针方向和 pointerPosition 对不上? pointerPosition 同时决定指针的绘制位置和"停稳角度的计算基准",两者由组件统一处理,只需设置位置即可。指针默认在正上方、尖端朝下;startAngle 默认 -90 表示正上方是相邻两个奖项的分界线,因此初始状态指针正好指向两个奖品中间。

3. 抽中的结果和指针位置不一致? 请确认没有同时修改 startAngle 与 pointerPosition 造成语义冲突:startAngle 描述的是"未旋转时第 0 个奖项的起始边界角"(扇区均匀分布,中心在边界顺时针半格处),pointerPosition 描述的是指针的物理位置,二者独立配置、由组件自动换算。想让某个奖项中心正对指针,把 start-angle 设为 -90 - 180 / 奖项数。

4. 盘面文字太长 / 显示不全? 横向排布(默认)时文字宽度受扇区在文字所在半径处的弦宽限制,所以扇区越多、文案越长越容易折行或省略。可调整:font-size 调小、text-inner-radius / text-outer-radius 扩大文字区域、max-lines 增加行数,或改用 text-layout="radial" 沿半径排布(可用长度更长)。文案仍建议控制在 8 个字以内。

5. 奖项图标 / 背景图不显示? 组件会绘制 四类图片:盘面背景 bg-image、奖项图 list[].image、外部指针图 pointer-image、中心按钮图 button-image——它们都会被预加载。

排查步骤:

  1. 打开 debug:给组件加上 debug,控制台会打印每张图片的结果 —— 图片加载成功:/static/gift.png → 64×64 说明拿到了;出现 图片加载失败,已跳过:… 就是这张图的问题,日志会带上地址与失败原因。
  2. 用包内路径:把图片放进项目(如 /static/gift.png),用以 / 开头的绝对路径引用,各端都能加载(示例就是这么做的)。
  3. 网络图:小程序端必须在小程序后台配置 downloadFile 合法域名且为 https;H5 端注意跨域(组件对 H5 图片设置了 crossOrigin)。
  4. 不要依赖 base64:data:image/png;base64,... 在 H5 / App 可用,但部分小程序基础库的 canvas.createImage() 不支持 base64,会静默失败——这是"图标不显示"最常见的原因。
  5. 组件首次加载失败会自动重试一次(间隔 400ms);若平台既不触发 onload 也不触发 onerror(小程序加载包内图片时偶发),组件会每 100ms 轮询图片尺寸判定是否可用,并在 5s 后强制结束,因此不会出现"永远挂着、图不显示且后续样式也不再刷新"的情况。
  6. 图片逐个加载成功就会重绘(按帧合并),不必等全部图片就绪;地址变化或 size 变化也会重新加载。仍失败可监听 image-error 事件做兜底。
  7. 若图片是"加载成功但看不到",检查 show-prize-image(奖项图开关)、prize-image-size(图片大小)、button-image-size / pointer-width 与 pointer-height 等尺寸配置是否被调得过小。

6. 奖品图和文字会重叠吗? 不会,两者在扇区内是上下排开的:text-layout="horizontal"(默认)下文字贴顶边在上、奖品图排在文字下方,文字行数会先给图片预留空间(图片尺寸 + prize-image-gap),间距用 prize-image-gap 调整;若图片过大以致会越过文字区内边界,则跳过该图以免压住中心按钮。text-layout="radial" 下奖品图在扇区外侧、文字在内侧沿半径排列。图片大小用 prize-image-size:≤1 是占半径的比例(默认 0.2),>1 或写 '80rpx' 则按像素。

7. 旋转卡顿? 组件用 canvas.requestAnimationFrame 逐帧重绘盘面,绘制量为「扇区数 + 文字 + 灯珠」,常规 4~12 个奖项无性能压力。若自定义了很大的 bgImage,建议压缩到 600px 以内,或把 bgImageRotate 设为 false 来复用同一张绘制(组件本身每帧仍需重绘背景图,超大图会明显影响帧率)。

8. 抽奖结果一定要由后端决定吗? 不一定。纯前端场景可用 auto-random 或调用 random()(按 weight 权重随机);正式营销活动建议由后端返回中奖结果后再调用 start(index),避免前端被篡改。

9. 为什么 text-layout="radial" 下左右两侧文字方向不一样? 这是 text-layout="radial"(沿半径排布)特有的现象:左半侧文字若不处理会上下倒置,组件默认 text-upright 为 true,会把左半侧文字翻转 180° 保证每个奖项都正向可读;如需所有文字统一"从圆心向外",设置 :text-upright="false"。默认的 text-layout="horizontal" 横向排布不存在这个问题。

10. 修改了 size / 样式后没有立即生效? 样式类 props 变化会通过 watch 自动重绘;尺寸(size)变化会重设画布分辨率并重新加载图片。若在极端场景下屏幕没有刷新,可手动调用 this.$refs.wheel.redraw()。

11. 指针 / 中心按钮会被转盘盖住吗? 不会。指针与中心按钮(含按钮内箭头、图片、文字)全部绘制在 canvas 内部,绘制顺序为「盘面 → 外圈灯珠 → 指针 → 中心按钮」,因此不存在 DOM 与原生组件的层级问题,各端表现完全一致。这也是本组件不提供插槽的原因。

12. rotate-mode="button" 时按钮文字去哪了? 按钮转动模式下按钮作为"指针"使用,只显示箭头或 button-image;切回 rotate-mode="wheel" 会恢复显示 button-text。

13. 点击哪里开始抽奖? 点击转盘任意位置(含中心按钮)都会触发 wheel-click。因为视觉元素都在 canvas 内,点击由 canvas 自身的 tap 事件接收,不存在"按钮被盖住点不动"的情况。

更新日志

详见 changelog.md

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议

暂无用户评论。