微信小程序canvas开发水果老虎机的思路详解

在这个超长假期中,无聊。。。,所以动手做一个早就计划要做的小玩意, 水果老虎机 ,嗯,这是一个小程序而不是小游戏...

使用结构还是canvas?

使用模板结构(view)生成水果盘的好处一是用户可自定义产出 n x n 的定制化老虎机,二是容易通过算法样式生成布局,三是通过 wx.selectQueryAll 的方法能够很方便的抓到定位数据。但,问题是动画性能过于孱弱,如图构建一个 7x7 的水果盘,动画性能估计会惨不忍睹,而且纯粹模板结构无论使用 animation 动画方法还是 css 的keyframe的动画方法得到的动画效果都非常差(测试过的结论),还有是已知的动画方法可控性很差

使用canvas来生成水果盘好处是动画性能很好(canvas2d),但是定制性和扩展性比较差

so综上考虑,使用模板(view)布局,使用canvas来实现动画。既保证了组件的性能,同时定制型,扩展性也很好

准备计时器方法

动画的生成离不开计时器方法,settimeout/setinterval这两兄弟真的不够看啊,问题还多,做过web开发的一定都知道 window.requestAnimationFrame ,这货在小程序的计时器方法中不存在,好在 canvas2d 中可以使用 Canvas.requestAnimationFrame(function callback) 方法来实现

准备运动算法

在水果老虎机中,激活状态会沿着四方的水果盘做非线性运动(easeInOut比较好用),需要基础的运动算法来计算实际的运动距离。在 animation 动画方法中,我们可以使用 ease-in/ease-out 等缓动算法来实现动画效果,但在这里必须要借助 tween.js 中的缓动算法来实现运动效果(因为需要控制运动节点)。

你会不会想到用css的keyframe动画来做这个运动效果,经过我的测试,css的动画和animation的动画会在每一条边上实现一次(ease)缓动运动(很奇怪的效果)

推荐这篇文章

使用其中一个,节省代码量

/*
 * Tween.js
 * t: current time(当前时间);
 * b: beginning value(初始值);
 * c: change in value(变化量);
 * d: duration(持续时间)。
 */
// Quart 四次方的缓动
const easeInOutQuart = function (t, b, c, d) {
 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}

tween算法是以时间为基准(时间比率 = 距离比率)来计算单位时间的实际运动距离

布局

以上面的图为例,我们需要做一个 7 x 7 的水果盘,实际有效的奖品格子数为 7*4-4 共24个有效格子

有效格子算法

js

// 0-6 第一行所有格子全部有效
// 21-27 最后一行所有格子全部有效
// 中间部分 i%7===0 和 i%7 === (7-1) 有效
// 算法源码有点无聊,依据上述思路,即可遍历28个格子并标识奖品格子valide=true
// 可以扩展想一想 6x6 5x5,思路是一样的

wxml

<view class="fruits-container" >
  <view class="fruits-table" >
    <block wx:for="{{ary}}" wx:key="index" >
      <view wx:if="{{item.valide}}" class="valide">{{item.title}}</view>
      <view wx:else class="in-valide"></view>
    </block>
  </view>
  <canvas type="2d" .... />
</view>

样式

只节选关键样式,目的是让canvas覆盖在水果盘上,长宽一致

.fruits-container {
  position: relative;
  width: 400px;
  height: 400px;
  ...
}

.fruits-table {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  ...
}

抓取位置信息

canvas的绘制需要X轴, Y轴的精确信息,可以使用 wx.createSelectorQuery 方式抓取类名为‘valide'的 view (奖品格子)的位置信息

let query = wx.createSelectorQuery().in(this)
query.selectAll(`.fruits-table .valide`).boundingClientRect(ret => {
  ....
  console.log(ret[0]) // top, left, right, bottom, width, height
  console.log(ret[1]) // top, left, right, bottom, width, height
  ...
  ...
  console.log(ret[23]) // top, left, right, bottom, width, height
})

得到每一个奖品格子的位置信息后,就可以使用canvas的 fillRect 方法来绘制激活状态了。

绘制一个激活状态

let query = wx.createSelectorQuery().in(this)
query.selectAll(`.fruits-table .valide`).boundingClientRect(ret => {
  ....
  let {top, left, right, bottom, width, height} = ret[0]
  const canvasQuery = wx.createSelectorQuery()
  canvasQuery.select('#fruit-canvas')
  .fields({ node: true, size: true })
  .exec((res) => {
    const canvas = res[0].node
    const ctx = canvas.getContext('2d')
    let x = top
    let y = left
    let dx = width
    let dy = height
    ctx.shadowOffsetX = 2
    ctx.shadowOffsetY = -2
    ctx.shadowColor = 'red'
    ctx.shadowBlur = 50
    ctx.lineWidth = 5
    ctx.strokeStyle = 'red'
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.strokeRect(x, y, dx, dy)
  })
})

跑起来

已经绘制了一个激活状态,接下来使它能够简单动起来

// 抽象激活方法
functon rect(point, canvas){
  let {x, y, dx, dy} = getPosition(point)
  ctx.shadowOffsetX = 2
  ctx.shadowOffsetY = -2
  ...
  ...
  ctx.clearRect(0, 0, canvas.width, canvas.height) // 擦除整个水果盘
  ctx.strokeRect(x, y, dx, dy) // 绘制激活区域
}

function run(){
  setTimeout(()=>{
    if (ret.length) {
      let point = ret.shift()
      rect(point, canvas)
      run()
    }
  }, 100)
}

执行run方法后可以看到水果盘的激活状态一步一步的往前走(100毫秒),拖拉机终于可以启动了

配上运动算法

经过上面的试验我们终于可以看到基本的运动效果了,接下来配上运动算法和计时器方法

// Quart 四次方的缓动
const easeInOutQuart = function (t, b, c, d) {
 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}

let start = 0 // 开始时间
let begin = 0 // 开始奖品位置
let end = 23 // 终点位置,这里跑一圈
let during = 5000 // 运动总时间

// 1000/60 ≈ 17,
// 17毫秒即表示屏幕60帧刷新率每秒 ≈ requestAnimationFrame计数频率(一般情况)
const steper = () => {
 // left为位移距离
 // 老虎机的运动位移是节点位移,不是精确位移
 // 所以这里用parseInt处理,只取整数部分
 // 数据变化为 0,1,2,3,4,5...23
 // 间隔时间/距离由easeInOutQuart算法计算
 var left = easeInOutQuart(start, begin, end, during);
 let idx = parseInt(left)
 start = start + 17;
 if (idx <= end) {
  let point = this.ret[idx] // 取节点位置信息
  this.rect(point) // 绘制
 }
 // 时间递增
 if (start <= during) {
  this.ctx.requestAnimationFrame(steper); // 计时器
 } else {
  // 动画结束,这里可以插入回调...
  // callback()...
 }
};
steper(); // 启动

总结

以上所述是小编给大家介绍的微信小程序canvas开发水果老虎机的思路详解,希望对大家有所帮助!

(0)

相关推荐

  • 详解微信小程序开发(项目从零开始)

    一.序 微信小程序,估计大家都不陌生,现在应用场景特别多.今天就系统的介绍一下小程序开发.注意,这里只从项目代码上做解析,不涉及小程序如何申请.打包.发布的东西.(这些跟着微信官方文档的流程走就好).好了废话不多说,看目录. 注: 小程序是一套特殊的东西,融合了原生和web端.他是一个不完整的浏览器对象,所以很多DOM . BOM 的东西无法使用,但是他又通过微信APP实现了多线程. 二.如何创建小程序 很简单,首先下载微信开发者工具,下载稳定版本的就好. 下载 然后,创建小程序,可以参考下述图

  • 微信小程序开发之tabbar图标和颜色的实现

    前期准备 :注册,填材料,验证等等:https://mp.weixin.qq.com 1.浏览一遍简易教程,下载相应的开发工具 写一个小例子 点击左侧的 "编辑"->点击右侧代码里的 app.json 修改为 { "pages":[ "pages/fightings/home", "pages/publish/home", "pages/mine/home" ], "tabBar"

  • 微信小程序开发实现消息推送

    微信小程序的消息推送简单的说就是发送一条微信通知给用户,用户点开消息可以查看消息内容,可以链接进入到小程序的指定页面. 微信小程序消息推送需要用户触发动作才能发送消息,比如用户提交订单.支付成功.一次只能发一条,当然可以通过某种方法发送多条,小的就不在这里赘述了.下面就介绍一下如何推送消息. 一.准备工作 首先,在微信公众平台开通消息推送功能,并添加消息模板.可以从模板库选择模板也可以创建一个模板,模板添加之后,模板ID我们接下来要用的. 发送模板消息需要用到accesstoken.formId

  • 使用微信小程序开发弹出框应用实例详解

    view class="container" class="zn-uploadimg"> <button type="primary"bindtap="showok">消息提示框</button> <button type="primary"bindtap="modalcnt">模态弹窗</button> <button typ

  • 详解如何在微信小程序开发中正确的使用vant ui组件

    微信小程序终于可以支持npm导入第三方库了(https://developers.weixin.qq....),但是这种导入模式和使用模式有别于我们使用的npm调用.今天我按照有赞新出的vant小程序ui库来讲解如何导入npm资源. 第一步: 在小程序工程的根目录下执行: npm i vant-weapp -S --production 第二步: 保证当前你的微信开发者工具是最新版本,然后点击执行"构建npm" 构建成功后会提示: 同时项目根目录中会多出一个目录"minipr

  • 在小程序开发中使用npm的方法

    微信小程序在 2.2.1 版本后增加了对 npm 包加载的支持,使得小程序支持使用 npm 安装第三方包. 1. 在小程序中加载 npm 包 npm install miniprogram-datepicker --production node_modules可以 在小程序根目录下,也可以存在于小程序根目录下的各个子目录中.但是不可以 在小程序根目录外.使用--production选项,可以减少安装一些业务无关的 npm 包,从而减少整个小程序包的大小. 2. 构建 npm 包 在微信小程序开

  • 微信小程序开发之点击按钮退出小程序的实现方法

    微信小程序官方是没有提供退出的API的,但是在navigator这个组件中,是有退出这个功能的: 详情参考官方文档:navigator. 示例代码: <navigator open-type="exit" target="miniProgram">关闭小程序</navigator> 不过这个功能最低支持版本时 2.1.0 ,如果一定要使用这个功能,那么最好在小程序管理后台中设置最低基础库版本不低于2.1.0,如果对小程序支持的最低基础库低于2

  • 微信小程序canvas开发水果老虎机的思路详解

    在这个超长假期中,无聊...,所以动手做一个早就计划要做的小玩意, 水果老虎机 ,嗯,这是一个小程序而不是小游戏... 使用结构还是canvas? 使用模板结构(view)生成水果盘的好处一是用户可自定义产出 n x n 的定制化老虎机,二是容易通过算法样式生成布局,三是通过 wx.selectQueryAll 的方法能够很方便的抓到定位数据.但,问题是动画性能过于孱弱,如图构建一个 7x7 的水果盘,动画性能估计会惨不忍睹,而且纯粹模板结构无论使用 animation 动画方法还是 css 的

  • 微信小程序换肤功能实现代码(思路详解)

    在手机.电脑使用频率如此高的当下,应用可以更换皮肤,以提升美观性,并减轻屏幕对眼睛的刺激,无疑对用户体验有很大的帮助 实现功能 要实现如上更换皮肤的效果,有几个思路: 1.准备皮肤相关的wxss,引入到app.wxss中,方便每个页面使用: 2.设置皮肤时,动态改变wxml中元素的类名或id,使页面应用对应的皮肤: 3.将选中皮肤的值保存在小程序本地缓存中,保证其他页面及下一次打开小程序时,页面展示正确的皮肤: 下面介绍一些实现的细节 wxml <view class="page"

  • 微信小程序的开发范式BeautyWe.js入门详解

    一个简单的介绍 BeautyWe.js 是什么? 它是一套专注于微信小程序的企业级开发范式,它的愿景是: 让企业级的微信小程序项目中的代码,更加简单.漂亮. 为什么要这样命名呢? Write beautiful code for wechat mini program by the beautiful we! 「We」 既是我们的 We,也是微信的 We,Both beautiful! 那么它有什么卖点呢? 专注于微信小程序环境,写原汁原味的微信小程序代码. 由于只专注于微信小程序,它的源码也很

  • 微信小程序wx.previewImage预览图片实例详解

    一.小知识 二.例子 1.wxml <span style="font-family:Comic Sans MS;font-size:18px;color:#333333;"><view class="container"> <view wx:for="{{imgalist}}" wx:for-item="image" class="previewimg"> <im

  • 微信小程序按钮点击跳转页面详解

    微信小程序中,按钮也是<button></button>标签,它通过bindtap属性绑定点击事件: 然后在js里面注册这个回调函数: 回调函数里面通过 wx.navigateTo({ url: '/pages/index/talkPage', }) 跳转到talkPage界面. 注意,html界面要在app.json里面注册: 不注册的话会报错:navigateTo:fail url "pages/index/talkPage" is not in app.j

  • 微信小程序 获取手机号 JavaScript解密示例代码详解

    当我们在开发微信小程序中,有一个常用的功能,就是获取用户的手机号,然后一键登入小程序,那么手机号如何获取呢?请认真看完本文,保证可以获取到用户的手机号. 刚开始开发微信小程序的时候,想着实现手机验证码登入,后来查阅资料得知,发给用户的短信是要自己付费的.后来想想,微信获取用户的手机号一样可以保证手机号码的真实性,因为手机号既然可以绑定微信,那么肯定是被严格核验过的,然后就开始了获取手机号之旅,网上教程有很多,但不知什么原因,都是会少一些内容,有的只有前端代码,没有后端:有的后端代码是PHP,不是

  • 微信小程序登录与注册功能的实现详解

    目录 小程序中的登录 用户注册 用户信息修改 总结 小程序中的登录 在小程序中有一个概念叫openid,这个相当于登录小程序用户的唯一标识,每个微信用户都不同.那要如何拿到用户的唯一标识呢?在微搭低代码中是通过调用系统的api来获取的 let userinfo = await app.utils.getWXContext() 我们通过这行代码的调用来获取到用户的唯一标识,获取到之后我们其他页面也需要使用,那就需要把他存起来.全局变量的作用域是所有页面都可见,所以我们需要在变量中定义一个全局变量叫

  • 微信小程序多表联合查询的实现详解

    目录 一对多表设计 SQL中的关联查询 低码中的表关联 自定义连接器中实现表关联查询 新建连接器 总结 一对一的设计一般不常见,只需要设计到主表中即可,避免增加复杂性.一对多的关系比较常见,一的一方通常作为主表,多的一方通常作为子表.而多对多一般会拆分成两个一对多的关系,这就必须要用中间表进行过渡. 我们本篇介绍的多表查询,侧重在一对多的关系,我们先看一下我们实际的表设计 一对多表设计 我们实现的是文章关注的业务,通常将文章作为主表,而关注信息作为子表.表和表之间要进行关联,常见的设计思路是子表

  • 微信小程序本作用域下调用全局JS详解及实例

    微信小程序本作用域下调用全局JS详解 本地wxml文件 <view> app版本:{{version}} </view> 本地js文件 var app; Page({ data:{ }, onLoad:function() { app = getApp(); this.setData({version:app.globalData.appName}); } }) 全局js文件 //app.js App({ globalData:{ appName:"hcoder"

  • 微信小程序 利用css实现遮罩效果实例详解

    微信小程序 利用css实现遮罩效果实例详解 实现效果图: 如图所示,使用css实现小程序的遮罩效果,代码如下 js文件代码: //index.js //获取应用实例 var app = getApp() Page({ data: { flag: false }, a: function(){ this.setData({flag: false}) }, b: function(){ this.setData({flag: true}) } }) wxss文件代码: .b1{position:fi

随机推荐