- 思 路
- 创建2个canvas,一个当作背景,一个当作拼图。
- 传入3个参数,x:canvas中裁剪区域的横坐标,y:canvas中裁剪区域的纵坐标,l:拼图的边长
- 背景图使用fill()的方法裁剪出一个洞
- 拼图使用wx.canvasToTempFilePath的方法裁剪成一个拼图图片。
- 小程序中的触控事件bindtouchmove,bindtouchend分别记录移动的距离和松手时的距离
- 判断移动距离和x的差距,如果两者小于一定阈值,则验证成功,反之失败。 - 开始操作
## 创建两个canvas
在微信小程序中,由于不能操作DOM,所以要想更改样式需要在组件中用style表明,在data中修改,通过{{}}渲染出来。比如我们需要控制canvas距离顶部的高度,除了在wxss中定义以外,还可以使用<canvas style='top:{{this.data.top}}rpx'>这种方式实现。小程序中操作创建canvas的方法如下:
思 路
- 创建2个canvas,一个当作背景,一个当作拼图。
- 传入3个参数,x:canvas中裁剪区域的横坐标,y:canvas中裁剪区域的纵坐标,l:拼图的边长
- 背景图使用fill()的方法裁剪出一个洞
- 拼图使用wx.canvasToTempFilePath的方法裁剪成一个拼图图片。
- 小程序中的触控事件bindtouchmove,bindtouchend分别记录移动的距离和松手时的距离
- 判断移动距离和x的差距,如果两者小于一定阈值,则验证成功,反之失败。
开始操作
## 创建两个canvas
在微信小程序中,由于不能操作DOM,所以要想更改样式需要在组件中用style表明,在data中修改,通过{{}}渲染出来。比如我们需要控制canvas距离顶部的高度,除了在wxss中定义以外,还可以使用<canvas style='top:{{this.data.top}}rpx'>这种方式实现。小程序中操作创建canvas的方法如下:
const canvas = wx.createCanvasContext('canvas1'),block = wx.createCanvasContext('block');
这样就创建了两个canvas画布
定义所需参数
let l = 50, //拼图的边长x =150+Math.random()*(canvas_width-l-150), //裁剪的x坐标y = 10+Math.random()*(canvas_height-l-10);//裁剪的y坐标that.setData({block_w:l,y:y,x:x})
背景图的制作
使用block.drawImage(img, 0, 0, canvas_width, canvas_height);的方法使图片绘制到canvas上。
使用globalCompositeOperation = 'xor'的方法,使裁剪的那一块变得透明。
block.beginPath()block.moveTo(x,y)block.lineTo(x,y+l)block.lineTo(x+l,y+l)block.lineTo(x+l,y)block.globalCompositeOperation = 'xor'block.fill()block.drawImage(img, 0, 0, canvas_width, canvas_height);block.draw( )
有可能这里无法显示图片,把代码放到 onReady 下就可以了,用本地图片
拼图的制作
使用wx.canvasToTempFilePath方法,从另一张canvas画布上截取一块。
canvas.drawImage(img, 0, 0, canvas_width, canvas_height);canvas.draw(false, setTimeout(() => {wx.canvasToTempFilePath({x: x,y: y,width:l,height: l,canvasId: 'canvas1',fileType: 'png',success(res) {console.log(res.tempFilePath)that.setData({pic: res.tempFilePath})},fail: err => {console.log(err)}}, this)}, 500))
这样res.temFilePath就是我们截取出来的拼图了。
注意!如果图片是空白的,需要添加一个定时器 setTimeout() 清除 canvas 缓存
## 在滑动块上添加触控事件
滑动的过程需要两个事件来完成bindtouchmove='move' bindtouchend='end'
js中创建这两个事件。
move:function(res){let left = res.touches[0].pageX;// 由于我这里是和page没有距离,也没有加外层盒子的,所以,pageX就是位移距离。if (left>0){this.setData({left: left})}else{this.setData({left:0})}},end:function(res){let end = this.data.left,moves = this.data.x;console.log(end)})}}
判断,出结果
end:function(res){......if (Math.abs(end-moves)<2){ //当小于2px的可接受阈值时,验证成功console.log('bingo')wx.showToast({title: '验证成功',icon:'success',duration:2000})setTimeout(function(){wx.redirectTo({url: 'verification',})},2000)}else{this.setData({left:0})}}
参考github/pages/index)
<canvas canvas-id='canvas1' class='canvas' ></canvas><canvas canvas-id='block' class='block' ></canvas><cover-image wx:if='{{pic}}' src="{{pic}}" class='three' style='top:{{y}}px;left:{{left+20}}px'></cover-image><view class='container'><view class="area"><movable-area><movable-view disable-scroll="true" direction="horizontal" bindtouchmove='move' bindtouchend='end' class='view' style='left:{{left}}px'>| | |</movable-view></movable-area></view></view><view class="title">拖动上方滑块完成拼图<image src="../../styles/img/refresh.png" bindtap="refresh" class="refresh"></image></view><view class="prompt" wx:if="{{prompt}}">请控制切图块对齐缺口</view>
划重点 movable-area
这里因为小程序有左滑关闭,所以滑块区域和起始值加大一点
/* pages/index/vfblock.wxss */.container{position: relative;overflow: hidden;margin-top:180px;height: 140rpx;}canvas{top: 0}.canvas{left: -9999px;position: absolute;z-index: -1;}.block{width: 100vw;height: 30vh;position: absolute;}.three{position: absolute;width: 50px;height: 50px;z-index: 999;border: 2px dashed rgb(252, 252, 252);box-sizing: border-box;}.view {text-align: center;height: 70rpx;width: 120rpx;line-height: 70rpx;background: #3a82fa;color: #fff;border-radius: 40rpx;overflow: hidden;box-shadow: 2px 2px 2px 2px rgba(44, 117, 244,0.3)}.area {height: 40rpx;width: 95%;background-color: #e5e5e5;/* overflow: hidden; */border-radius: 20rpx;margin: 50rpx auto;}movable-area{position: absolute;top:35rpx;width: 120rpx;height: 70rpx;}.title{text-align: center;font-weight: 500;font-size: 36rpx;width: 100%;height: 75rpx;line-height: 75rpx;position: relative;}.refresh{width: 60rpx;height: 60rpx;position: absolute;right: 40rpx;top: 50%;transform: translate(0,-50%);}.prompt{width: 95%;height: 44rpx;margin: 20rpx auto;color: #fa3a3a;font-size: 30rpx;font-weight: 500;}
Page({/*** 页面的初始数据*/data: {img:'',width:'',height:'',pic:'',y:'',x:'',left:0,prompt:false},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function () {this.verification()},move:function(res){let left = res.touches[0].pageX;if (left>0){this.setData({left: left,prompt:false})}else{this.setData({left:0})}},end:function(res){var that = thislet end = this.data.left,moves = this.data.x-20;if (Math.abs(end-moves)<5){console.log('bingo')}else{this.setData({left:0,prompt:true})//这里加了个动画提示效果this.animate('.prompt', [{ translateX: -5 },{ translateX: 5 },{ translateX: -5 },{ translateX: 5 },{ translateX: -5 },{ translateX: 5 },], 200, function () {this.clearAnimation('.prompt', { translateX: true }, function () {})setTimeout(()=>{this.setData({prompt: false})},500)}.bind(this))}},//刷新refresh:function(){this.verification()this.setData({left:0})},verification:function(){let that = this;wx.getSystemInfo({success: function (res) {let width = res.windowWidth;let height = res.windowHeight;that.setData({width: width,height: height})},})const canvas = wx.createCanvasContext('canvas1');const block = wx.createCanvasContext('block'),three = wx.createCameraContext('three');const img = that.data.img,canvas_width = that.data.width,canvas_height = that.data.height * 0.3;let l = 50,x = 150 + Math.random() * (canvas_width - l - 150),y = 10 + Math.random() * (canvas_height - l - 10);that.setData({block_w: l,y: y,x: x})canvas.drawImage(img, 0, 0, canvas_width, canvas_height);canvas.draw(false, setTimeout(() => {wx.canvasToTempFilePath({x: x,y: y,width: l,height: l,canvasId: 'canvas1',fileType: 'png',success(res) {console.log(res.tempFilePath)that.setData({pic: res.tempFilePath})},fail: err => {console.log(err)}}, this)}, 500))block.beginPath()block.moveTo(x, y)block.lineTo(x, y + l)block.lineTo(x + l, y + l)block.lineTo(x + l, y)block.globalCompositeOperation = 'xor'block.fill()block.drawImage(img, 0, 0, canvas_width, canvas_height);block.draw()}})
