uni-app以其“一次开发,多端覆盖”的理念深得大家青睐,并且生态环境丰富,本文以一个幸运轮盘小例子,简述canvas的相关操作,仅供学习分享使用,如有不足之处,还请指正。
什么是canvas?
canvas又称画布,为uni-app提供自定义绘制的区域,通常用于图表或者图片的处理。在uni-app开发中,如果要在canvas中进行绘制,需要通过CanvasContext完成。
canvas相关知识点
canvas属性说明【canvas-id 在同一页面中不可重复】
CanvasContext的定义通过uni-app提供的API【uni.createCanvasContext(canvasId, this)】完成,如下所示:
在canvas上进行绘制,主要通过CanvasContex对象进行。关于CanvasContext的使用方法,可参考官方文档。
示例效果图
在Chrome浏览器上,如下所示:
在Android手机上,如下图所示:
核心源代码
在uni-app开发中,一个功能可以封装成一个控件,便于维护和调用。
本例Lottery控件的template源码如下:
<template>
<view class="content1">
<canvas type="2D" canvas-id="canvas" id="canvas" :style="canvasStyle">
</canvas>
<image :src="inLottery?'../../static/img/start_disabled.png':'../../static/img/start.png'" id="start" @tap="playReward"></image>
<view class="bottom1">
<image src="../../static/img/xiaolian.png" class="smile"></image>
<view class="winner">{{winner}}</view>
</view>
</view>
</template>
本例Lottery控件的JavaScript源码如下:
<script>
var ctx = null;
export default {
props: {
lwidth: {
type: Number,
default: 100,
},
lheight: {
type: Number,
default: 100
},
inLottery: {
type: Boolean,
default: false
},
parts: {
type: Array,
default: function() {
return [{
id: 1,
name: '香蕉',
img: '',
color: "#AABBCC"
},
{
id: 2,
name: '苹果',
img: '',
color: "#FFCCFF"
},
{
id: 3,
name: '梨子',
img: '',
color: "#FFFFFF"
},
{
id: 4,
name: '青瓜',
img: '',
color: "#FFCCFF"
},
{
id: 5,
name: '番茄',
img: '',
color: "#FFFFFF"
}
]
}
}
},
data() {
return {
winner:"请抽奖"
};
},
computed: {
canvasStyle() {
return {
width: (this.lwidth) + "px",
height: (this.lheight) + "px"
};
},
},
methods: {
initLottery:function(ctx,angleTo){
const len = this.parts.length; //数组长度
if (len == 0) {
return;
}
var center_x = this.lwidth / 2;
var center_y = this.lheight / 2;
var total = 2 * Math.PI; //总度数为2π
var Angle = total / len; //平均值
var radius = center_x - 14;
center_x = center_x;
center_y = center_y;
angleTo = angleTo || 0;
ctx.clearRect(0,0, this.lwidth, this.lheight);
ctx.translate(center_x, center_y);
ctx.setFontSize(14);
ctx.setLineWidth(14);
ctx.save();
//旋转画布
ctx.rotate(angleTo * Math.PI / 180);
//
var beginAngle = 2 * Math.PI / 360 * (-90);
//先画外圆
ctx.setStrokeStyle("#ffaa00");
ctx.arc(0, 0, radius + 3, 0, Math.PI * 2);
ctx.stroke();
//画装饰点
for (var i = 0; i < 24; i++) {
// 装饰点 圆心 坐标计算
ctx.beginPath();
var r = radius + 6;
var xr = r * Math.cos(beginAngle);
var yr = r * Math.sin(beginAngle);
ctx.fillStyle = "#FFFFFF";
ctx.arc(xr, yr, 4, 0, 2 * Math.PI);
ctx.fill();
beginAngle += (2 * Math.PI / 360) * (360 / 24);
}
ctx.setLineWidth(0.1);
beginAngle = 2 * Math.PI / 360 * (-90);
//绘制填充形状
for (var i = 0; i < len; i++) {
// console.log("color = "+this.parts[i].color);
// console.log("beginAngle="+beginAngle);
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.setStrokeStyle(this.parts[i].color);
ctx.setFillStyle(this.parts[i].color);
ctx.arc(0, 0, radius, beginAngle, beginAngle + Angle, false);
//ctx.stroke();
ctx.fill();
ctx.save();
beginAngle = beginAngle + Angle;
}
beginAngle = 0; //Angle / 2;
for (var i = 0; i < len; i++) {
var ry = -(center_x / 2) - 25;
//绘制旋转文字
ctx.rotate((beginAngle + (Angle * 0.5))); //顺时针旋转
ctx.setTextAlign("center");
ctx.setFillStyle("#AA00CC");
ctx.fillText(this.parts[i].name, 0, ry);
ctx.restore();
beginAngle = beginAngle + Angle;
}
ctx.save();
ctx.beginPath();
ctx.arc(0, 0, 8, 0, Math.PI * 2);
ctx.setFillStyle("#FFFFFF");
ctx.fill();
ctx.draw();
},
playReward:function(){
var len = this.parts.length; //数组长度
if (len == 0) {
return;
}
var angle = 360/len ;
var num =Math.floor(Math.random()*len);
//num= num%len;
angle = num * angle + angle / 2;
angle = angle || 0;
angle = 360-angle;
angle += 1440;
console.log("angle = "+angle +",num = "+num);
var that = this;
var count = 1;
// 基值(减速)
var baseStep = 50;
// 起始滚动速度
var baseSpeed =1;
var timer = setInterval(function(){
console.log("count = "+count);
that.initLottery(that.ctx,count) ;
if (count == angle) {
clearInterval(timer);
that.winner = "当前奖品为:"+that.parts[num].name;
}
count = count + baseStep * (((angle - count) / angle) > baseSpeed ? baseSpeed : ((angle - count) / angle))+0.1;
if (angle - count < 0.5) {
count = angle;
}
},25);
}
},
// 组件内么有onReady和onLoad等生命周期
mounted: function() {
this.ctx = uni.createCanvasContext("canvas");
this.initLottery(this.ctx,0);
}
}
</script>
本例Lottery控件的CSS源码如下:
<style>
.content1 {
width: 100%;
height: 100%;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
#canvas {
left: 2rpx;
top: 2rpx;
}
#start {
position: absolute;
width: 110rpx;
height: 150rpx;
cursor: pointer;
top: 240rpx;
}
.bottom1{
display: flex;
flex-direction: row;
justify-content: center;
justify-content: center;
}
.winner{
height: 70rpx;
vertical-align: middle;
padding-top: 10rpx;
color: #FFFFFF;
}
.smile{
width: 70rpx;
height: 70rpx;
}
</style>
本例index页面调用组件,代码如下:
<template>
<view class="content">
<view class="top"></view>
<lottery class="lottery" :lwidth="lwidth" :lheight="lheight"></lottery>
<view class="bottom">Provider By Alan.hsiang</view>
</view>
</template>
<script>
import lottery from "@/components/Lottery/Lottery.vue"
export default {
components:{
lottery
},
data() {
return {
title: 'Hello',
lwidth:300,
lheight:300
}
},
onLoad() {
},
methods: {
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
/* align-items: center; */
/* justify-content: center; */
background-image: url(../../static/img/bg.jpg);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 100%;
width: 100%;
}
.top{
height: 35%;
width: 100%;
}
.lottery{
/* position: absolute; */
/* top: 200rpx; */
/* bottom: 200rpx; */
margin: 2rpx;
width: 100%;
height: 700rpx;
}
.bottom{
position: absolute;
bottom: 10rpx;
color: #FFFFFF;
width: 100%;
text-align: center;
}
</style>
另外为了页面显示完整,需要在App.vue中定义页面显示100%,如下所示:
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
uni-page-body,#app {width:100%;height: 100%;}
page{
width: 100%;
height: 100%;
}
</style>
备注
八声甘州·对潇潇暮雨洒江天
【作者】柳永 【朝代】宋
对潇潇暮雨洒江天,一番洗清秋。
渐霜风凄紧,关河冷落,残照当楼。
是处红衰翠减,苒苒物华休。
惟有长江水,无语东流。
不忍登高临远,望故乡渺邈,归思难收。
叹年来踪迹,何事苦淹留?
想佳人、妆楼颙望,误几回、天际识归舟。
争知我,倚栏杆处,正恁凝愁。