1. 抽奖活动页面

yj-dev
杨杰 3 years ago
parent 89b68cdb7e
commit b1210d7b96
  1. 190
      pages.json
  2. 5
      pages/tabBar/user/user.vue
  3. BIN
      static/img/head.jpg
  4. BIN
      subPages/lottery/lottery.lnk
  5. 552
      subPages/lottery/lottery.vue
  6. BIN
      subPages/static/lottery/gold.png
  7. BIN
      subPages/static/lottery/gold@2x.png
  8. BIN
      subPages/static/lottery/gold@3x.png
  9. BIN
      subPages/static/lottery/icon_xxhg.png
  10. BIN
      subPages/static/lottery/icon_xxhg@2x.png
  11. BIN
      subPages/static/lottery/icon_xxhg@3x.png
  12. BIN
      subPages/static/lottery/icon_zhongjiang.png
  13. BIN
      subPages/static/lottery/icon_zhongjiang@2x.png
  14. BIN
      subPages/static/lottery/icon_zhongjiang@3x.png
  15. BIN
      subPages/static/lottery/top-bg.jpg
  16. BIN
      subPages/static/lottery/top-bg.png
  17. BIN
      subPages/static/lottery/top-bg@2x.jpg
  18. BIN
      subPages/static/lottery/top-bg@2x.png
  19. BIN
      subPages/static/lottery/top-bg@3x.jpg
  20. BIN
      subPages/static/lottery/top-bg@3x.png
  21. 132
      uni_modules/almost-lottery/changelog.md
  22. 970
      uni_modules/almost-lottery/components/almost-lottery/almost-lottery.vue
  23. 78
      uni_modules/almost-lottery/package.json
  24. 159
      uni_modules/almost-lottery/readme.md
  25. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__action.png
  26. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__action2x.png
  27. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__action3x.png
  28. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__bg.png
  29. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__bg2x.png
  30. BIN
      uni_modules/almost-lottery/static/almost-lottery/almost-lottery__bg3x.png
  31. 303
      uni_modules/almost-lottery/utils/almost-utils.js

@ -1,6 +1,6 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
{
"path": "pages/tabBar/home/home",
"style": {
"navigationBarTextStyle": "white",
@ -18,18 +18,17 @@
}]
}
}
},{
"path" : "pages/user/bindingCardList/bindingCardList",
"style" :
{
"navigationBarTitleText": "卡列表",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": false
}
},{
}, {
"path": "pages/user/bindingCardList/bindingCardList",
"style": {
"navigationBarTitleText": "卡列表",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": false
}
}, {
"path": "pages/login/login",
"style": {
"navigationBarBackgroundColor": "#0083f5",
@ -385,101 +384,102 @@
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/tabBar/order/webUrl/webUrl",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/login/updatePas/updatePas",
"style" :
{
}, {
"path": "pages/tabBar/order/webUrl/webUrl",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "pages/login/updatePas/updatePas",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "设置密码",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
} ,{
"path" : "pages/login/updatePas/resetPas",
"style" :
{
}
}, {
"path": "pages/login/updatePas/resetPas",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "修改密码",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}
,{
"path" : "pages/login/loginInvalid/loginInvalid",
"style" :
{
"enablePullDownRefresh": false,
"navigationBarTitleText": "",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}
, {
"path" : "pages/user/bindingCard/bindingCard",
"style" :
{
"navigationBarTitleText": "绑定卡号",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": false
}
},{
"path" : "pages/user/unionCard/unionCard",
"style" :
{
"enablePullDownRefresh": false,
"navigationBarTitleText": "卡记录",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}
,{
"path" : "pages/userLogin/userLogin",
"style" :
{
"navigationBarTitleText": "嗨森购",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/user/cardDes/cardDes",
"style" :
{
"enablePullDownRefresh": false,
"navigationBarTitleText": "绑定卡号",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}
],
}
}, {
"path": "pages/login/loginInvalid/loginInvalid",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}, {
"path": "pages/user/bindingCard/bindingCard",
"style": {
"navigationBarTitleText": "绑定卡号",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": false
}
}, {
"path": "pages/user/unionCard/unionCard",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "卡记录",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}, {
"path": "pages/userLogin/userLogin",
"style": {
"navigationBarTitleText": "嗨森购",
"enablePullDownRefresh": false
}
}, {
"path": "pages/user/cardDes/cardDes",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "绑定卡号",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#0083f5",
"backgroundColorTop": "#0083f5",
"navigationBarTextStyle": "white"
}
}
],
"subpackages": [{
"root": "subPages",
"pages": [{
"path": "lottery/lottery",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "抽奖活动",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#FF893F",
"backgroundColorTop": "#FF893F",
"navigationBarTextStyle": "white"
}
}]
}],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "",

@ -200,6 +200,11 @@
url: '../../login/login?id=2',
text: '我的推广',
img: 'user/renw.png'
},
{
url: '../../../subPages/lottery/lottery',
text: '抽奖活动',
img: 'user/renw.png'
}
// ,
// {

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

@ -0,0 +1,552 @@
<template>
<view class="almost-lottery">
<!-- head -->
<image src="../static/lottery/top-bg@3x.jpg" mode="widthFix" class="width100"></image>
<view class="almost-lottery__wheel">
<almost-lottery :lottery-size="lotteryConfig.lotterySize" :action-size="lotteryConfig.actionSize"
:ring-count="2" :duration="1" :prize-list="prizeList" :prize-index="prizeIndex"
@reset-index="prizeIndex = -1" @draw-start="handleDrawStart" @draw-end="handleDrawEnd"
@finish="handleDrawFinish" v-if="prizeList.length" />
</view>
<view class="almost-lottery__rule">
<view class="rule-head">
<view class="line"></view>
<text class="title">中奖名单</text>
<view class="line"></view>
</view>
<!--
<view style="height: 40px;">
<view class="page-section swiper">
<view class="page-section-spacing">
<swiper class="swiper" vertical="true" :indicator-dots="indicatorDots" :autoplay="autoplay" :interval="interval" :duration="duration">
<swiper-item style="height: 40px;">
<view class="swiper-item uni-bg-red">冯绍峰地方是的反手反手电风扇电风扇发是</view>
</swiper-item>
<swiper-item style="height: 40px;">
<view class="swiper-item uni-bg-green">了看了你别浪费女生了妇女了啥地方了南斯拉夫弄丢了</view>
</swiper-item>
<swiper-item style="height: 40px;">
<view class="swiper-item uni-bg-blue">沃尔沃二女玩女人温柔我能让你为免费派送费牛棚</view>
</swiper-item>
</swiper>
</view>
</view>
</view> -->
<view class="rule-head">
<view class="line"></view>
<text class="title">活动规则</text>
<view class="line"></view>
</view>
<view class="rule-body">
<view class="item">
<view class="number">1</view>
<view class="text">
<text>抽奖细则</text>
<text>每人每天拥有{{ freeNumDay }}次抽奖机会每次消耗{{ goldNum }}金币</text>
</view>
</view>
<view class="item item-rule">
<view class="number">2</view>
<view class="text">
<text>奖励说明</text>
<text style="margin-top: 1px;">1.一等奖1000元加油券(4)</text>
<text style="margin-top: 1px;">2.二等奖500元加油券(12)</text>
<text style="margin-top: 1px;">3.三等奖300元加油券(20)</text>
<text style="margin-top: 1px;">4.参与奖20元(200-20)抵扣券(2000)</text>
</view>
</view>
<template>
<view class="item">
<view class="number">3</view>
<view class="text">本次活动由XXXXXXX发起与Apple.Inc无关</view>
</view>
<view class="item">
<view class="number">4</view>
<view class="text">苹果公司不是本活动的赞助商且没有以任何形式参与活动</view>
</view>
<view class="item">
<view class="number">5</view>
<view class="text">本活动仅限17岁以上用户参加</view>
</view>
<view class="item">
<view class="number">6</view>
<view class="text">本活动最终解释权归XXXXXXX所有</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
import AlmostLottery from '../../uni_modules/almost-lottery/components/almost-lottery/almost-lottery.vue'
export default {
name: 'Home',
components: {
AlmostLottery
},
data() {
return {
//
isDev: true,
background: ['color1', 'color2', 'color3'],
indicatorDots: false,
autoplay: true,
interval: 2000,
duration: 500,
//
lotteryConfig: {
// rpx
lotterySize: 600,
// rpx
actionSize: 200
},
// UI
//
lotteryBg: 'https://raw.githubusercontent.com/ialmost/almost-components_uniapp/dev/Lottery/almost-lottery_sample/static/lottery-bg.png',
//
actionBg: 'https://raw.githubusercontent.com/ialmost/almost-components_uniapp/dev/Lottery/almost-lottery_sample/static/action-bg.png',
//
//
prizeList: [],
//
onStock: true,
//
prizeIndex: -1,
//
prizeing: false,
//
//
onFrontend: false,
//
prizeWeightMax: 0,
//
prizeWeightArr: [],
//
//
goldCoin: 600,
//
freeNum: 3,
//
goldNum: 20,
//
freeNumDay: 3
}
},
computed: {
isApple() {
return uni.getSystemInfoSync().platform === 'ios'
}
},
methods: {
changeIndicatorDots(e) {
this.indicatorDots = !this.indicatorDots
},
changeAutoplay(e) {
this.autoplay = !this.autoplay
},
intervalChange(e) {
this.interval = e.target.value
},
durationChange(e) {
this.duration = e.target.value
},
//
handleInitCanvas() {
this.prizeList = []
this.getPrizeList()
},
//
async getPrizeList() {
uni.showLoading({
title: '奖品准备中...'
})
//
let res = await this.requestApiGetPrizeList()
console.log('获取奖品列表', res)
if (res.ok) {
let data = res.data
if (data.length) {
this.prizeList = data
console.log('已获取到奖品列表数据,开始绘制抽奖转盘')
//
//
if (this.onFrontend) {
//
this.prizeWeightArr = this.prizeList.map((item) => item.prizeWeight)
let prizeWeightArrSort = [...this.prizeWeightArr]
prizeWeightArrSort.sort((a, b) => b - a)
this.prizeWeightMax = prizeWeightArrSort[0]
}
}
} else {
uni.hideLoading()
uni.showToast({
title: '获取奖品失败',
mask: true,
icon: 'none'
})
}
},
//
// Promise
//
requestApiGetPrizeList() {
return new Promise((resolve, reject) => {
let requestTimer = setTimeout(() => {
clearTimeout(requestTimer)
requestTimer = null
// prizeStock
// prizeWeight
resolve({
ok: true,
data: [{
prizeId: 1,
prizeName: '0.1元现金',
prizeStock: 10,
prizeWeight: 200,
prizeImage: '/static/img/order3.png'
},
{
prizeId: 2,
prizeName: '10元现金',
prizeStock: 0,
prizeWeight: 50,
prizeImage: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/56f085e0-bcfe-11ea-b244-a9f5e5565f30.png'
},
{
prizeId: 3,
prizeName: '5元话费',
prizeStock: 1,
prizeWeight: 80,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABCElEQVRoge3YMa4BURSH8Y8o7UAp0WgkotBZwluAfhqlZSgUGr23ENUUCpppJnTswAIUSCaTiziZJ8d9/193zdzrfMltABF5plb+oLscDoAV0Pn8OC/lwDhL0k35QT3wstcIuM61Cj0IhXiNuAvOFwr5SgrxRiHeKMSbhnHfAVgU1i1gajhnBpwK6wnQtgxkDTlmSTq/L7rLYQ9byG+WpLvCOT8YQ6K5WgrxRiHeKMQbhXijEG8U4o1CvIkmxPrDquwMrI37KlFJSJake2BUxVlW0VytaEKsV6t5+8Ohak3rRmtIH9hav/QvRHO1FOKNQrwJheQfn+I9wflCIeNHLzuQc51PRP6rC1ZeIm1I8cC5AAAAAElFTkSuQmCC'
},
{
prizeId: 4,
prizeName: '50元现金',
prizeStock: 0,
prizeWeight: 10,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAB2klEQVRIia3Wv09TURjG8U+u2kQZcNENh2ok0cnJ0R+L0RB2g/EvwITEwRgnV0Zx0fAHYBh0khBD4sBAmAgDiwQNg4qwmSBDAYf7Fi+n97al9UlObnvO836f3tO355b2GsAYZrCBPRzGdSPmx8J3YtXwDDsB7TR2wl/rNmAYK13C07ES9W11E9s9BjTHdnBKVf8PAcWgehpwCssF0w/5FzqLgw7AA7zDQ/wqzC8H90jjSeFMYW0Yr8PzALfjOh7z1wve2YTzpLlQw2ay+KZ1N7vS24SziVqGexhKzB07pEJXk/dDwTetdZ9HewwZLWFNw2oyudZjQFNrCW8109pqS32GpPX1TOu586fPkLR+IEMjmbzYZ0ha34Atx/fwW58hXxPeFixo7Yg7PQbcKmEtZFgsMb/C4AkDBuUnQKpFuFZI3cZuvP6C+8g6wDP572O95C4Og49/W/YJl/G5YPqO9yV3dh4f8LMCfhjcI93Afix8xAX5SVx2YBY10yagEdxjmiwY7uISXuIFrlSEPG0TMllWcAbzYViXd0rzeXC6ImSiImA+eKU6h7mCeV/eCBMnCJkLzpHSztnFCJ7L//ZkOFv1iRLtRd1IcLpSHVP4jccVnkexPqXkmd7UX15b7tiz29ReAAAAAElFTkSuQmCC'
},
{
prizeId: 5,
prizeName: '1卷抽纸',
prizeStock: 3,
prizeWeight: 3000,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABCElEQVRoge3YMa4BURSH8Y8o7UAp0WgkotBZwluAfhqlZSgUGr23ENUUCpppJnTswAIUSCaTiziZJ8d9/193zdzrfMltABF5plb+oLscDoAV0Pn8OC/lwDhL0k35QT3wstcIuM61Cj0IhXiNuAvOFwr5SgrxRiHeKMSbhnHfAVgU1i1gajhnBpwK6wnQtgxkDTlmSTq/L7rLYQ9byG+WpLvCOT8YQ6K5WgrxRiHeKMQbhXijEG8U4o1CvIkmxPrDquwMrI37KlFJSJake2BUxVlW0VytaEKsV6t5+8Ohak3rRmtIH9hav/QvRHO1FOKNQrwJheQfn+I9wflCIeNHLzuQc51PRP6rC1ZeIm1I8cC5AAAAAElFTkSuQmCC'
},
{
prizeId: 6,
prizeName: '0.2元现金',
prizeStock: 8,
prizeWeight: 120,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABCElEQVRoge3YMa4BURSH8Y8o7UAp0WgkotBZwluAfhqlZSgUGr23ENUUCpppJnTswAIUSCaTiziZJ8d9/193zdzrfMltABF5plb+oLscDoAV0Pn8OC/lwDhL0k35QT3wstcIuM61Cj0IhXiNuAvOFwr5SgrxRiHeKMSbhnHfAVgU1i1gajhnBpwK6wnQtgxkDTlmSTq/L7rLYQ9byG+WpLvCOT8YQ6K5WgrxRiHeKMQbhXijEG8U4o1CvIkmxPrDquwMrI37KlFJSJake2BUxVlW0VytaEKsV6t5+8Ohak3rRmtIH9hav/QvRHO1FOKNQrwJheQfn+I9wflCIeNHLzuQc51PRP6rC1ZeIm1I8cC5AAAAAElFTkSuQmCC'
},
{
prizeId: 7,
prizeName: '谢谢参与',
prizeStock: 100,
prizeWeight: 10000,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABCElEQVRoge3YMa4BURSH8Y8o7UAp0WgkotBZwluAfhqlZSgUGr23ENUUCpppJnTswAIUSCaTiziZJ8d9/193zdzrfMltABF5plb+oLscDoAV0Pn8OC/lwDhL0k35QT3wstcIuM61Cj0IhXiNuAvOFwr5SgrxRiHeKMSbhnHfAVgU1i1gajhnBpwK6wnQtgxkDTlmSTq/L7rLYQ9byG+WpLvCOT8YQ6K5WgrxRiHeKMQbhXijEG8U4o1CvIkmxPrDquwMrI37KlFJSJake2BUxVlW0VytaEKsV6t5+8Ohak3rRmtIH9hav/QvRHO1FOKNQrwJheQfn+I9wflCIeNHLzuQc51PRP6rC1ZeIm1I8cC5AAAAAElFTkSuQmCC'
},
{
prizeId: 8,
prizeName: '100金币',
prizeStock: 100,
prizeWeight: 3000,
prizeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABCElEQVRoge3YMa4BURSH8Y8o7UAp0WgkotBZwluAfhqlZSgUGr23ENUUCpppJnTswAIUSCaTiziZJ8d9/193zdzrfMltABF5plb+oLscDoAV0Pn8OC/lwDhL0k35QT3wstcIuM61Cj0IhXiNuAvOFwr5SgrxRiHeKMSbhnHfAVgU1i1gajhnBpwK6wnQtgxkDTlmSTq/L7rLYQ9byG+WpLvCOT8YQ6K5WgrxRiHeKMQbhXijEG8U4o1CvIkmxPrDquwMrI37KlFJSJake2BUxVlW0VytaEKsV6t5+8Ohak3rRmtIH9hav/QvRHO1FOKNQrwJheQfn+I9wflCIeNHLzuQc51PRP6rC1ZeIm1I8cC5AAAAAElFTkSuQmCC'
}
]
})
}, 2000)
})
},
//
handleDrawStart() {
console.log('触发抽奖按钮')
if (this.prizeing) return
this.prizeing = true
//
if (this.freeNum > 0 || this.goldCoin >= this.goldNum) {
//
if (this.freeNum > 0) {
this.freeNum--
} else if (this.goldCoin >= this.goldNum) {
this.goldCoin -= this.goldNum
}
this.tryLotteryDraw()
} else {
this.prizeing = false
uni.showModal({
title: '金币不足',
content: '是否前往赚取金币?',
success: (res) => {
//
console.log('金币不足', res)
}
})
}
},
//
tryLotteryDraw() {
console.log('旋转开始,获取中奖下标......')
//
if (this.onFrontend) {
this.localGetPrizeIndex()
} else {
this.remoteGetPrizeIndex()
}
},
//
localGetPrizeIndex() {
console.warn('###当前处于前端控制中奖概率,安全起见,强烈建议由后端控制###')
//
// prizeId
if (!this.prizeWeightMax) {
console.warn('###当前已开启前端控制中奖概率,但是奖品数据列表中的 prizeWeight 参数似乎配置不正确###')
return
}
console.log('当前权重最大值为 =>', this.prizeWeightMax)
// 使 Math.ceil 0
let randomWeight = Math.ceil(Math.random() * this.prizeWeightMax)
console.log('本次权重随机数 =>', randomWeight)
//
let tempMaxArrs = []
this.prizeList.forEach((item) => {
if (item.prizeWeight >= randomWeight) {
tempMaxArrs.push(item.prizeWeight)
}
})
console.log('tempMaxArrs', tempMaxArrs)
//
//
let tempMaxArrsLen = tempMaxArrs.length
if (tempMaxArrsLen) {
tempMaxArrs.sort((a, b) => a - b)
//
if (tempMaxArrsLen > 1) {
//
let sameCount = 0
for (let i = 0; i < tempMaxArrs.length; i++) {
if (tempMaxArrs[i] === tempMaxArrs[0]) {
sameCount++
}
}
// 1
if (sameCount === 1) {
this.prizeIndex = this.prizeWeightArr.indexOf(tempMaxArrs[0])
} else {
//
let sameWeight = tempMaxArrs[0]
let sameWeightArr = []
let sameWeightItem = {}
this.prizeWeightArr.forEach((item, index) => {
if (item === sameWeight) {
sameWeightArr.push({
prizeWeight: item,
index
})
}
})
console.log('sameWeightArr', sameWeightArr)
sameWeightItem = sameWeightArr[Math.floor(Math.random() * sameWeightArr.length)]
console.log('sameWeightItem', sameWeightItem)
this.prizeIndex = sameWeightItem.index
}
} else {
this.prizeIndex = this.prizeWeightArr.indexOf(tempMaxArrs[0])
}
}
console.log('本次抽中奖品 =>', this.prizeList[this.prizeIndex].prizeName)
//
if (this.onStock) {
console.log('本次奖品库存 =>', this.prizeList[this.prizeIndex].prizeStock)
}
},
//
//
remoteGetPrizeIndex() {
console.warn('###当前处于模拟的请求接口,并返回了中奖信息###')
//
let stoTimer = setTimeout(() => {
clearTimeout(stoTimer)
stoTimer = null
let list = [...this.prizeList]
// prizeId prizeId
let prizeId = Math.floor(Math.random() * list.length + 1)
// prizeId
for (let i = 0; i < list.length; i++) {
let item = list[i]
if (item.prizeId === prizeId) {
//
this.prizeIndex = i
break
}
}
console.log('本次抽中奖品 =>', this.prizeList[this.prizeIndex].prizeName)
//
if (this.onStock) {
console.log('本次奖品库存 =>', this.prizeList[this.prizeIndex].prizeStock)
}
}, 200)
},
//
handleDrawEnd() {
console.log('旋转结束,执行拿到结果后到逻辑')
//
this.prizeing = false
let prizeName = this.prizeList[this.prizeIndex].prizeName
let tipContent = ''
if (prizeName === '谢谢参与') {
tipContent = '很遗憾,没有中奖,请再接再厉!'
} else {
//
if (this.onStock) {
let prizeStock = this.prizeList[this.prizeIndex].prizeStock
tipContent = prizeStock ? `恭喜您,获得 ${prizeName}` : `很抱歉,您来晚了,当前奖品 ${prizeName} 已无库存`
} else {
tipContent = `恭喜您,获得 ${prizeName}`
}
}
uni.showModal({
content: tipContent,
showCancel: false
})
},
//
handleDrawFinish(res) {
console.log('抽奖转盘绘制完成', res)
uni.showToast({
title: res.msg,
mask: true,
icon: 'none'
})
}
},
onLoad() {
this.handleInitCanvas()
},
onUnload() {
uni.hideLoading()
}
}
</script>
<style lang="scss" scoped>
.almost-lottery {
flex: 1;
background-color: #FF893F;
}
.almost-lottery__wheel {
margin-top: 30rpx;
text-align: center;
.almost-lottery__count {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 40rpx 0;
}
.text {
color: #FFFFFF;
font-size: 24rpx;
}
}
.almost-lottery__rule {
padding: 0 28rpx;
color: #FFF8CB;
.rule-head {
display: flex;
justify-content: space-around;
align-items: center;
margin: 40rpx 0;
.line {
flex: 1;
height: 1px;
background-color: #FFF3A5;
}
.title {
width: 280rpx;
color: #F63857;
line-height: 70rpx;
text-align: center;
margin: 0 20rpx;
border-radius: 8rpx;
background-image: linear-gradient(0deg, rgba(255, 242, 158, 1), rgba(255, 244, 168, 1));
}
}
.rule-body {
color: #FFF8CB;
font-size: 24rpx;
padding: 10rpx 0 40rpx;
.item {
display: flex;
margin-bottom: 10rpx;
}
.number {
position: relative;
top: 4rpx;
width: 28rpx;
height: 28rpx;
line-height: 28rpx;
text-align: center;
border-radius: 50%;
margin-right: 10rpx;
}
.text {
flex: 1;
margin-top: 1px;
}
.item-rule .text {
display: flex;
margin-top: 1px;
flex-direction: column;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

@ -0,0 +1,132 @@
## 1.8.5(2021-09-13)
本次更新:
- 修复一个已知问题
## 1.8.4(2021-09-12)
本次更新:
- 调整 `strFontColor``strFontColors`,现在可以设置每个区块的文字颜色,详见文档说明
## 1.8.3(2021-09-12)
本次更新:
- 修复因 `1.8.0` 改动引起的文字方向、无奖品图时绘制异常的问题
- 新增 `imgDrawed` 是否绘制奖品图片的配置项 ,默认为 `true`
## 1.8.2(2021-09-10)
本次更新:
**不兼容旧版本的更新**
- 移除配置项 `strKey` 字段
- 调整 `prizeList` 结构
## 1.8.1(2021-09-06)
本次更新:
- 修复 `hbx3.1.22` 在小程序平台处理 `id-name` 存在解析错误的问题
## 1.8.0(2021-09-06)
本次更新:
**该版本更新涉及破坏性的变更,请重新查看 `API - Props` 的部分**
- `px` 全面调整为 `rpx` 单位,多个`Props` 的参数相应调整,请查看文档
- 新增 `pixelRatio` 参数,该参数为设计稿的设备像素比基准值,默认为 `2` 倍素
## 1.7.18(2021-09-05)
本次更新:
- 修复一个已知问题
## 1.7.17(2021-08-23)
本次更新:
- 更新示例项目
## 1.7.16(2021-08-14)
本次更新:
- 更新示例项目
## 1.7.15(2021-08-03)
本次更新:
- 新增文字竖向展示的功能,详见文档说明
## 1.7.13(2021-08-02)
本次更新:
- 更新文档
## 1.7.12(2021-07-30)
本次更新:
- 修复示例项目的已知问题
- 现已提供Almost-Lottery抽奖转盘的uniCloud云端一体页面模板
- 现已提供Almost-Lottery抽奖转盘云端一体页面配套的Admin配置中心
## 1.7.11(2021-07-22)
本次更新:
- 修复部分安卓手机文字大小异常的问题
- 字段 `strHeightMultiple` 更换为 `strLineHeight`
## 1.7.10(2021-07-12)
本次更新:
- 修复奖品名称 `name` 为空字符串时无法成功绘制转盘的问题
- 新增 `prizeNameDrawed` 是否绘制奖品名称的配置项,现在可以仅展示奖品图片了
## 1.7.9(2021-07-09)
本次更新:
- 优化组件内部代码
- 修复奖品图片已然是 `base64` 格式时导致转盘绘制失败的问题
- 文档新增QQ群号,让沟通更便捷
## 1.7.8(2021-07-08)
本次更新:
- 调整 `Canvas` 默认宽高为 `280`
## 1.7.7(2021-07-08)
本次更新:
- 新增多个配置项,满足更多自定义需求
- 优化多行文本情况下非中文字符的字节处理
- 修复偶发的第一条数据文本不居中显示的问题
## 1.7.6(2021-07-02)
本次更新:
- 调整 `imageWidth``imageHeight` 字段为 `imgWidth``imgHeight`
- 更新示例项目
## 1.7.5(2021-07-01)
本次更新:
- 新增配置项 `imgMarginStr` 奖品图片距离奖品文字的距离
## 1.7.4(2021-06-28)
本次更新:
- 新增轮盘旋转或指针旋转配置项
- 转盘内置的外环图片以及按钮图片统一调整为 `image` 展示
- 更新相关文档说明
## 1.7.3(2021-06-16)
本次更新:
- 优化错误提示
- 优化示例项目
- 优化文档说明
## 1.7.2(2021-06-11)
本次更新:
- 新增 `canvasId` 参数配置项,多画板情况下需要配置不同的 `canvasId`
- 优化多画板情况下的缓存功能
- 优化示例项目
- 修改文档说明
## 1.7.1(2021-06-10)
本次更新:
- 优化示例项目中的注释
## 1.7.0(2021-06-04)
本次更新:
- 修复 `1.6.1` 引起的多行奖品文字行高异常的问题
- 新增配置转盘外环和抽奖按钮图片的功能,详见文档说明
- 更新示例项目,新增抽奖次数等业务有关的逻辑供参考
## 1.6.1(2021-05-28)
本次更新:
- 修复小程序平台画板模糊的问题
## 1.6.0(2021-05-28)
本次更新:
- 新增奖品区块是否开启描边的配置项,默认不开启
- 调整画板缓存为默认不开启
- 优化代码
- 优化文档说明
- 更新示例项目并修改部分注释
## 1.5.13(2021-05-22)
本次更新:
- 优化文档说明
- 更新示例项目
## 1.5.12(2021-05-22)
本次更新:
- 新增配置项 `strokeColor` 奖品区块边框颜色
- 更新文档说明
## 1.5.11(2021-05-19)
本次更新:
- 新增`strMarginOutside`参数,用于设置奖品文字距离边缘的距离
- 修复奖品文字在某些情况下不是居中显示的问题
## 1.5.10(2021-05-19)
本次更新:
- 修复示例项目中权重值相同时的取值逻辑
## 1.5.9(2021-05-14)
本次更新:
- 调整代码,优化小程序端的展示
## 1.5.8(2021-05-12)
本次更新:
- 文档增加预警提示:不再维护非 `uni_modules` 模式下的版本
## 1.5.7(2021-05-12)
本次更新:
- 修复小程序平台奖品名称不清晰的问题
## 1.5.6(2021-03-18)
本次更新:
- 适配 uni_modules 插件模式

@ -0,0 +1,970 @@
<template>
<view class="almost-lottery">
<view class="almost-lottery__wrap" :style="{ width: lotterySize + 'rpx', height: lotterySize + 'rpx' }">
<view class="lottery-action" :style="{ width: actionSize + 'rpx', height: actionSize + 'rpx', left: canvasMarginOutside + 'rpx' }"></view>
<view class="str-margin-outside" :style="{ left: strMarginOutside + 'rpx' }"></view>
<view class="img-margin-str" :style="{ left: imgMarginStr + 'rpx' }"></view>
<view class="img-size" :style="{ width: imgWidth + 'rpx', height: imgHeight + 'rpx' }"></view>
<template v-if="lotteryImg">
<image
class="almost-lottery__bg"
mode="widthFix"
:src="lotteryBg"
:style="{
width: lotteryPxSize + 'px',
height: lotteryPxSize + 'px'
}"
></image>
<image
class="almost-lottery__canvas-img"
mode="widthFix"
:src="lotteryImg"
:style="{
width: canvasPxSize + 'px',
height: canvasPxSize + 'px',
transform: `rotate(${canvasAngle + targetAngle}deg)`,
transitionDuration: `${transitionDuration}s`
}"
></image>
<image
class="almost-lottery__action almost-lottery__action-bg"
mode="widthFix"
:src="actionBg"
:style="{
width: actionPxSize + 'px',
height: actionPxSize + 'px',
transform: `rotate(${actionAngle + targetActionAngle}deg)`,
transitionDuration: `${transitionDuration}s`
}"
@click="handleActionStart"
></image>
</template>
<!-- 正在绘制转盘时的提示文本 -->
<text class="almost-lottery__tip" v-else>{{ almostLotteryTip }}</text>
</view>
<!-- 为了兼容 app ctx.measureText 所需的标签 -->
<text class="almost-lottery__measureText" :style="{ fontSize: higtFontSize + 'px' }">{{ measureText }}</text>
<!-- #ifdef MP-ALIPAY -->
<canvas
:class="className"
:id="canvasId"
:width="higtCanvasSize"
:height="higtCanvasSize"
:style="{
width: higtCanvasSize + 'px',
height: higtCanvasSize + 'px'
}"
/>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<canvas
:class="className"
:canvas-id="canvasId"
:width="higtCanvasSize"
:height="higtCanvasSize"
:style="{
width: higtCanvasSize + 'px',
height: higtCanvasSize + 'px'
}"
/>
<!-- #endif -->
</view>
</template>
<script>
const systemInfo = uni.getSystemInfoSync()
import { getStore, setStore, clearStore, clacTextLen, downloadFile, pathToBase64 } from '@/uni_modules/almost-lottery/utils/almost-utils.js'
export default {
name: 'AlmostLottery',
props: {
// 稿
pixelRatio: {
type: Number,
default: 2
},
// canvas
canvasId: {
type: String,
default: 'almostLottery'
},
//
lotterySize: {
type: Number,
default: 600
},
//
actionSize: {
type: Number,
default: 200
},
// canvas
canvasMarginOutside: {
type: Number,
default: 90
},
//
prizeList: {
type: Array,
required: true,
validator: (value) => {
return value.length > 1
}
},
//
prizeIndex: {
type: Number,
required: true
},
//
colors: {
type: Array,
default: () => [
'#FFFFFF',
'#FFBF05'
]
},
//
lotteryBg: {
type: String,
default: '/uni_modules/almost-lottery/static/almost-lottery/almost-lottery__bg2x.png'
},
//
actionBg: {
type: String,
default: '/uni_modules/almost-lottery/static/almost-lottery/almost-lottery__action2x.png'
},
//
prizeNameDrawed: {
type: Boolean,
default: true
},
//
stroked: {
type: Boolean,
default: false
},
//
strokeColor: {
type: String,
default: '#FFBF05'
},
//
rotateType: {
type: String,
default: 'roulette'
},
// s
duration: {
type: Number,
default: 8
},
//
ringCount: {
type: Number,
default: 8
},
//
pointerPosition: {
type: String,
default: 'edge',
validator: (value) => {
return value === 'edge' || value === 'middle'
}
},
//
strDirection: {
type: String,
default: 'horizontal',
validator: (value) => {
return value === 'horizontal' || value === 'vertical'
}
},
//
strFontColors: {
type: Array,
default: () => [
'#FFBF05',
'#FFFFFF'
]
},
//
strFontSize: {
type: Number,
default: 24
},
//
strMarginOutside: {
type: Number,
default: 0
},
//
imgMarginStr: {
type: Number,
default: 60
},
//
strLineHeight: {
type: Number,
default: 1.2
},
//
strMaxLen: {
type: Number,
default: 12
},
//
strLineLen: {
type: Number,
default: 6
},
//
imgWidth: {
type: Number,
default: 50
},
//
imgHeight: {
type: Number,
default: 50
},
//
imgDrawed: {
type: Boolean,
default: true
},
//
successMsg: {
type: String,
default: '奖品准备就绪,快来参与抽奖吧'
},
//
failMsg: {
type: String,
default: '奖品仍在准备中,请稍后再来...'
},
//
canvasCached: {
type: Boolean,
default: false
}
},
data() {
return {
// className
className: 'almost-lottery__canvas',
// px
lotteryPxSize: 0,
// px
canvasPxSize: 0,
// px
actionPxSize: 0,
//
strMarginPxOutside: 0,
//
imgMarginPxStr: 0,
//
imgPxWidth: 0,
imgPxHeight: 0,
//
lotteryImg: '',
//
targetAngle: 0,
targetActionAngle: 0,
// s
transitionDuration: 0,
//
isRotate: false,
//
stayIndex: 0,
//
targetIndex: 0,
//
isCacheImg: false,
oldLotteryImg: '',
//
almostLotteryTip: '奖品准备中...',
// app measureText
// app 2.9.3 measureText
measureText: ''
}
},
computed: {
//
higtCanvasSize() {
return this.canvasPxSize * systemInfo.pixelRatio
},
//
higtFontSize() {
return (this.strFontSize / this.pixelRatio) * systemInfo.pixelRatio
},
//
higtHeightMultiple() {
return (this.strFontSize / this.pixelRatio) * this.strLineHeight * systemInfo.pixelRatio
},
// canvas
canvasAngle() {
let result = 0
let prizeCount = this.prizeList.length
let prizeClip = 360 / prizeCount
let diffNum = 90 / prizeClip
if (this.pointerPosition === 'edge' || this.rotateType === 'pointer') {
result = -(prizeClip * diffNum)
} else {
result = -(prizeClip * diffNum + prizeClip / 2)
}
return result
},
actionAngle() {
return 0
},
//
outsideRadius() {
return this.higtCanvasSize / 2
},
//
insideRadius() {
return 20 * systemInfo.pixelRatio
},
//
textRadius() {
return this.strMarginPxOutside * systemInfo.pixelRatio || (this.higtFontSize / 2)
},
//
textDistance() {
const textZeroY = Math.round(this.outsideRadius - (this.insideRadius / 2))
return textZeroY - this.textRadius
}
},
watch: {
//
prizeIndex(newVal, oldVal) {
if (newVal > -1) {
this.targetIndex = newVal
this.onRotateStart()
} else {
console.info('旋转结束,prizeIndex 已重置')
}
}
},
methods: {
//
onRotateStart() {
if (this.isRotate) return
this.isRotate = true
//
let prizeCount = this.prizeList.length
let baseAngle = 360 / prizeCount
let angles = 0
if (this.rotateType === 'pointer') {
if (this.targetActionAngle === 0) {
//
angles = (this.targetIndex - this.stayIndex) * baseAngle + baseAngle / 2 - this.actionAngle
} else {
//
//
angles = (this.targetIndex - this.stayIndex) * baseAngle
}
//
this.stayIndex = this.targetIndex
// 8
this.targetActionAngle += angles + 360 * this.ringCount
console.log('targetActionAngle', this.targetActionAngle)
} else {
if (this.targetAngle === 0) {
//
// 0°
// = 270 - (-) * - - canvas
angles = (270 - (this.targetIndex - this.stayIndex) * baseAngle - baseAngle / 2) - this.canvasAngle
} else {
//
//
angles = -(this.targetIndex - this.stayIndex) * baseAngle
}
//
this.stayIndex = this.targetIndex
// 8
this.targetAngle += angles + 360 * this.ringCount
}
//
let endTime = this.transitionDuration * 1000 + 100
let endTimer = setTimeout(() => {
clearTimeout(endTimer)
endTimer = null
this.isRotate = false
this.$emit('draw-end')
}, endTime)
let resetPrizeTimer = setTimeout(() => {
clearTimeout(resetPrizeTimer)
resetPrizeTimer = null
// prizeIndex
this.$emit('reset-index')
}, endTime + 50)
},
//
handleActionStart() {
if (!this.lotteryImg) return
if (this.isRotate) return
this.$emit('draw-start')
},
//
async onCreateCanvas() {
// canvas
const canvasId = this.canvasId
const ctx = uni.createCanvasContext(canvasId, this)
// canvas
let canvasW = this.higtCanvasSize
let canvasH = this.higtCanvasSize
//
let prizeCount = this.prizeList.length
let baseAngle = Math.PI * 2 / prizeCount
//
ctx.setFontSize(this.higtFontSize)
// 0°
//
for (let i = 0; i < prizeCount; i++) {
let prizeItem = this.prizeList[i]
//
let angle = i * baseAngle
//
ctx.save()
// x => x
// y => y
// radius =>
// startAngle =>
// endAngle =>
// anticlockwise() => true false
ctx.beginPath()
//
ctx.arc(canvasW * 0.5, canvasH * 0.5, this.outsideRadius, angle, angle + baseAngle, false)
//
ctx.arc(canvasW * 0.5, canvasH * 0.5, this.insideRadius, angle + baseAngle, angle, true)
//
if (this.colors.length === 2) {
ctx.setFillStyle(this.colors[i % 2])
} else {
ctx.setFillStyle(this.colors[i])
}
//
ctx.fill()
//
if (this.strFontColors.length === 1) {
ctx.setFillStyle(this.strFontColors[0])
} else if (this.strFontColors.length === 2) {
ctx.setFillStyle(this.strFontColors[i % 2])
} else {
ctx.setFillStyle(this.strFontColors[i])
}
//
if (this.stroked) {
//
ctx.setStrokeStyle(`${this.strokeColor}`)
//
ctx.stroke()
}
//
// (0,0)
let translateX = canvasW * 0.5 + Math.cos(angle + baseAngle / 2) * this.textDistance
let translateY = canvasH * 0.5 + Math.sin(angle + baseAngle / 2) * this.textDistance
ctx.translate(translateX, translateY)
//
let rewardName = this.strLimit(prizeItem.prizeName)
// rotate线
ctx.rotate(angle + (baseAngle / 2) + (Math.PI / 2))
//
if (this.strDirection === 'horizontal') {
//
if (rewardName && this.prizeNameDrawed) {
let realLen = clacTextLen(rewardName).realLen
let isLineBreak = realLen > this.strLineLen
if (isLineBreak) {
//
let firstText = ''
let lastText = ''
let firstCount = 0
for (let j = 0; j < rewardName.length; j++) {
firstCount += clacTextLen(rewardName[j]).byteLen
if (firstCount <= (this.strLineLen * 2)) {
firstText += rewardName[j]
} else {
lastText += rewardName[j]
}
}
rewardName = firstText + ',' + lastText
let rewardNames = rewardName.split(',')
//
for (let j = 0; j < rewardNames.length; j++) {
if (ctx.measureText && ctx.measureText(rewardNames[j]).width > 0) {
//
let tempStrSize = ctx.measureText(rewardNames[j])
let tempStrWidth = -(tempStrSize.width / 2).toFixed(2)
ctx.fillText(rewardNames[j], tempStrWidth, j * this.higtHeightMultiple)
} else {
this.measureText = rewardNames[j]
//
await this.$nextTick()
let textWidth = await this.getTextWidth()
let tempStrWidth = -(textWidth / 2).toFixed(2)
ctx.fillText(rewardNames[j], tempStrWidth, j * this.higtHeightMultiple)
// console.log(rewardNames[j], textWidth, j)
}
}
} else {
if (ctx.measureText && ctx.measureText(rewardName).width > 0) {
//
let tempStrSize = ctx.measureText(rewardName)
let tempStrWidth = -(tempStrSize.width / 2).toFixed(2)
ctx.fillText(rewardName, tempStrWidth, 0)
} else {
this.measureText = rewardName
//
await this.$nextTick()
let textWidth = await this.getTextWidth()
let tempStrWidth = -(textWidth / 2).toFixed(2)
ctx.fillText(rewardName, tempStrWidth, 0)
}
}
}
} else {
let rewardNames = rewardName.split('')
for (let j = 0; j < rewardNames.length; j++) {
if (ctx.measureText && ctx.measureText(rewardNames[j]).width > 0) {
//
let tempStrSize = ctx.measureText(rewardNames[j])
let tempStrWidth = -(tempStrSize.width / 2).toFixed(2)
ctx.fillText(rewardNames[j], tempStrWidth, j * this.higtHeightMultiple)
} else {
this.measureText = rewardNames[j]
//
await this.$nextTick()
let textWidth = await this.getTextWidth()
let tempStrWidth = -(textWidth / 2).toFixed(2)
ctx.fillText(rewardNames[j], tempStrWidth, j * this.higtHeightMultiple)
// console.log(rewardNames[j], textWidth, i)
}
}
}
//
if (this.imgDrawed && prizeItem.prizeImage && this.strDirection !== 'vertical') {
// App-Android webview Chrome84+ canvas uni.canvasToTempFilePath
// base64
// https://ask.dcloud.net.cn/question/103303
let reg = /^(https|http)/g
//
if (reg.test(prizeItem.prizeImage)) {
let platformTips = ''
// #ifdef APP-PLUS
platformTips = ''
// #endif
// #ifdef MP
platformTips = '需要处理好下载域名的白名单问题,'
// #endif
// #ifdef H5
platformTips = '需要处理好跨域问题,'
// #endif
console.warn(`###当前数据列表中的奖品图片为网络图片,${platformTips}开始尝试下载图片...###`)
let res = await downloadFile(prizeItem.prizeImage)
console.log('处理远程图片', res)
if (res.ok) {
let tempFilePath = res.tempFilePath
// #ifndef MP
prizeItem.prizeImage = await pathToBase64(tempFilePath)
// #endif
// #ifdef MP
prizeItem.prizeImage = tempFilePath
// #endif
} else {
this.handlePrizeImgSuc({
ok: false,
data: res.data,
msg: res.msg
})
}
} else {
console.log('处理非远程图片', prizeItem.prizeImage)
// #ifndef MP
if (prizeItem.prizeImage.indexOf(';base64,') === -1) {
prizeItem.prizeImage = await pathToBase64(prizeItem.prizeImage)
}
// #endif
}
let prizeImageX = -(this.imgPxWidth * systemInfo.pixelRatio / 2)
let prizeImageY = this.imgMarginPxStr * systemInfo.pixelRatio
let prizeImageW = this.imgPxWidth * systemInfo.pixelRatio
let prizeImageH = this.imgPxHeight * systemInfo.pixelRatio
ctx.drawImage(prizeItem.prizeImage, prizeImageX, prizeImageY, prizeImageW, prizeImageH)
}
ctx.restore()
}
//
ctx.draw(true, () => {
let drawTimer = setTimeout(() => {
clearTimeout(drawTimer)
drawTimer = null
// #ifdef MP-ALIPAY
ctx.toTempFilePath({
destWidth: this.higtCanvasSize,
destHeight: this.higtCanvasSize,
success: (res) => {
// console.log(res.apFilePath)
this.handlePrizeImg({
ok: true,
data: res.apFilePath,
msg: '画布导出生成图片成功'
})
},
fail: (err) => {
this.handlePrizeImg({
ok: false,
data: err,
msg: '画布导出生成图片失败'
})
}
})
// #endif
// #ifndef MP-ALIPAY
uni.canvasToTempFilePath({
canvasId: this.canvasId,
success: (res) => {
// H5 tempFilePath base64
// console.log(res.tempFilePath)
this.handlePrizeImg({
ok: true,
data: res.tempFilePath,
msg: '画布导出生成图片成功'
})
},
fail: (err) => {
this.handlePrizeImg({
ok: false,
data: err,
msg: '画布导出生成图片失败'
})
}
}, this)
// #endif
}, 500)
})
},
//
handlePrizeImg(res) {
if (res.ok) {
let data = res.data
if (!this.canvasCached) {
this.lotteryImg = data
this.handlePrizeImgSuc(res)
return
}
// #ifndef H5
if (this.isCacheImg) {
uni.getSavedFileList({
success: (sucRes) => {
let fileList = sucRes.fileList
// console.log('getSavedFileList Cached', fileList)
let cached = false
if (fileList.length) {
for (let i = 0; i < fileList.length; i++) {
let item = fileList[i]
if (item.filePath === data) {
cached = true
this.lotteryImg = data
console.info('经查,本地缓存中存在的转盘图可用,本次将不再绘制转盘')
this.handlePrizeImgSuc(res)
break
}
}
}
if (!cached) {
console.info('经查,本地缓存中存在的转盘图不可用,需要重新初始化转盘绘制')
this.initCanvasDraw()
}
},
fail: (err) => {
this.initCanvasDraw()
}
})
} else {
uni.saveFile({
tempFilePath: data,
success: (sucRes) => {
let filePath = sucRes.savedFilePath
// console.log('saveFile', filePath)
setStore(`${this.canvasId}LotteryImg`, filePath)
this.lotteryImg = filePath
this.handlePrizeImgSuc({
ok: true,
data: filePath,
msg: '画布导出生成图片成功'
})
},
fail: (err) => {
this.handlePrizeImg({
ok: false,
data: err,
msg: '画布导出生成图片失败'
})
}
})
}
// #endif
// #ifdef H5
setStore(`${this.canvasId}LotteryImg`, data)
this.lotteryImg = data
this.handlePrizeImgSuc(res)
// console info
let consoleText = this.isCacheImg ? '缓存' : '导出'
console.info(`当前为 H5 端,使用${consoleText}中的 base64 图`)
// #endif
} else {
console.error('处理导出的图片失败', res)
uni.hideLoading()
// #ifdef H5
console.error('###当前为 H5 端,下载网络图片需要后端配置允许跨域###')
// #endif
// #ifdef MP
console.error('###当前为小程序端,下载网络图片需要配置域名白名单###')
// #endif
}
},
//
handlePrizeImgSuc (res) {
uni.hideLoading()
this.$emit('finish', {
ok: res.ok,
data: res.data,
msg: res.ok ? this.successMsg : this.failMsg
})
this.almostLotteryTip = res.ok ? this.successMsg : this.failMsg
},
// app ctx.measureText
// 2s
// hbx 2.8.12+ app
getTextWidth() {
console.warn('正在采用兼容方式获取文本的 size 信息,虽然没有任何问题,如果可以,请将此 systemInfo 及 hbx 版本号 反馈给作者', systemInfo)
let query = uni.createSelectorQuery().in(this)
let nodesRef = query.select('.almost-lottery__measureText')
return new Promise((resolve, reject) => {
nodesRef.fields({
size: true,
}, (res) => {
resolve(res.width)
}).exec()
})
},
//
strLimit(value) {
let maxLength = this.strMaxLen
if (!value || !maxLength) return value
return clacTextLen(value).realLen > maxLength ? value.slice(0, maxLength - 1) + '..' : value
},
//
checkCacheImg () {
console.log('检查本地缓存中是否存在转盘图')
//
//
this.oldLotteryImg = getStore(`${this.canvasId}LotteryImg`)
let oldPrizeList = getStore(`${this.canvasId}PrizeList`)
let newPrizeList = JSON.stringify(this.prizeList)
if (this.oldLotteryImg) {
if (oldPrizeList === newPrizeList) {
console.log(`经查,本地缓存中存在转盘图 => ${this.oldLotteryImg}`)
this.isCacheImg = true
console.log('需要继续判断这张缓存图是否可用')
this.handlePrizeImg({
ok: true,
data: this.oldLotteryImg,
msg: '画布导出生成图片成功'
})
return
}
}
console.log('经查,本地缓存中不存在转盘图')
this.initCanvasDraw()
},
//
initCanvasDraw () {
console.log('开始初始化转盘绘制')
this.isCacheImg = false
this.lotteryImg = ''
clearStore(`${this.canvasId}LotteryImg`)
setStore(`${this.canvasId}PrizeList`, this.prizeList)
this.onCreateCanvas()
},
//
async beforeInit () {
// rpx
let lotterySize = await new Promise((resolve) => {
uni.createSelectorQuery().in(this).select('.almost-lottery__wrap').boundingClientRect((rects) => {
resolve(rects)
// console.log(' lottery rpx ', rects)
}).exec()
})
let actionSize = await new Promise((resolve) => {
uni.createSelectorQuery().in(this).select('.lottery-action').boundingClientRect((rects) => {
resolve(rects)
// console.log(' action rpx ', rects)
}).exec()
})
let strMarginSize = await new Promise((resolve) => {
uni.createSelectorQuery().in(this).select('.str-margin-outside').boundingClientRect((rects) => {
resolve(rects)
// console.log(' str-margin-outside rpx ', rects)
}).exec()
})
let imgMarginStr = await new Promise((resolve) => {
uni.createSelectorQuery().in(this).select('.img-margin-str').boundingClientRect((rects) => {
resolve(rects)
// console.log(' img-margin-str rpx ', rects)
}).exec()
})
let imgSize = await new Promise((resolve) => {
uni.createSelectorQuery().in(this).select('.img-size').boundingClientRect((rects) => {
resolve(rects)
// console.log(' img-size rpx ', rects)
}).exec()
})
this.lotteryPxSize = Math.floor(lotterySize.width)
this.actionPxSize = Math.floor(actionSize.width)
this.canvasPxSize = this.lotteryPxSize - Math.floor(actionSize.left) + Math.floor(lotterySize.left)
this.strMarginPxOutside = Math.floor(strMarginSize.left) - Math.floor(lotterySize.left)
this.imgMarginPxStr = Math.floor(imgMarginStr.left) - Math.floor(lotterySize.left)
this.imgPxWidth = Math.floor(imgSize.width)
this.imgPxHeight = Math.floor(imgSize.height)
let stoTimer = setTimeout(() => {
clearTimeout(stoTimer)
stoTimer = null
//
if (this.canvasCached) {
this.checkCacheImg()
} else {
this.initCanvasDraw()
}
this.transitionDuration = this.duration
}, 50)
}
},
mounted() {
this.$nextTick(() => {
let stoTimer = setTimeout(() => {
clearTimeout(stoTimer)
stoTimer = null
this.beforeInit()
}, 50)
})
}
}
</script>
<style lang="scss" scoped>
.almost-lottery {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.almost-lottery__wrap {
position: relative;
// background-color: red;
}
.lottery-action,
.str-margin-outside,
.img-margin-str,
.img-size {
position: absolute;
left: 0;
top: 0;
z-index: -1;
// background-color: blue;
}
.almost-lottery__wrap {
display: flex;
justify-content: center;
align-items: center;
}
.almost-lottery__action,
.almost-lottery__bg,
.almost-lottery__canvas {
position: absolute;
}
.almost-lottery__canvas {
left: -9999px;
opacity: 0;
display: flex;
justify-content: center;
align-items: center;
}
.almost-lottery__canvas-img,
.almost-lottery__action-bg {
display: block;
transition: transform cubic-bezier(.34, .12, .05, .95);
}
.almost-lottery__tip {
color: #FFFFFF;
font-size: 24rpx;
text-align: center;
}
.almost-lottery__measureText {
position: absolute;
left: 0;
top: 0;
white-space: nowrap;
font-size: 12px;
opacity: 0;
}
</style>

@ -0,0 +1,78 @@
{
"id": "almost-lottery",
"displayName": "Almost-Lottery抽奖转盘",
"version": "1.8.5",
"description": "【荣获2021插件大赛三等奖】提供奇数、缓存等众多配置项,更有抽奖概率、抽奖次数、付费抽奖等功能内置于示例项目中,完美支持APP、各平台小程序、H5、PC,同时提供 uniCloud 云端版本",
"keywords": [
"转盘",
"抽奖",
"大转盘抽奖"
],
"repository": "https://github.com/ialmost/almost-components_uniapp",
"engines": {
"HBuilderX": "^3.1.22"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

@ -0,0 +1,159 @@
# almost-lottery
*使用 Canvas 绘制的抽奖转盘,提供奇数、缓存等众多配置项,更有抽奖概率、抽奖次数、付费抽奖等功能内置于示例项目中*
> <br />
>
> 如果用着还行,请支持一下
> - 前往 [GitHub](https://github.com/ialmost/almost-components_uniapp) 给个 Star
> - 前往 [UniApp](https://ext.dcloud.net.cn/plugin?id=1030) 给个五星
> - 使用中遇到问题时,可以添加 **QQ群 20441313**
>
> <br />
## 基于 uniCloud 开发的云端 Almost-Lottery 抽奖转盘,欢迎尝试体验
- [Almost-Lottery抽奖转盘的云端一体页面](https://ext.dcloud.net.cn/plugin?id=5763)
- [Almost-Lottery抽奖转盘的配置中心](https://ext.dcloud.net.cn/plugin?id=5762)
## 高能预警
- 本插件仅支持 `uni_modules` 模式,强烈推荐使用该模式,**非 `uni_modules` 模式不再维护**
- 在使用本插件之前,强烈建议使用 `HBuilderX` 导入示例项目验证可用性并参照修改
## 功能概要
- [x] 可配置奖品文字 **支持横向/竖向展示**
- [x] 可配置每个奖品区块的背景颜色
- [x] 可配置每个奖品区块的奖品文字颜色
- [x] 可配置奖品区块是否开启描边以及边框的颜色,默认不开启
- [x] 可配置转盘外环和抽奖按钮图
- [x] 可配置每个奖品区块的奖品图片,**当图片是网络地址时,小程序端需要配置白名单,H5端需要允许跨域,奖品文字为竖向时不支持展示奖品图片**
- [x] 奖品列表支持奇数,**奇数时需尽量能被 `360` 除尽**
- [x] 可配置内圈与外圈的间距
- [x] 可配置轮盘旋转或指针旋转
- [x] 可配置画板是否缓存,默认不开启
- [x] 更多配置请查看API说明
## 示例项目附加功能
- [x] 中奖概率,**强烈推荐中奖概率应由后端控制**
- [x] 抽奖次数
- [x] 付费抽奖
## 注意事项
- 编译到小程序端时,请务必勾选ES6转ES5
- `@reset-index="prizeIndex = -1"` 必须默认写入到 `template` 中,不可删除
- 每个奖品区块的奖品图片尺寸不宜过大,图片越大,绘制的过程越慢,尽量将图片尺寸控制在 `100*100` 以内,且图片大小控制在 `40KB` 以内
- 关于中奖概率的配置,请下载示例项目,参照 `pages/index/index.vue` 中的代码进行配置
- 组件本身不涉及任何业务逻辑,与业务相关的代码建议都放在 `pages/index/index.vue`
## 代码演示
#### 基础用法
```
// template
// @reset-index="prizeIndex = -1" 必须默认写入到 template 中,不可删除
<almost-lottery
:prizeList="prizeList"
:prizeIndex="prizeIndex"
@reset-index="prizeIndex = -1"
@draw-start="handleDrawStart"
@draw-end="handleDrawEnd"
@finish="handleDrawFinish"
v-if="prizeList.length"
/>
// script
import AlmostLottery from '@/uni_modules/almost-lottery/components/almost-lottery/almost-lottery.vue'
export default {
components: {
AlmostLottery
},
data () {
return {
// 以下是奖品配置数据
// 奖品数据
prizeList: [],
// 中奖下标
prizeIndex: -1
}
},
methods: {
// 本次抽奖开始
handleDrawStart () {
// 这里需要处理你的中奖逻辑,并得出 prizeIndex
// 请查看示例项目中的代码
},
// 本次抽奖结束
handleDrawEnd () {
// 完成抽奖后,这里处理你拿到结果后的逻辑
// 请查看示例项目中的代码
},
// 抽奖转盘绘制完成
handleDrawFinish (res) {
// 抽奖转盘准备就绪后,这里处理你的逻辑
// 请查看示例项目中的代码
// console.log('抽奖转盘绘制完成', res)
}
}
}
```
## API
#### Props
参数 | 说明 | 类型 | 默认值
:---|:---|:---|:---
pixelRatio | 移动端设计稿的像素比基准值,**涉及到 `rpx` 的适配问题** | *`Number`* | `2`
canvasId | Canvas的标识,**多画板情况下需要配置不同的标识** | *`String`* | `'almostLottery'`
lotterySize | 抽奖转盘的整体尺寸,单位 `rpx` | *`Number`* | `600`
actionSize | 抽奖按钮的尺寸,单位 `rpx` | *`Number`* | `200`
canvasMarginOutside | Canvas边缘距离转盘边缘的距离,单位`rpx` | *`Number`* | `90`
prizeIndex | 获奖奖品在奖品列表中的序号,**每次抽奖结束后会自动重置为 `-1`** | *`Number`* | `-1`
prizeList | 奖品列表,支持奇数(尽量能被 `360` 除尽),**为奇数时需要重设 `colors` 参数** | *`Array`* | -
lotteryBg | 转盘外环图片 | `String` | `默认内置的本地图片`
actionBg | 抽奖按钮图片 | `String` | `默认内置的本地图片`
colors | 奖品区块对应的背景颜色,默认 2 个颜色相互交替,**也可以对每个区块设置不同颜色** | *`Array`* | `['#FFFFFF', '#FFBF05']`
prizeNameDrawed | 是否绘制奖品名称 | *`Boolean`* | `true`
stroked | 是否开启奖品区块描边 | *`Boolean`* | `false`
strDirection | 奖品名称展示方向,可选值 `'horizontal'` => 横向 `'vertical'` => 竖向 | *`String`* | `'horizontal'`
strokeColor | 奖品区块边框颜色 | *`String`* | `'#FFBF05'`
rotateType | 旋转的类型,可选值 `'roulette'` => 轮盘旋转 `'pointer'` => 指针旋转 | *`String`* | `'roulette'`
duration | 转盘旋转的动画时长,单位:秒 | *`Number`* | `8`
ringCount | 旋转的圈数 | *`Number`* | `8`
pointerPosition | 点击抽奖按钮指针的位置,可选值 `'edge'` => 指向边界 `'middle'` => 指向中间 | *`String`* | `'edge'`
strFontColors | 奖品文字颜色,默认 2 个颜色相互交替,**也可以对每个区块的文字设置不同颜色,或仅设置一个颜色** | *`Array`* | `['#FFBF05', '#FFFFFF']`
strFontSize | 奖品名称的字号,单位 `rpx` | *`Number`* | `24`
strLineHeight | 奖品名称多行情况下的行高 | *`Number`* | `1.2`
strMaxLen | 奖品名称长度限制,**文字竖向时不生效** | *`Number`* | `12`
strLineLen | 奖品名称在多行情况下第一行文字的长度,**文字竖向时不生效** | *`Number`* | `6`
strMarginOutside | 奖品文字相对轮盘边缘的距离,单位 `rpx` | *`Number`* | `strFontSize 的一半`
imgMarginStr | 奖品图片相对奖品文字的距离,单位 `rpx` | *`Number`* | `60`
imgWidth | 奖品图片的宽度,单位 `rpx` | *`Number`* | `50`
imgHeight | 奖品图片的高度,单位 `rpx` | *`Number`* | `50`
imgDrawed | 是否绘制奖品图片,默认绘制 | *`Boolean`* | `true`
successMsg | 转盘绘制成功的提示 | *`String`* | `'奖品准备就绪,快来参与抽奖吧'`
failMsg | 转盘绘制失败的提示 | *`String`* | `'奖品仍在准备中,请稍后再来...'`
canvasCached | 是否开启缓存,避免在数据不变的情况下重复绘制,建议在生产环境中开启 | *`Boolean`* | `false`
#### Events
事件名 | 说明 | 回调参数
:---|:---|:---
@reset-index | 每次抽奖结束后重置获奖的序号为 `-1`,**该事件必须默认写入到 `template` 中,不可删除** | -
@draw-start | 转盘旋转开始时触发 | -
@draw-end | 转盘旋转结束时触发 | -
@finish | Canvas转盘绘制完成时触发 | `{ ok: 绘制是否成功, data: 转盘的图片, msg: 绘制结果的提示 }`
#### prizeList 数据结构
*请按如下数据字段对你对奖品列表数据结构进行调整*
键名 | 说明 | 类型
:---|:---|:---
prizeId | 奖品对应 `ID` | *`Number`*
prizeName | 奖品名称 | *`String`*
prizeStock | 奖品库存 | *`Number`*
prizeWeight | 奖品权重 | *`Number`*
prizeImage | 奖品图片地址,网络图片仅支持`http`和`https`协议 | *`String`*

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -0,0 +1,303 @@
/**
* 存储 localStorage 数据
* @param {String} name - 缓存数据的标识
* @param {any} content - 缓存的数据内容
*/
export const setStore = (name, content) => {
if (!name) return
if (typeof content !== 'string') {
content = JSON.stringify(content)
}
uni.setStorageSync(name, content)
}
/**
* 获取 localStorage 数据
* @param {String} name - 缓存数据的标识
*/
export const getStore = (name) => {
if (!name) return
return uni.getStorageSync(name)
}
/**
* 清除 localStorage 数据
* @param {String} name - 缓存数据的标识
*/
export const clearStore = (name) => {
if (name) {
uni.removeStorageSync(name)
} else {
console.log('清理本地全部缓存')
uni.clearStorageSync()
}
}
/**
* 计算文本的长度
* @param {String} text - 文本内容
*/
export const clacTextLen = (text) => {
if (!text) return { byteLen: 0, realLen: 0 }
text += ''
let clacLen = 0
for (let i = 0; i < text.length; i++) {
if ((text.charCodeAt(i) < 0) || (text.charCodeAt(i) > 255)) {
clacLen += 2
} else {
clacLen += 1
}
}
// console.log(`当前文本 ${text} 的长度为 ${clacLen / 2}`)
return {
byteLen: clacLen,
realLen: clacLen / 2
}
}
/**
* 下载文件并返回临时路径
* @return {String} 临时路径
* @param {String} fileUrl - 网络地址
*/
export const downloadFile = (fileUrl) => {
return new Promise((resolve) => {
uni.downloadFile({
url: fileUrl,
success: (res) => {
// #ifdef MP-ALIPAY
if (res.errMsg === 'downloadFile:ok') {
resolve({
ok: true,
data: res.errMsg,
tempFilePath: res.tempFilePath
})
} else {
resolve({
ok: false,
data: res.errMsg,
msg: '图片下载失败'
})
}
// #endif
// #ifndef MP-ALIPAY
if (res.statusCode === 200) {
resolve({
ok: true,
data: res.errMsg,
tempFilePath: res.tempFilePath
})
} else {
resolve({
ok: false,
data: res.errMsg,
msg: '图片下载失败'
})
}
// #endif
},
fail: (err) => {
resolve({
ok: false,
data: err.errMsg,
msg: '图片下载失败'
})
}
})
})
}
/**
* 清理应用已缓存的文件
*/
export const clearCacheFile = () => {
// #ifndef H5
uni.getSavedFileList({
success: (res) => {
let fileList = res.fileList
if (fileList.length) {
for (let i = 0; i < fileList.length; i++) {
uni.removeSavedFile({
filePath: fileList[i].filePath,
complete: () => {
console.log('清除缓存已完成')
}
})
}
}
},
fail: (err) => {
console.log('getSavedFileList Fail')
}
})
// #endif
// #ifdef H5
clearStore()
// #endif
}
// 图像转换工具,可用于图像和base64的转换
// https://ext.dcloud.net.cn/plugin?id=123
const getLocalFilePath = (path) => {
if (
path.indexOf('_www') === 0 ||
path.indexOf('_doc') === 0 ||
path.indexOf('_documents') === 0 ||
path.indexOf('_downloads') === 0
) return path
if (path.indexOf('/storage/emulated/0/') === 0) return path
if (path.indexOf('/storage/sdcard0/') === 0) return path
if (path.indexOf('/var/mobile/') === 0) return path
if (path.indexOf('file://') === 0) return path
if (path.indexOf('/') === 0) {
// ios 无法获取本地路径
let localFilePath = plus.os.name === 'iOS' ? path : plus.io.convertLocalFileSystemURL(path)
if (localFilePath !== path) {
return localFilePath
} else {
path = path.substring(1)
}
}
return '_www/' + path
}
export const pathToBase64 = (path) => {
return new Promise((resolve, reject) => {
if (typeof window === 'object' && 'document' in window) {
if (typeof FileReader === 'function') {
let xhr = new XMLHttpRequest()
xhr.open('GET', path, true)
xhr.responseType = 'blob'
xhr.onload = function() {
if (this.status === 200) {
let fileReader = new FileReader()
fileReader.onload = function(e) {
resolve(e.target.result)
}
fileReader.onerror = reject
fileReader.readAsDataURL(this.response)
}
}
xhr.onerror = reject
xhr.send()
return
}
let canvas = document.createElement('canvas')
let c2x = canvas.getContext('2d')
let img = new Image
img.onload = function() {
canvas.width = img.width
canvas.height = img.height
c2x.drawImage(img, 0, 0)
resolve(canvas.toDataURL())
canvas.height = canvas.width = 0
}
img.onerror = reject
img.src = path
return
}
if (typeof plus === 'object') {
let tempPath = getLocalFilePath(path)
plus.io.resolveLocalFileSystemURL(tempPath, (entry) => {
entry.file((file) => {
let fileReader = new plus.io.FileReader()
fileReader.onload = function(data) {
resolve(data.target.result)
}
fileReader.onerror = function(error) {
console.log(error)
reject(error)
}
fileReader.readAsDataURL(file)
}, (error) => {
reject(error)
})
}, (error) => {
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
wx.getFileSystemManager().readFile({
filePath: path,
encoding: 'base64',
success: (res) => {
resolve('data:image/png;base64,' + res.data)
},
fail: (error) => {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}
export const base64ToPath = (base64) => {
return new Promise((resolve, reject) => {
if (typeof window === 'object' && 'document' in window) {
base64 = base64.split(',')
let type = base64[0].match(/:(.*?);/)[1]
let str = atob(base64[1])
let n = str.length
let array = new Uint8Array(n)
while (n--) {
array[n] = str.charCodeAt(n)
}
return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], {
type: type
})))
}
let extName = base64.match(/data\:\S+\/(\S+);/)
if (extName) {
extName = extName[1]
} else {
reject(new Error('base64 error'))
}
let fileName = Date.now() + '.' + extName
if (typeof plus === 'object') {
let bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
let filePath = '_doc/uniapp_temp/' + fileName
bitmap.save(filePath, {}, () => {
bitmap.clear()
resolve(filePath)
}, (error) => {
bitmap.clear()
reject(error)
})
}, (error) => {
bitmap.clear()
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
let filePath = wx.env.USER_DATA_PATH + '/' + fileName
wx.getFileSystemManager().writeFile({
filePath: filePath,
data: base64.replace(/^data:\S+\/\S+;base64,/, ''),
encoding: 'base64',
success: () => {
resolve(filePath)
},
fail: (error) => {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}
Loading…
Cancel
Save