OpenHarmony ArkUI+原生绘图之幸运大转盘

时间:2021-07-27 05:38:58

OpenHarmony ArkUI+原生绘图之幸运大转盘

效果展示

OpenHarmony ArkUI+原生绘图之幸运大转盘

此外,转盘的奖项的数量,内容都是可以变动的(菜单就是用来编辑奖项的,后续完善),如下:

OpenHarmony ArkUI+原生绘图之幸运大转盘

主要功能

  1. 实现转盘抽奖功能,可以设定中奖概率。
  2. 奖项的数量、内容可*设定。
  3. 原生html\css\js代码,没有使用资源文件,可复用。

设计时考虑到的问题

1.控件是使用现有图片还是通过CSS画出?

先是用的图片充当控件,考虑到奖项的内容可编辑性,还是老老实实画控件比较好。

2.每个奖项的概率如何设计?

先生成一个随机数,根据随机数取值大小,决定奖品内容。假设所有奖项的取值范围坐落到0100的数轴上,并且1号奖品的取值范围是010,2号:10~30, 3号:30~35,。。。通过设定每个奖项取值区间的大小来决定中奖的权重,这样就能控制中奖概率了。

3.如何实现奖项可编辑?

我将所有奖项存放在一个数据数组中,先能通过遍历数组中奖项信息,画出转盘,这是第一步。

之后,通过菜单功能提供一个列表控件,使其能够对数组中的信息进行增删改查,这是第二步。

在界面加载的onShow()函数中进行初始化,这样每次界面显示的时候就能更新转盘了。

具体代码

index.hml

  1. "container">
  2. "title"> 幸运大转盘
  3. "outer" id="outer">
  4. --画布-->
  5. "canvas" class="canvas">
  6. --内圆-->
  7. "circle">
  8. --长方形-->
  9. "rectangle">
  10. --正方形箭头-->
  11. "square">
  12. "btns">
  13. type="capsule" onclick="start"> 抽奖
  14. type="capsule" onclick="menu"> 菜单

outer就是转盘整体,包含转盘和箭头。我箭头是通过将圆+长方形+正方形平移、旋转组合而成的(虽然有点笨,没有想到其它办法)。转盘是一个画布canvas,通过移动画笔起点,旋转,一个扇区接一个扇区画出的。按键有两个,抽奖就是转动转盘,实现抽奖逻辑。菜单按键跳转到新的界面,实现奖项内容的编辑,当然还没写完。。。 

index.css

  1. .container {
  2. flex-direction: column;
  3. align-items: center;
  4. justify-content: space-between;
  5. }
  6. .title {
  7. font-size: 38px;
  8. font-weight: 600;
  9. height: 20%;
  10. }
  11. .outer {
  12. position: relative;
  13. }
  14. .canvas {
  15. width: 360px;
  16. height: 400px;
  17. }
  18. .circle {
  19. position: absolute;
  20. width: 40px;
  21. height: 40px;
  22. background-color: darkred;
  23. border-radius: 20px;
  24. transform: translate(160px,180px);
  25. }
  26. .rectangle {
  27. position: absolute;
  28. width: 20px;
  29. height: 40px;
  30. background-color: darkred;
  31. transform: translate(170px,150px);
  32. }
  33. .square {
  34. position:absolute;
  35. width: 20px;
  36. height: 20px;
  37. background-color: darkred;
  38. top: 140px;
  39. left: 170px;
  40. transform: rotate(45deg);
  41. }
  42. .btns {
  43. justify-content:space-around;
  44. }
  45. .button{
  46. margin-top: 10%;
  47. height: 10%;
  48. font-size: 30px;
  49. font-weight: 600;
  50. }

canvas中的宽、高决定了转盘大小,代码中将转盘的半径设置为画布一半宽的长度。同时,由于箭头是由圆、长方形、正方形平移旋转组成,那他们的偏移量、大小也是相对.canvas的属性取的,如果大小有变动需要调整。

为什么不将箭头也画出来?

如果将箭头也画在画布上,那么我不能实现转盘转动,箭头不动的动画了,画布是一个整体。

index.js

  1. import prompt from '@system.prompt';
  2. import router from '@system.router';
  3. export default {
  4. data: {
  5. //1.1创建奖项信息
  6. infoArr: [
  7. { name: '1号奖品' },
  8. { name: '2号奖品' },
  9. { name: '3号奖品' },
  10. { name: '4号奖品' },
  11. { name: '5号奖品' },
  12. { name: '6号奖品' },
  13. { name: '7号奖品' },
  14. { name: '未中奖' },
  15. ],
  16. //1.2画布大小
  17. circleHeight: 400,
  18. circleWidth: 360,
  19. //1.3扇区弧度
  20. arcAngle: 0,
  21. //1.4扇区角度
  22. jiaoDu: 0,
  23. //1.4动画参数
  24. animation: '',
  25. options: {
  26. duration: 5000,
  27. fill: 'forwards',
  28. easing: 'cubic-bezier(.2,.93,.43,1);',
  29. },
  30. },
  31. onShow() {
  32. const ca = this.$element('canvas');
  33. const ctx = ca.getContext('2d');
  34. //2.设定参数
  35. //2.1定义圆心,显示在画布中间
  36. var x0 = this.circleWidth * 0.5;
  37. var y0 = this.circleHeight * 0.5;
  38. //2.2定义半径
  39. var radius = this.circleWidth * 0.5;
  40. //2.3扇形弧度
  41. this.arcAngle = 360 / this.infoArr.length * Math.PI / 180;
  42. //2.4扇区角度
  43. this.jiaoDu = 360 / this.infoArr.length;
  44. //2.5定义起始弧度,箭头向上,初始度数需要-90deg
  45. var beginAngle = this.arcAngle * 0.5 - 90 * Math.PI / 180;
  46. //3.遍历,绘制扇区
  47. for (var i = 0; i < this.infoArr.length; i++) {
  48. //3.1结束弧度
  49. var endAngle = beginAngle + this.arcAngle;
  50. //3.2开启路径
  51. ctx.beginPath();
  52. //3.3起点
  53. ctx.moveTo(x0, y0);
  54. //3.4绘制扇区
  55. ctx.arc(x0, y0, radius, beginAngle, endAngle);
  56. //3.5设置颜色
  57. if (i == this.infoArr.length - 1) {
  58. ctx.fillStyle = '#2f4f4f'; //未中奖灰色
  59. } else if (i % 2) {
  60. ctx.fillStyle = '#ffa500';
  61. } else {
  62. ctx.fillStyle = '#ff4500';
  63. }
  64. //3.6填充颜色
  65. ctx.fill();
  66. //4.绘制文字
  67. //4.1文字弧度
  68. var textAngle = beginAngle + this.arcAngle * 0.5;
  69. var text = this.infoArr[i].name;
  70. //4.2文字坐标
  71. var textX = x0 + (radius * 2 / 3) * Math.cos(textAngle);
  72. var textY = y0 + (radius * 2 / 3) * Math.sin(textAngle);
  73. //4.3平移画布起点到文字位置
  74. ctx.translate(textX, textY);
  75. //4.4旋转画布
  76. ctx.rotate((this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
  77. //4.5设置文字字号和字体
  78. ctx.font = "25px '微软雅黑'";
  79. //4.6文字居中对齐
  80. ctx.textAlign = 'center';
  81. ctx.textBaseline = 'middle';
  82. //4.7绘制文字
  83. ctx.strokeText(text, 0, 0);
  84. //4.8还原旋转、平移,方便下次旋转
  85. ctx.rotate(-(this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
  86. ctx.translate(-textX, -textY);
  87. //5.更新起始弧度, 将当前扇形的结束弧度作为下一个扇形的起始弧度
  88. beginAngle = endAngle;
  89. }
  90. },
  91. start: function () {
  92. //6.旋转事件
  93. //6.1奖品总数
  94. let count = this.infoArr.length;
  95. //6.2生成随机数
  96. let randomNum = Math.floor(Math.random() * count);
  97. //6.3转动角度(+ 360*3)
  98. let deg = randomNum * this.jiaoDu + 360 * 3 + "deg";
  99. //6.4奖品名
  100. let index = count - randomNum - 1;
  101. let name = this.infoArr[index].name;
  102. console.log("name == " + name);
  103. //6.5动画帧
  104. var frames = [
  105. {
  106. transform: {
  107. rotate: '0deg'
  108. },
  109. },
  110. {
  111. transform: {
  112. rotate: deg
  113. },
  114. }
  115. ];
  116. //6.5动画绑定
  117. this.animation = this.$element('canvas').animate(frames, this.options);
  118. //6.6添加完成事件
  119. this.animation.onfinish = function () {
  120. if (randomNum % count) {
  121. prompt.showDialog({
  122. message: "恭喜抽中" + name + "!"
  123. });
  124. } else {
  125. prompt.showDialog({
  126. message: "下次再来!"
  127. });
  128. }
  129. };
  130. //6.7调用播放开始的方法
  131. this.animation.play();
  132. },
  133. menu: function () {
  134. router.push ({
  135. uri: 'pages/menuPage/menuPage',
  136. });
  137. },
  138. }

js中存放主要逻辑,所以对注释也比较详细。下面是个人踩坑中学习的点:

  1. //1.1创建奖项信息
  2. 可以增加减少奖项来预览将要实现的菜单功能,不要搞事情哈,奖项至少为1,代码中没有除0保护。
  3. //1.4动画参数
  4. duration是时长。easing,是描述动画的时间曲线,实现动画由快变慢。fill:forwards在动画结束后,目标将保留动画结束时的状态。
  5. //3.4绘制扇区
  6. x0, y0,扇区的起点坐标。radius,扇区半径。beginAngle,扇区起始的弧度,endAngle,扇区结束的弧度。
  7. //3.5设置颜色
  8. 每个扇区设置两个相间的颜色,未中奖特殊扇区用灰色调标识。
  9. //4.2文字坐标
  10. 由于文字在扇区中间,所以需要利用正弦余弦计算坐标,再进行画面旋转,才能调整正确的文字方向。
  11. //4.8还原旋转、平移,方便下次旋转
  12. translate函数是基于当前坐标进行偏移,旋转也是基于当前坐标进行旋转。所以当一个扇区的文字填写结束后,需要将坐标还原,这样才方便定位到一下处扇区位置。

原文链接: https://harmonyos.51cto.com