123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- /**
- * ease:
- * 'linear' 动画从头到尾的速度是相同的
- * 'ease' 动画以低速开始,然后加快,在结束前变慢
- * 'ease-in' 动画以低速开始
- *
- * 'ease-in-out' 动画以低速开始和结束
- * 'ease-out' 动画以低速结束
- * 'step-start' 动画第一帧就跳至结束状态直到结束
- * 'step-end' 动画一直保持开始状态,最后一帧跳到结束状态
- */
- let config = {
- size: {
- width: '560rpx',
- height: '560rpx'
- }, // 转盘宽高
- bgColors: ['#FFC53F', '#FFED97'], // 转盘间隔背景色 支持多种颜色交替
- fontSize: 12, // 文字大小
- fontColor: '#C31A34', // 文字颜色
- nameMarginTop: 12, // 最外文字边距
- nameLength: 6, // 最外文字个数
- iconWidth: 32, // 图标宽度
- iconHeight: 32, // 图标高度
- iconAndTextPadding: 4, // 最内文字与图标的边距
- duration: 3000, // 转盘转动动画时长
- rate: 1.5, // 由时长s / 圈数得到
- border: 'border: 10rpx solid #FEFAE4;', // 转盘边框
- ease: 'ease-out' // 转盘动画
- };
- let preAngle = 0; // 上一次选择角度
- let preAngle360 = 0; // 上一次选择角度和360度之间的差
- let retryCount = 10; // 报错重试次数
- let retryTimer; // 重试setTimeout
- let drawTimer; // 绘制setTimeout
- Component({
- properties: {
- // 是否可用
- enable: {
- type: Boolean,
- value: true
- },
- // 数据
- gifts: {
- type: Array,
- value: []
- },
- // 中奖id
- prizeId: {
- type: String,
- value: ''
- },
- // 配置项 传入后和默认的配置进行合并
- config: {
- type: Object,
- value: {}
- },
- // 抽奖次数
- count: {
- type: Number,
- default: ""
- },
- },
- data: {
- lotteryCount: null,
- cost: null,
- turnCanvasInfo: { width: 0, height: 0 },
- size: config.size,
- giftModule: [],
- disable: false,
- canvasImgUrl: '',
- border: config.border,
- infos: []
- },
- methods: {
- async getCanvasContainerInfo(id) {
- return new Promise((resolve) => {
- const query = wx.createSelectorQuery().in(this);
- query.select(id).boundingClientRect(function (res) {
- const { width, height } = res;
- resolve({ width, height });
- }).exec();
- });
- },
- async init() {
- try {
- const info = await this.getCanvasContainerInfo('#turn');
- if (info.width && info.height) {
- this.setData({
- turnCanvasInfo: info
- });
- this.drawTurn();
- } else {
- wx.showToast({
- icon: 'nont',
- title: '获取转盘宽高失败'
- })
- }
- } catch (e) {
- if (retryCount <= 0) {
- return;
- }
- retryCount--;
- if (retryTimer) {
- clearTimeout(retryTimer);
- }
- retryTimer = setTimeout(async () => {
- await this.init();
- }, 100);
- }
- },
- drawTurn() {
- const turnCanvasInfo = this.data.turnCanvasInfo;
- const giftModule = this.properties.gifts;
- const ctx = wx.createCanvasContext('turn', this);
- // 计算没个扇区弧度
- const radian = Number((2 * Math.PI / giftModule.length).toFixed(2));
- // 绘制扇区并记录每个扇区信息
- const infos = this.drawSector(radian, giftModule, ctx, turnCanvasInfo);
- // 记录旋转角度
- this.recordTheRotationAngle(infos);
- // 绘制扇区文本及图片
- this.drawTextAndImage(giftModule, ctx, turnCanvasInfo, radian);
- ctx.draw(false, () => {
- this.saveToTempPath(turnCanvasInfo);
- });
- },
- saveToTempPath(turnCanvasInfo) {
- if (drawTimer) {
- clearTimeout(drawTimer);
- }
- drawTimer = setTimeout(() => {
- wx.canvasToTempFilePath({
- canvasId: 'turn',
- quality: 1,
- x: 0,
- y: 0,
- width: turnCanvasInfo.width,
- height: turnCanvasInfo.height,
- success: (res) => {
- this.setData({
- canvasImgUrl: res.tempFilePath
- });
- },
- fail: (error) => {
- console.log(error);
- }
- }, this);
- }, 500);
- },
- drawSector(radian, giftModule, ctx, turnCanvasInfo) {
- const halfRadian = Number((radian / 2).toFixed(2));
- let startRadian = -Math.PI / 2 - halfRadian;
- const angle = 360 / giftModule.length;
- const halfAngle = angle / 2;
- let startAngle = -90 - halfAngle;
- const infos = [];
- // 绘制扇形
- for (let i = 0; i < giftModule.length; i++) {
- // 保存当前状态
- ctx.save();
- // 开始一条新路径
- ctx.beginPath();
- ctx.moveTo(turnCanvasInfo.width / 2, turnCanvasInfo.height / 2);
- ctx.arc(turnCanvasInfo.width / 2, turnCanvasInfo.height / 2, turnCanvasInfo.width / 2, startRadian, startRadian + radian);
- if (giftModule[i].bgColor) {
- ctx.setFillStyle(giftModule[i].bgColor);
- } else {
- ctx.setFillStyle(config.bgColors[i % config.bgColors.length]);
- }
- ctx.fill();
- ctx.closePath();
- ctx.restore();
- infos.push({
- id: giftModule[i].objectId,
- angle: (startAngle + startAngle + angle) / 2
- });
- startRadian += radian;
- startAngle += angle;
- }
- return infos;
- },
- drawTextAndImage(giftModule, ctx, turnCanvasInfo, radian) {
- let startRadian = 0;
- // 绘制扇形文字和logo
- for (let i = 0; i < giftModule.length; i++) {
- // 保存当前状态
- ctx.save();
- // 开始一条新路径
- ctx.beginPath();
- ctx.translate(turnCanvasInfo.width / 2, turnCanvasInfo.height / 2);
- ctx.rotate(startRadian);
- ctx.translate(-turnCanvasInfo.width / 2, -turnCanvasInfo.height / 2);
- if (giftModule[i].fontSize) {
- ctx.setFontSize(giftModule[i].fontSize);
- } else {
- ctx.setFontSize(config.fontSize);
- }
- ctx.setTextAlign('center');
- if (giftModule[i].fontColor) {
- ctx.setFillStyle(giftModule[i].fontColor);
- } else {
- ctx.setFillStyle(config.fontColor);
- }
- ctx.setTextBaseline('top');
- if (giftModule[i].name) {
- ctx.fillText(giftModule[i].name, turnCanvasInfo.width / 2, config.nameMarginTop);
- }
- if (giftModule[i].subname) {
- ctx.fillText(giftModule[i].subname ? giftModule[i].subname : '', turnCanvasInfo.width / 2, config.nameMarginTop + config.fontSize + 2);
- }
- if (giftModule[i].imgUrl) {
- ctx.drawImage(giftModule[i].imgUrl,
- turnCanvasInfo.width / 2 - config.iconWidth / 2,
- config.nameMarginTop + config.fontSize * 2 + 2 + config.iconAndTextPadding,
- config.iconWidth, config.iconHeight);
- }
- ctx.closePath();
- ctx.restore();
- startRadian += radian;
- }
- },
- recordTheRotationAngle(infos) {
- for (let i = infos.length - 1; i >= 0; i--) {
- infos[i].angle -= infos[0].angle;
- infos[i].angle = 360 - infos[i].angle;
- }
- // 记录id及滚动的角度
- this.setData({
- infos: infos
- });
- },
- luckDrawHandle() {
- if (this.data.disable || !this.data.canvasImgUrl) {
- return;
- }
- this.setData({
- disable: true
- });
- console.log('开始抽奖')
- this.triggerEvent('LuckDraw');
- },
- startAnimation(angle) {
- if (this.data.lotteryCount - this.data.cost < 0) {
- this.setData({
- disable: false
- });
- this.triggerEvent('NotEnough', '积分不足!');
- return;
- }
- // 抽奖次数减一
- this.setData({
- lotteryCount: this.data.lotteryCount - this.data.cost
- });
- const currentAngle = preAngle;
- preAngle += Math.floor((config.duration / 1000) / config.rate) * 360 + angle + preAngle360;
- this.animate('#canvas-img', [
- { rotate: currentAngle, ease: 'linear' },
- { rotate: preAngle, ease: config.ease },
- ], config.duration, () => {
- this.setData({
- disable: false
- });
- preAngle360 = 360 - angle;
- this.triggerEvent('LuckDrawFinish');
- });
- },
- downloadHandle(url) {
- return new Promise((resolve, reject) => {
- wx.downloadFile({
- url: url, // 仅为示例,并非真实的资源
- success: (res) => {
- // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
- if (res.statusCode === 200) {
- resolve(res.tempFilePath);
- } else {
- reject();
- }
- },
- fail: () => {
- reject();
- }
- });
- });
- },
- async downloadImg(imgs) {
- let result;
- try {
- const downloadHandles = [];
- for (const url of imgs) {
- if (this.isAbsoluteUrl(url)) { // 是网络地址
- downloadHandles.push(this.downloadHandle(url));
- } else {
- downloadHandles.push(Promise.resolve(url));
- }
- }
- result = await Promise.all(downloadHandles);
- } catch (e) {
- console.log(e);
- result = [];
- }
- return result;
- },
- clearTimeout() {
- if (retryTimer) {
- clearTimeout(retryTimer);
- }
- if (drawTimer) {
- clearTimeout(drawTimer);
- }
- },
- isAbsoluteUrl(url) {
- return /(^[a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
- },
- async initData(data) {
- let name;
- let subname;
- let imgUrls = [];
- if (this.properties.config) {
- config = Object.assign(config, this.properties.config);
- }
- for (const d of data) {
- name = d.name;
- imgUrls.push(d.imgUrl);
- d.imgUrl = '';
- if (name.length > config.nameLength) {
- d.name = name.slice(0, config.nameLength);
- subname = name.slice(config.nameLength);
- if (subname.length > config.nameLength - 2) {
- d['subname'] = subname.slice(0, config.nameLength - 2) + '...';
- } else {
- d['subname'] = subname;
- /* console.log('是否开启了概率???', that.data.probability);
- //开启概率 probability这属性必须要传个ture
- if (that.data.probability) {
- r = that._openProbability();
- } */
- }
- }
- }
- imgUrls = await this.downloadImg(imgUrls);
- for (let i = 0; i < imgUrls.length; i++) {
- data[i].imgUrl = imgUrls[i];
- }
- this.setData({
- giftModule: data
- });
- await this.init();
- }
- },
- observers: {
- 'gifts': async function (gifts) {
- if (!gifts || !gifts.length) {
- return;
- }
- await this.initData(gifts);
- },
- 'enable': function (enable) {
- this.setData({
- disable: !enable
- });
- },
- 'prizeId': function (id) {
- if (!id) {
- this.setData({
- disable: false
- });
- return;
- }
- try {
- const infos = this.data.infos;
- console.log(infos, id)
- const info = infos.find((item) => item.id == id);
- console.log(info)
- this.startAnimation(info.angle);
- } catch (e) {
- this.setData({
- disable: false
- });
- }
- },
- 'count': function (lotteryCount) {
- console.log(lotteryCount)
- this.setData({
- lotteryCount
- });
- },
- // 'cost': function(cost) {
- // console.log(cost)
- // this.setData({
- // cost
- // });
- // },
- },
- lifetimes: {
- detached() {
- this.clearTimeout();
- }
- },
- pageLifetimes: {
- hide() {
- this.clearTimeout();
- }
- }
- });
|