小程序canvas大转盘

// luckRoll.wxml
<view class="canvas-container" style="transform: rotate({{isRotate}}deg)">
    <canvas disable-scroll="true" canvas-id='canvas' id="canvas-bg" class='canvas '></canvas>
    <image src="{{tempFilePath}}"></image>
</view>

需要定义的数据

// luckRoll.js
data: {
    trunBtn: false,//抽奖按钮是否可以点击
    itemsNum: 3, //大转盘等分数,可根据后台配置加载
    itemsArc: 0, //大转盘每等分角度
    coupons: [],//每个扇形中的文字填充
    isRotate: -180, // 初始旋转角度
}

创建一个canvas对象,因为它是个组件,所以在传入转盘数据的时候,初始化canvas

properties: {
    rollOptions: {
        type: Array,
        observer(val) {
            if(val.length > 0) {
                this.setData({
                    coupons: val,
                    itemsNum: val.length
                })
                const ctx = wx.createCanvasContext("canvas", this); //创建id为canvas的绘图
                this.getGiftList(ctx)
            }
        }
    }
},
getGiftList(ctx) {
    let that = this;
    let itemsArc = 360/that.data.itemsNum
    that.setData({
        itemsArc
    }, function () {
        const query = wx.createSelectorQuery().in(that)
        query.select('#canvas-bg').boundingClientRect()
        query.exec(function (rect) {
            // 外面定义的两个全局变量,用于存储画布的宽高
            w1 = parseInt(rect[0].width / 2);
            h1 = parseInt(rect[0].height / 2);
            that.drawRegion(itemsArc, ctx);//每一份扇形的内部绘制。
        })
    })
}

绘制扇形

drawRegion(e, ctx) {
    let that = this;
    let itemsArc = e;//每一份扇形的角度
    let num = that.data.itemsNum;//等分数量
    let itemArr = that.data.coupons.map(item=>item.name);//放文字的数组
    for (let i = 0; i < num; i++) {
        ctx.beginPath();
        ctx.moveTo(w1, h1);
        ctx.arc(w1, h1, w1 - 2, itemsArc * i * Math.PI / 180, (itemsArc + itemsArc * i) * Math.PI / 180);//绘制扇形,默认从第四象限开始画,所以区域编号1的地方为三点钟开始位置
        ctx.closePath();
        const colorList = ['#7cd8e3', '#ffffff']
        ctx.setFillStyle(colorList[i % 2]);
        ctx.fill();
        ctx.save();
        ctx.beginPath();
        ctx.translate(w1, h1);//将原点移至圆形圆心位置
        ctx.rotate((itemsArc * (i + 1 + (num - 2) * 0.25)) * Math.PI / 180);//旋转文字
        if (num >= 6) {
            ctx.setFontSize(18);//设置文字字号大小
        } else {
            ctx.setFontSize(20);//设置文字字号大小
        }
        if (i % 2 == 0) {
            ctx.setFillStyle("#ffffff");//设置文字颜色
        } else {
            ctx.setFillStyle("#7cd8e3");//设置文字颜色
        }
        ctx.setTextAlign("center");//使文字垂直居中显示
        ctx.setTextBaseline("middle");//使文字水平居中显示
        if (itemArr[i].length < 7) {
            ctx.setFontSize(12);//设置文字字号大小
            ctx.fillText(itemArr[i], 0, -(h1 * 0.75));
        } else if (itemArr[i].length >= 7 && itemArr[i].length <= 10) {
            let len = Math.ceil(itemArr[i].length / 2)
            ctx.fillText(itemArr[i].slice(0, len), 0, -(h1 * 0.80));
            ctx.fillText(itemArr[i].slice(len), 0, -(h1 * 0.65));
            ctx.setFontSize(20);//设置文字字号大小
        } else {
            let mainInfo = itemArr[i].slice(0, 10) + '...'
            ctx.fillText(mainInfo.slice(0, 6), 0, -(h1 * 0.80));
            ctx.fillText(mainInfo.slice(6, 13), 0, -(h1 * 0.65));
            ctx.setFontSize(20);//设置文字字号大小
        }
        ctx.restore();//保存绘图上下文,使上一个绘制的扇形保存住。
    }
    ctx.draw();
    setTimeout(()=>{
        wx.canvasToTempFilePath({
            x: 0,
            y: 0,
            width: 2 * w1,
            height: 2 * h1,
            destWidth: 8 * w1,
            destHeight: 8 * h1,
            fileType: 'jpg', 
            quality: 1,//图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
            canvasId: 'canvas',
            success: function (res) {
                var tempFilePath = res.tempFilePath;
                that.setData({
                    tempFilePath: res.tempFilePath
                })

            },
            fail: function (res) {
                console.log('----------  ', res)
            }
        }, that)
    },1000)
    ctx.draw(true);//参数为true的时候,保存当前画布的内容,继续绘制
}
wx.canvasToTempFilePath 特别重要,必须将canvas转换为图片在页面上渲染,不然会出现各种问题,canvas用translateY移出页面显示

另外wx.canvasToTempFilePath必须写在setTimeout里面,不然安卓机的图片是黑屏

上面转盘已经画成,下面就要转动起来

startRoll(index) {
    let that = this;
    let turntableRegionId = index+1
    let rotate = that.data.isRotate - that.data.isRotate % 360 + (720 - Number(turntableRegionId - 1) * that.data.itemsArc - 0.5 * that.data.itemsArc - 90)
    // 转动过程写在css动画里面,时长3s
    that.setData({
        isRotate: rotate + 2160
    })
    let cur = that.data.coupons[index]
    let isBingo = !!cur.isPrize
    let award = cur.name
    that.setData({
        isBingo: isBingo, // 是否中奖
        award: award // 奖品
    })
    setTimeout(()=>{
        //3S后显示最终结果弹窗
        that.setData({
            dialogShow: true
        })
    },3000)
}

其他细节,项目不同再另外写
核心css

.canvas-container {
    width: 550rpx;
    height: 550rpx;
    transition:all 3s ease;
    position: absolute;
    top: 45rpx;
    left: 110rpx;
    z-index: 1;
    border-radius: 50%;
    overflow: hidden;
}
.canvas{
    width: 550rpx;
    height: 550rpx;
    display: block;
    position: fixed;
    left: -5000px;
}

英目频道
名乎利乎,道路奔波休碌碌;来者往者,溪山清净且停停

路漫漫其修远兮,吾将上下而求索

67 声望
4 粉丝
0 条评论
推荐阅读
vue点击空白处
{代码...} 使用 {代码...}

yingmhd1阅读 1k

使用taro+canvas实现微信小程序的图片分享功能 | 京东云技术团队
二轮充电业务中,用户充电完成后在订单详情页展示订单相关信息,用户点击分享按钮唤起微信小程序分享菜单,将生成的图片海报分享给微信好友或者下载到本地,好友可通过扫描海报中的二维码加群领取优惠。

京东云开发者2阅读 427

封面图
小程序内参数和扫码参数统一
写小程序的时候都会遇到扫码参数和小程序内跳转参数要分开处理的问题,但实际上参数和处理的方式都是一样的,这里封装一种方法,将扫码参数直接放到options下面,这样就不需要分开独立处理,减少冗余代码增加代码...

海洋饼干1阅读 847

微信小程序归结
是的,在这个框架满天飞的年代,我既然有有幸使用了原生小程序开发项目,除了麻烦些,倒也不是一无所获,耕耘总有收货嘛,写博客本身不是为了炫技还是什么,单纯的是记性不好,有些知识点 自己是花了时间去查找的...

HappyCodingTop1阅读 1.3k

封面图
5 分钟带你小程序入门 [实战总结分享]
微信小程序常常用 4 种文件类型JS 文件JS 在小程序中用于编写页面逻辑和交互效果,可调用 API 接口完成数据请求和处理,也可以使用第三方库和框架。模块化编程:小程序中JS文件可以使用ES6的模块化语法,通过expo...

程序员海军2阅读 482

封面图
基于微信云开发 SayLove 表白墙微信小程序V1.0
后续会继续更新,敬请期待2.0全新版本~欢迎添加右边的微信一起探讨!项目地址:[链接][其他开源项目]租房小程序 [链接]计划助手 [链接]Bug修复更新日历[2021-05-12] 更新说明:【 课设毕设参考专用版本 】针对 昵...

LiangSenCheng阅读 2.1k

基于微信小程序云开发-租房微信小程序-带管理员后台
本项目使用 LGPL-3.0协议,请勿商用、请勿售卖、请勿售卖、请勿售卖,仅适用于学习交流,并且不提供无偿的、 不提供无偿的、 不提供无偿的 维护修改服务(但可提issue)。若直接将本项目用于商用,因本项目带来的...

LiangSenCheng阅读 1.8k评论 1

路漫漫其修远兮,吾将上下而求索

67 声望
4 粉丝
宣传栏