- 思 路
- 创建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 = this
let 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()
}
})