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源码如下:
1 <template> 2 <view class="content1"> 3 <canvas type="2D" canvas-id="canvas" id="canvas" :style="canvasStyle"> 4 5 </canvas> 6 <image :src="inLottery?'../../static/img/start_disabled.png':'../../static/img/start.png'" id="start" @tap="playReward"></image> 7 <view class="bottom1"> 8 <image src="../../static/img/xiaolian.png" class="smile"></image> 9 <view class="winner">{{winner}}</view> 10 </view> 11 12 </view> 13 </template>
本例Lottery控件的JavaScript源码如下:
1 <script> 2 var ctx = null; 3 export default { 4 props: { 5 lwidth: { 6 type: Number, 7 default: 100, 8 9 }, 10 lheight: { 11 type: Number, 12 default: 100 13 }, 14 inLottery: { 15 type: Boolean, 16 default: false 17 }, 18 19 parts: { 20 type: Array, 21 default: function() { 22 return [{ 23 id: 1, 24 name: '香蕉', 25 img: '', 26 color: "#AABBCC" 27 }, 28 { 29 id: 2, 30 name: '苹果', 31 img: '', 32 color: "#FFCCFF" 33 }, 34 { 35 id: 3, 36 name: '梨子', 37 img: '', 38 color: "#FFFFFF" 39 }, 40 { 41 id: 4, 42 name: '青瓜', 43 img: '', 44 color: "#FFCCFF" 45 }, 46 { 47 id: 5, 48 name: '番茄', 49 img: '', 50 color: "#FFFFFF" 51 } 52 ] 53 } 54 } 55 }, 56 data() { 57 return { 58 winner:"请抽奖" 59 }; 60 }, 61 computed: { 62 canvasStyle() { 63 return { 64 width: (this.lwidth) + "px", 65 height: (this.lheight) + "px" 66 }; 67 }, 68 69 }, 70 methods: { 71 initLottery:function(ctx,angleTo){ 72 73 const len = this.parts.length; //数组长度 74 if (len == 0) { 75 return; 76 } 77 var center_x = this.lwidth / 2; 78 var center_y = this.lheight / 2; 79 var total = 2 * Math.PI; //总度数为2π 80 var Angle = total / len; //平均值 81 var radius = center_x - 14; 82 center_x = center_x; 83 center_y = center_y; 84 angleTo = angleTo || 0; 85 ctx.clearRect(0,0, this.lwidth, this.lheight); 86 ctx.translate(center_x, center_y); 87 ctx.setFontSize(14); 88 ctx.setLineWidth(14); 89 ctx.save(); 90 //旋转画布 91 ctx.rotate(angleTo * Math.PI / 180); 92 // 93 var beginAngle = 2 * Math.PI / 360 * (-90); 94 //先画外圆 95 ctx.setStrokeStyle("#ffaa00"); 96 ctx.arc(0, 0, radius + 3, 0, Math.PI * 2); 97 ctx.stroke(); 98 //画装饰点 99 for (var i = 0; i < 24; i++) { 100 // 装饰点 圆心 坐标计算 101 ctx.beginPath(); 102 var r = radius + 6; 103 var xr = r * Math.cos(beginAngle); 104 var yr = r * Math.sin(beginAngle); 105 106 ctx.fillStyle = "#FFFFFF"; 107 ctx.arc(xr, yr, 4, 0, 2 * Math.PI); 108 ctx.fill(); 109 110 beginAngle += (2 * Math.PI / 360) * (360 / 24); 111 112 } 113 ctx.setLineWidth(0.1); 114 beginAngle = 2 * Math.PI / 360 * (-90); 115 //绘制填充形状 116 for (var i = 0; i < len; i++) { 117 // console.log("color = "+this.parts[i].color); 118 // console.log("beginAngle="+beginAngle); 119 ctx.save(); 120 ctx.beginPath(); 121 ctx.moveTo(0, 0); 122 ctx.setStrokeStyle(this.parts[i].color); 123 ctx.setFillStyle(this.parts[i].color); 124 125 ctx.arc(0, 0, radius, beginAngle, beginAngle + Angle, false); 126 //ctx.stroke(); 127 ctx.fill(); 128 ctx.save(); 129 beginAngle = beginAngle + Angle; 130 } 131 beginAngle = 0; //Angle / 2; 132 for (var i = 0; i < len; i++) { 133 var ry = -(center_x / 2) - 25; 134 //绘制旋转文字 135 ctx.rotate((beginAngle + (Angle * 0.5))); //顺时针旋转 136 ctx.setTextAlign("center"); 137 ctx.setFillStyle("#AA00CC"); 138 ctx.fillText(this.parts[i].name, 0, ry); 139 140 ctx.restore(); 141 beginAngle = beginAngle + Angle; 142 } 143 ctx.save(); 144 ctx.beginPath(); 145 ctx.arc(0, 0, 8, 0, Math.PI * 2); 146 ctx.setFillStyle("#FFFFFF"); 147 ctx.fill(); 148 ctx.draw(); 149 }, 150 playReward:function(){ 151 var len = this.parts.length; //数组长度 152 if (len == 0) { 153 return; 154 } 155 var angle = 360/len ; 156 var num =Math.floor(Math.random()*len); 157 //num= num%len; 158 angle = num * angle + angle / 2; 159 angle = angle || 0; 160 angle = 360-angle; 161 angle += 1440; 162 console.log("angle = "+angle +",num = "+num); 163 var that = this; 164 var count = 1; 165 // 基值(减速) 166 var baseStep = 50; 167 // 起始滚动速度 168 var baseSpeed =1; 169 var timer = setInterval(function(){ 170 console.log("count = "+count); 171 that.initLottery(that.ctx,count) ; 172 if (count == angle) { 173 clearInterval(timer); 174 that.winner = "当前奖品为:"+that.parts[num].name; 175 } 176 count = count + baseStep * (((angle - count) / angle) > baseSpeed ? baseSpeed : ((angle - count) / angle))+0.1; 177 if (angle - count < 0.5) { 178 count = angle; 179 } 180 },25); 181 } 182 }, 183 // 组件内么有onReady和onLoad等生命周期 184 mounted: function() { 185 this.ctx = uni.createCanvasContext("canvas"); 186 this.initLottery(this.ctx,0); 187 } 188 } 189 </script>
本例Lottery控件的CSS源码如下:
1 <style> 2 .content1 { 3 width: 100%; 4 height: 100%; 5 text-align: center; 6 display: flex; 7 flex-direction: column; 8 align-items: center; 9 position: relative; 10 } 11 12 #canvas { 13 left: 2rpx; 14 top: 2rpx; 15 } 16 17 #start { 18 position: absolute; 19 width: 110rpx; 20 height: 150rpx; 21 cursor: pointer; 22 top: 240rpx; 23 } 24 .bottom1{ 25 display: flex; 26 flex-direction: row; 27 justify-content: center; 28 justify-content: center; 29 } 30 .winner{ 31 height: 70rpx; 32 vertical-align: middle; 33 padding-top: 10rpx; 34 color: #FFFFFF; 35 } 36 .smile{ 37 width: 70rpx; 38 height: 70rpx; 39 } 40 </style>
本例index页面调用组件,代码如下:
1 <template> 2 <view class="content"> 3 <view class="top"></view> 4 <lottery class="lottery" :lwidth="lwidth" :lheight="lheight"></lottery> 5 <view class="bottom">Provider By Alan.hsiang</view> 6 </view> 7 </template> 8 9 <script> 10 import lottery from "@/components/Lottery/Lottery.vue" 11 export default { 12 components:{ 13 lottery 14 }, 15 data() { 16 return { 17 title: 'Hello', 18 lwidth:300, 19 lheight:300 20 } 21 }, 22 onLoad() { 23 24 }, 25 methods: { 26 27 } 28 } 29 </script> 30 31 <style> 32 .content { 33 display: flex; 34 flex-direction: column; 35 /* align-items: center; */ 36 /* justify-content: center; */ 37 background-image: url(../../static/img/bg.jpg); 38 background-position: center; 39 background-repeat: no-repeat; 40 background-size: cover; 41 height: 100%; 42 width: 100%; 43 } 44 .top{ 45 height: 35%; 46 width: 100%; 47 } 48 .lottery{ 49 /* position: absolute; */ 50 /* top: 200rpx; */ 51 /* bottom: 200rpx; */ 52 margin: 2rpx; 53 width: 100%; 54 height: 700rpx; 55 } 56 .bottom{ 57 position: absolute; 58 bottom: 10rpx; 59 color: #FFFFFF; 60 width: 100%; 61 text-align: center; 62 } 63 </style>
另外为了页面显示完整,需要在App.vue中定义页面显示100%,如下所示:
1 <script> 2 export default { 3 onLaunch: function() { 4 console.log('App Launch') 5 }, 6 onShow: function() { 7 console.log('App Show') 8 }, 9 onHide: function() { 10 console.log('App Hide') 11 } 12 } 13 </script> 14 15 <style> 16 /*每个页面公共css */ 17 uni-page-body,#app {width:100%;height: 100%;} 18 page{ 19 width: 100%; 20 height: 100%; 21 } 22 </style>
备注
八声甘州·对潇潇暮雨洒江天
【作者】柳永 【朝代】宋
【作者】柳永 【朝代】宋
对潇潇暮雨洒江天,一番洗清秋。
渐霜风凄紧,关河冷落,残照当楼。
是处红衰翠减,苒苒物华休。
惟有长江水,无语东流。
渐霜风凄紧,关河冷落,残照当楼。
是处红衰翠减,苒苒物华休。
惟有长江水,无语东流。
不忍登高临远,望故乡渺邈,归思难收。
叹年来踪迹,何事苦淹留?
想佳人、妆楼颙望,误几回、天际识归舟。
争知我,倚栏杆处,正恁凝愁。
叹年来踪迹,何事苦淹留?
想佳人、妆楼颙望,误几回、天际识归舟。
争知我,倚栏杆处,正恁凝愁。