更新记录
1.0.21(2026-09-30) 下载此版本
- 新增
button-rotate-x/button-rotate-y:中心按钮主体图片可自定义转动中心的上下左右位置(相对图片中心偏移) - 取值支持两种写法:
|值| ≤ 1视为占图片最长边的比例(图片会缩放到按钮尺寸,用比例可保持对准),其它按 px / rpx - 轴心偏移只影响旋转轨迹,不改变图片静止位置;默认
0时与之前行为完全一致(绕图片中心转),因此对已有用法无影响 - 适用场景:图片里的圆心不在图片几何中心时(如"圆在下方 + 向上箭头"的竖长图),把轴心对准圆心,抽奖时圆才是原地自转而不是绕图片中心画圈
- 示例页改用新增的竖长示例图
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-y2 主体(带向上箭头的圆按钮) 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——它们都会被预加载。
排查步骤:
- 打开
debug:给组件加上debug,控制台会打印每张图片的结果 ——图片加载成功:/static/gift.png → 64×64说明拿到了;出现图片加载失败,已跳过:…就是这张图的问题,日志会带上地址与失败原因。 - 用包内路径:把图片放进项目(如
/static/gift.png),用以/开头的绝对路径引用,各端都能加载(示例就是这么做的)。 - 网络图:小程序端必须在小程序后台配置 downloadFile 合法域名且为 https;H5 端注意跨域(组件对 H5 图片设置了
crossOrigin)。 - 不要依赖 base64:
data:image/png;base64,...在 H5 / App 可用,但部分小程序基础库的canvas.createImage()不支持 base64,会静默失败——这是"图标不显示"最常见的原因。 - 组件首次加载失败会自动重试一次(间隔 400ms);若平台既不触发
onload也不触发onerror(小程序加载包内图片时偶发),组件会每 100ms 轮询图片尺寸判定是否可用,并在 5s 后强制结束,因此不会出现"永远挂着、图不显示且后续样式也不再刷新"的情况。 - 图片逐个加载成功就会重绘(按帧合并),不必等全部图片就绪;地址变化或
size变化也会重新加载。仍失败可监听image-error事件做兜底。 - 若图片是"加载成功但看不到",检查
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

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 3
赞赏 0
下载 12649018
赞赏 1953
赞赏
京公网安备:11010802035340号