微信小程序画布开发小记

最近圣诞节就要到了,于是想为我的小程序添加个圣诞帽生成的功能,可以为你的头像选择圣诞帽,然后生成图片到本地。看下效果图,有兴趣的小伙伴也可以扫小程序码体验一下

在开发这个功能的过程中,遇到了一些小问题,就在这里记录一下吧。

一、canvas加载图片问题

在canvas里无法直接加载网络图片,只能使用本地的。所以需要使用 wx.getImageInfo 来把网络图片缓存到本地,然后才能使用。我在绘制图片的时候是使用懒加载的方式,没有在一开始的时候就把全部图片都缓存下来,只有在用户点击的时候,才会缓存图片,并保存缓存路径,这样下次继续绘制同样的图片时速度就会快很多。

 

二、canvas坐标原点变换问题

我们在绘制帽子的时候,因为帽子是有一定的旋转角度以及它和左上方的顶点是有距离的,所以我们需要通过translate和rotate方法去变换坐标原点和角度。

从官方文档我们可以看到,rotate是会叠加的,所以我们可以在rotate某个degree后,再rotate个-degree去修正,但是在rotate前我translate了坐标原点,因为不translate的话帽子的原点就会一直是(0,0),一直在左上方。在往相反方向rotate和translate之后,本以为就可以恢复原点坐标为(0,0),但事实却并非如此,我通过绘制一些辅助的矩形发现,rotate个-degree后对原点没有影响,原点仍然是我们rotate个degree后的位置,所以我们需要通过计算去获取旋转后的坐标,在此附上计算公式吧。

假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转a角度后的新的坐标设为(x0, y0),有公式:

    x0= (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0 ;

    y0= (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0 ;

通过上面的公式我们就可以计算出当前帽子左上角的坐标了,注意角度是弧度制的(degrees * Math.PI/180)。

三、多个帽子的识别问题

要让程序知道是哪个帽子,就要对每一个帽子进行记录,记录的信息为:帽子的编号/左上角坐标/旋转角度/大小。

在此记录下具体的实现代码


start:function(e){//开始按住画布时触发
// console.log(e)
  var sx = e.changedTouches[0].x, sy = e.changedTouches[0].y,
  t_icon=this.data.targetIcon;
  this.data.current = t_icon.length - 1;
this.data.startX=sx;
this.data.startY =sy;
//防止同时作用于多个帽子
if (this.data.scaleMode)return; 

for(var i=t_icon.length-1;i>=0;i--){//倒序读取,因为后添加的会覆盖在之前添加的上面
  var obj=t_icon[i],wid=Number(this.getCapWidth(obj.size)),px=Number(obj.px),py=Number(obj.py),
  degree=obj.degree*Math.PI/180;
  var pos=this.getRotatePosition(0,0,(wid/2),(wid/2),degree);
  //帽子左上角的坐标px,py;右下角的rx,ry
  px+=pos[0];
  py+=pos[1];
  //console.log(px+' '+py+' '+sx+' '+sy);
  pos=this.getRotatePosition(wid,wid,wid/2,wid/2,degree);
  var rx=pos[0],
  ry=pos[1];
  //删除动作
  var off=iconSize/2;//图标大小的一半
  if (sx >= px - off && sx <= px + off && sy >= py - off && sy <= py + off) {
    this.data.targetIcon.splice(i, 1);
    this.drawCap();
    return;
  }
  //旋转缩放动作
  px = Number(obj.px); py = Number(obj.py);//恢复到旋转前的坐标
  if (sx >= px+rx - off && sx <= px+rx + off && sy >= py+ry - off && sy <= py+ry + off) {
    this.data.scaleMode=1;//按住了缩放
    return;//防止同时作用于多个帽子
  }
  if(sx>=px&&sy>=py&&sx<=(px+wid)&&sy<=(py+wid)){
    //交换位置,把选中的移到最后面,这样才能让其显示在最上面
    [t_icon[i], t_icon[this.data.current]] = [t_icon[this.data.current], t_icon[i]]
    break;
  }
}
},
move: function (e) {
  var nx = e.changedTouches[0].x, ny = e.changedTouches[0].y,
    sx = this.data.startX, sy = this.data.startY;
  var ti=this.data.targetIcon,current=this.data.current;
  if(!this.data.scaleMode){
    var obj = ti[current];
    if(!obj)return;
    var wid = Number(this.getCapWidth(obj.size)), px = Number(obj.px), py = Number(obj.py);
    //触摸到帽子区域
    if (sx >= px && sy >= py && sx <= (px + wid) && sy <= (py + wid)) {
      obj.px += (nx - sx);
      obj.py += (ny - sy);
    }
    ti[current]=obj;
  }else{//缩放模式
    var obj = ti[current],wid=this.getCapWidth(obj.size),px=obj.px,py=obj.py;
    var ox=obj.ox,oy=obj.oy;//px+wid/2
    var old=Math.sqrt((sx-ox)*(sx-ox)+(sy-oy)*(sy-oy)),
    now=Math.sqrt((nx-ox)*(nx-ox)+(ny-oy)*(ny-oy));
    //旧半径,要保持旋转中心不变
    var old_size=ti[current].size;
    ti[current].size=now*Math.SQRT2;
    ti[current].px = ox - ti[current].size/2;
    ti[current].py = oy - ti[current].size/2;
    var [y,x]=[(ny-oy),(nx-ox)],
    tan_a =  y* 1.0 / x, tan_b = 1, 
    origin_degree = Math.atan(tan_a)*180/Math.PI,
    rotate_degree = origin_degree -45 ;
    
    //处理一些特殊情况
    if ((y >= 0 && x < 0) || (y < 0 && x < 0))rotate_degree=135+origin_degree;

    console.log(origin_degree + ' ' + rotate_degree + ' ' + y + ' ' + x)
    ti[current].degree = rotate_degree;
  //  console.log(ox+' now:['+nx+','+ny+'] '+(ny-oy)+' '+(nx-ox)+' '+ti[current].degree)
  }
  this.data.targetIcon = ti;
  this.data.startX = nx;
  this.data.startY = ny;
  this.drawCap();
},

其中还有一些不足之处,希望各位小伙伴帮忙指正~~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值