0%

手把手教你玩转3D骰子组件!抽奖/小游戏必备

谁懂啊!做小程序或 App 时,想加个抽奖、桌游类互动功能,一个酷炫的 3D 骰子真的能拉满氛围感✨。今天就给大家分享一个我珍藏的 Uniapp 骰子组件,不仅能直接用,还能随心定制样式,新手也能轻松拿捏。咱们不搞晦涩术语,就像拆玩具一样,一步步摸清它的构造!

一、先看效果

先放个“成品预告”,让大家有直观感受:

情侣飞行棋

酒桌大话骰子

有趣的骰子玩法

不管是做节日抽奖、线上桌游,还是给小程序加个趣味互动模块,这个组件都能直接“拎包入住”。

二、组件结构分析

一个能跑的骰子组件,核心就三部分:“骨架”(模板)、“大脑”(脚本)、“皮肤”(样式)。咱们逐个拆解,新手也能看明白。

1、模板

骰子是正方体,有 6 个面,模板的作用就是把这 6 个面“拼”起来,再留好接口让样式和数据能联动。

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<view class="dice-container" :style="[containerStyle]">
<!-- 骰子主体:所有面都装在这个“盒子”里 -->
<view class="dice" :style="[diceStyle]">
<!-- 循环生成 6 个面,每个面对应不同位置 -->
<view v-for="(face, index) in processedFaces" :key="index"
class="dice-face" :class="[getFaceClass(index)]" :style="[getFaceStyle(index)]">
{{ face }} <!-- 面儿上的内容,比如1、2,或自定义文案 -->
</view>
</view>
</view>
</template>

这里有个小细节:用 v-for 循环生成 6 个面,而不是写 6 遍重复代码,既简洁又方便后续修改。就像做玩具时用模具批量做零件,比手工一个个画省事多了

2、脚本

脚本是骰子的“大脑”,负责控制它什么时候滚、滚多少圈、最后显示哪个面。咱们挑最关键的功能讲,避开复杂逻辑。完整脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<script>
export default {
props: {
// 骰子六个面的文案
faces: {
type: Array,
default: () => ['1', '2', '3', '4', '5', '6'],
// validator: arr => arr.length === 6
},
// 骰子大小(单位:rpx)
size: {
type: Number,
default: 300
},
backgroundColor: {
type: String,
default: '#4CAF50'
},
fontSize: {
type: Number,
default: 40
},
},
data() {
return {
rotateX: 10,
rotateY: 20,
rotateZ: 30,
isRolling: false,
result: null,
lastResult: null,
history: [],
duration: 2000,
lastTurns: null, // 用于记录上一次滚动的圈数
};
},
computed: {
// 处理后的faces数组(自动截断/填充)
processedFaces() {
const result = this.faces.slice(0, 6); // 截断前6个
// 补充空字符串至6个元素
while (result.length < 6) {
result.push('空');
}
return result;
},
// 新增:容器样式
containerStyle() {
// 容器宽高为骰子尺寸的1.5倍,确保旋转时不会超出容器
const containerSize = this.size * 1.5;
return {
width: `${containerSize}rpx`,
height: `${containerSize}rpx`,
};
},
// 动态骰子样式(宽高、旋转、过渡)
diceStyle() {
return {
width: this.size + 'rpx',
height: this.size + 'rpx',
transform: `rotateX(${this.rotateX}deg) rotateY(${this.rotateY}deg) rotateZ(${this.rotateZ}deg)`,
transition: this.isRolling ? `transform ${this.duration}ms ease-out` : 'none'
};
},
},
mounted() {

},
methods: {
// 获取骰子面类名
getFaceClass(index) {
const classes = ['front', 'back', 'right', 'left', 'top', 'bottom'];
return classes[index];
},
// 获取骰子面样式(位置、大小)
getFaceStyle(index) {
const sizeHalf = this.size / 2;
const transforms = [
`translateZ(${sizeHalf}rpx)`, // front
`rotateX(180deg) translateZ(${sizeHalf}rpx)`, // back
`rotateY(90deg) translateZ(${sizeHalf}rpx)`, // right
`rotateY(-90deg) translateZ(${sizeHalf}rpx)`, // left
`rotateX(90deg) translateZ(${sizeHalf}rpx)`, // top
`rotateX(-90deg) translateZ(${sizeHalf}rpx)` // bottom
];

// 获取当前面的内容
let dynamicSize=this.fontSize;

const content = this.processedFaces[index] || '';
const len = content.length;
if(len>2){
// 设置字体大小的动态计算逻辑
const baseSize = this.fontSize;
const minSize = 20; // 最小字体大小
const charReduction = 5; // 每个字符减少的字体大小

dynamicSize = baseSize - (len * charReduction);
dynamicSize = Math.max(minSize, dynamicSize); // 防止字体过小
}
// console.log(this.fontSize,dynamicSize,content,content.length)
return {
width: this.size + 'rpx',
height: this.size + 'rpx',
transform: transforms[index],
backgroundColor: this.backgroundColor,
fontSize: dynamicSize + 'rpx'
};
},
// 获取随机结果(0~5)
getRandomResult() {
return Math.floor(Math.random() * 6);
},
// 开始滚动骰子(可被外部调用)
startRolling() {
if (this.isRolling) return;

this.isRolling = true;
this.result = null;
// 1. 随机选择结果(0-5对应六个面)
let targetFace = this.getRandomResult();
// 2. 为每个面定义精确的摄像机朝向角度
const faceAngles = [{
x: 0,
y: 10,
z: 10
}, // 1点面(正面)
{
x: 180,
y: 10,
z: 10
}, // 6点面(后面)
{
x: 10,
y: -90,
z: 10
}, // 5点面(右面)
{
x: 0,
y: 90,
z: 10
}, // 2点面(左面)
{
x: -90,
y: 10,
z: 10
}, // 4点面(下面)
{
x: 90,
y: 10,
z: 10
} // 3点面(上面)
];
// 3. 计算最终旋转角度(包含多圈旋转)
let turns = 3 + Math.floor(Math.random() * 3); // 3-5圈旋转
// 判断是否与上一次相同
let wasSameTurn = turns === this.lastTurns;
if (wasSameTurn) {
turns += 3 + Math.floor(Math.random() * 3); // 额外圈数
}

// 4. 应用新旋转角度
this.rotateX = turns * 360 + faceAngles[targetFace].x;
this.rotateY = turns * 360 + faceAngles[targetFace].y;
this.rotateZ = turns * 360 + faceAngles[targetFace].z;

// 5. 添加小幅度随机偏移(±5°)使动画更自然
this.rotateX += (Math.random() * 10) - 5;
this.rotateY += (Math.random() * 10) - 5;
this.rotateZ += (Math.random() * 10) - 5;

this.lastTurns = turns;

// 6. 动画结束后确定结果
setTimeout(() => {
this.isRolling = false;
// 添加细微抖动效果(可选)
this.rotateX += (Math.random() * 2) - 1;
this.rotateY += (Math.random() * 2) - 1;
this.rotateZ += (Math.random() * 2) - 1;

this.result = this.processedFaces[targetFace];
// 回调
this.$emit('result', this.result);
}, this.duration);
}
}
};
</script>

①、自定义参数

组件开头的 props 就是“自定义开关”,想改骰子样式,改这里的参数就行,不用动核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
props: {
faces: { // 骰子6个面的内容,默认是1-6
type: Array,
default: () => ['1', '2', '3', '4', '5', '6'],
},
size: { // 骰子大小,单位rpx(适配手机屏幕)
type: Number,
default: 300, // 默认差不多占手机1/3宽
},
backgroundColor: { // 骰子颜色,默认是清新绿
type: String,
default: '#4CAF50',
},
fontSize: { // 面儿上文字大小
type: Number,
default: 40,
},
}

举个例子:想做一个抽奖骰子,只需把 faces 改成 [‘一等奖’, ‘二等奖’, ‘谢谢参与’, ‘三等奖’, ‘谢谢参与’, ‘谢谢参与’],瞬间变身抽奖工具,是不是超方便?

②、滚动核心:startRolling 方法

这是让骰子滚动的“核心指令”,逻辑很像玩真骰子:先晃几圈,再随机停在一个面。关键步骤拆解:

  • 防重复滚动:用 isRolling 标记状态,避免手快连续点,导致骰子“卡壳”;
  • 随机定结果:用getRandomResult 随机选一个面(0-5 对应 6 个面),相当于骰子落地后的结果;
  • 加滚动动画:让骰子绕 X/Y/Z 轴转 3-5 圈(随机圈数,避免呆板),最后精准停在目标面;
  • 结果同步:动画结束后,通过 $emit 把结果传给父组件,比如弹窗显示“恭喜中一等奖”。

这里还有个小优化:滚动结束后会加轻微抖动,就像骰子落地时的惯性晃动,细节拉满!

3、样式

光有骨架和大脑还不够,样式能让扁平的代码变成立体骰子,核心就两个 CSS 属性。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<style>
/* 容器样式 */
.dice-container {
/* 给眼睛加“透视”,让3D效果更真实 */
perspective: 1000rpx;
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx;
overflow: visible;
}

/* 骰子整体样式 */
.dice {
position: relative;
/* 开启3D空间,让6个面不重叠 */
transform-style: preserve-3d;
/* 初始斜着放,更有立体感 */
transform: rotateX(35deg) rotateY(45deg);
/* 初始视角 */
transition: transform 1s ease-out;
}

/* 面样式 */
.dice-face {
/* 让6个面叠在一起,再通过3D旋转分开 */
position: absolute;
/* 白色边框,区分每个面 */
border: 4rpx solid #fff;
border-radius: 10rpx;
color: #fff;
/* font-size: 40rpx; */
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-shadow:
inset 0 0 20rpx rgba(255, 255, 255, 0.3),
0 0 20rpx rgba(0, 0, 0, 0.2);
}
</style>

简单说:perspective 让我们看骰子时有“远近感”,transform-style: preserve-3d 让 6 个面各自待在自己的 3D 位置,不会扁平重叠。少了这两个属性,骰子就会变成“一张贴在一起的纸片”,毫无立体感。

三、使用示例

讲完原理,最实用的部分来了!把组件引入项目,3 步就能用:

第一步:引入组件

把骰子组件文件(比如 dice.vue)放在项目的 components 文件夹里,在父组件中引入并注册:

1
2
3
4
5

import dice from '@/components/dice/dice.vue';
export default {
components: { dice }, // 注册组件
}

第二步:页面中使用

像用普通标签一样写在模板里,还能自定义参数:

1
2
3
4
5
6
7
8
9
10

&lt;dice
:size="250" <!-- 骰子大小250rpx -->
backgroundColor="#f44336" <!-- 红色骰子 -->
:faces="['一等奖','二等奖','三等奖','谢谢参与','谢谢参与','谢谢参与']"
@result="handleResult" <!-- 监听滚动结果 -->
ref="diceRef" <!-- 用来触发滚动 -->
></dice>
<!-- 加个按钮,点击触发滚动 -->
<button @click="startRoll" style="margin-top: 40rpx;">开始抽奖</button>

第三步:写触发和结果逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14

methods: {
startRoll() {
// 调用骰子的滚动方法
this.$refs.diceRef.startRolling();
},
handleResult(result) {
// 接收骰子滚动结果,弹窗显示
uni.showToast({
title: `恭喜获得:${result}`,
icon: 'none'
});
}
}

搞定!点击按钮,红色的抽奖骰子就会翻滚起来,结束后弹窗显示结果,成就感直接拉满 🎉。

扩展:进阶玩法

3个实用扩展玩法

  • 加音效:滚动开始时播放“摇晃声”,结束时播放“叮”的提示音,沉浸感翻倍;
  • 结果记录:用组件里的 history 数组,记录每次滚动结果,做个“抽奖记录”面板;
  • 控制滚动时长:把 duration 改成 props 参数,想让骰子滚得久一点就调大数值(比如 3000 就是 3 秒)。

四、最后

一个效果生动的 3D 骰子,能让抽奖、游戏等互动场景的体验感大幅提升。上面拆解的所有逻辑,都整合在各自的结构开头的源码里了。大家可以直接复制源码到项目中,根据自己的需求改一改 faces、颜色、大小,就能快速实现一个酷炫的 3D 骰子。

如果你喜欢这个组件,或者有更好的实现思路,欢迎在评论区分享你的想法~

我超好奇大家会用这个组件做什么场景~ 是做节日抽奖小程序,还是搭线上桌游局?或者有更脑洞大开的玩法?快来评论区晒出你的需求和创意!

如果实操中遇到样式错乱、滚动异常等问题,也欢迎随时留言提问,我会逐一回复帮你排查;觉得有用的话,别忘了点赞收藏,后续还会分享更多实用组件,咱们评论区见~ ✨

您的打赏,是我创作的动力!不给钱?那我只能靠想象力充饥了。