zoukankan      html  css  js  c++  java
  • 移动端 H5图片裁剪插件,内置简单手势操作

    前面曾经写过一篇《H5图片裁剪升级版》,但里面需要借助第三方手势库,这次就不需要使用手势库,全部封装在代码中。

    下图是裁剪的展示,下面就做了拖放和裁剪,没有做缩放,在插件中需要用到大量的计算。veImage的源码可以在此处浏览

    一、原理

    1)拖动缩放裁剪都是借助Canvas实现的。Canvas的基础概念可以参考《让自己也能使用Canvas》。

    2)拖动是通过设置Canvas画布左上角的起点实现的。使用CanvasRenderingContext2D.translate方法。

    3)缩放是通过设置目标画布上绘制图像的宽度和高度实现的。使用CanvasRenderingContext2D.drawImage方法。

    4)裁剪是通过计算比例后(效果图中的画布尺寸与实际网页中的画布尺寸不符),在原画布上面截取计算后的尺寸,画到一张新画布中,新画布的宽高就是需要截取的宽高。

    5)拖动缩放是需要计算两个坐标的差值,而这两个坐标是通过e.touches获取到,这个参数可以参考《触屏touch事件记录》。

    6)将画布的起始点,设置在画布的中心,也就是宽高的一半。这样设置后,缩放的效果看上去就是向四周缩放。否则效果是固定在图片左上角,然后缩放,如下图所示。

     

    二、参数

    参数目前就3个。

    1)Canvas:画布,可以通过DOM获取到。

    2)Image:图片,Image对象,这里先做的简单点,不是传地址。

    3)relativeWidth:相对宽度,裁剪的时候可计算比例。

    HTML代码如下

    <canvas id="captureCanvas2" class="car-img car-canvas"></canvas>

    JavaScript代码如下

    var imgEdit2;
    var img = new Image();
    img.src = 'img/car-demo2.jpg';
    img.onload = function() {
        imgEdit2 = new veImage({canvas:document.getElementById('captureCanvas2'), image:this});
    };

    三、事件绑定

    拖动、缩放涉及的事件是touchstarttouchmovetouchend

    给Canvas画布添加上述事件,模拟出手势效果。

    事件绑定用到了“handleEvent”方式绑定。前面的《Slider图片滑动插件》也是用相同的方式绑定。

    /**
     * 绑定事件
     */
    veImagePrototype._bind = function() {
      this.canvas.addEventListener(events.start, this);
      this.canvas.addEventListener(events.move, this);
      this.canvas.addEventListener(events.end, this);
    };
    /**
     * 高级的绑定方法
     */
    veImagePrototype.handleEvent = function(e) {
      switch (e.type) {
        case events.start:
          this.startEvt(e);
          break;
        case events.move:
          this.moveEvt(e);
          break;
        case events.end:
          this.endEvt(e);
          break;
      }
    };

    四、拖动与缩放

    1)touchstart

    1. 在touchstart事件中记录开始的坐标

    2. 通过手指的数量,设置当前的模式,简单处理,1根手指是拖动,2根手指是缩放。

    this.start = _finger(e.touches);//记录开始的坐标
    this._mode(e.touches);//模式初始化

    2)touchmove

    1. 记录移动中的坐标。

    2. 当模式是1的时候,才做拖动。

    3. 原先是通过手指数量来判断,拖动还是缩放,不过后面发现缩放后,移除手指的时候,会出现一个手指还停留在页面上,导致位移一段距离。如下图所示:

     

    e.preventDefault(); //禁止滚动
    
    var fingers = _finger(e.touches); //记录移动中的坐标
    //不能仅仅通过手指数量来判断 因为当缩放后,移除手指的时候,会出现一个手指还停留在页面上,导致位移
    if (this.mode == 1) { //位移
      this._translate(fingers);
    } else if (this.mode == 2) { //缩放
      this._zoom(fingers);
    }
    //将start的坐标复为移动中的坐标 时时计算偏移值,不然会变得非常大,图片在移动中会飞出画面
    this.start = fingers;

    3)移动计算

    1. 最普通的计算方式,移动中的坐标点减去刚开始触屏的坐标点

    2. 向左或上移动,就是负数。

    //计算手指的位移
    this._draw(
      fingers[0].x - this.start[0].x,
      fingers[0].y - this.start[0].y
    );

    4)缩放计算

    1. 先计算上一次手指两个X轴和Y轴之间的距离。

    2. 再计算当前的手指两个X轴和Y轴之间的距离。

    3. 都取绝对值,缩放率分母是上一次手指,分子是当前手指两个X轴和Y轴之间的距离。

    //上一次手指两个X轴和Y轴之间的距离
    var lastOffset = {
      x: Math.abs(this.start[0].x - this.start[1].x),
      y: Math.abs(this.start[0].y - this.start[1].y)
    };
    if (lastOffset.x == 0 || lastOffset.y == 0) { //防止分母是0
      return;
    }
    //缩放不需要坐标轴偏移 但计算缩放值 需要偏移值
    //缩放率分母是上一次手指,分子是当前手指两个X轴和Y轴之间的距离
    this._draw(
      0, 0,
      Math.abs(fingers[0].x - fingers[1].x) / lastOffset.x,
      Math.abs(fingers[0].y - fingers[1].y) / lastOffset.y
    );

    五、画图

    1)清空画并保存状态

    1. 如果不清空画布,那么刚刚拖动的图片还会在画布上,形成了拖影。

    2. 保存状态(CanvasRenderingContext2D.save)是为了下面能够还原到清空的画布。

    //清空画布 擦除之前绘制的所有内容 不清空的话会显示各个步骤的画布
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.context.save(); //保存状态

    2)拖动消息

    拖动是由translate实现的,先计算原点和偏移值的和,再设置画布的原点。

    this.origin.x += dx || 0; //计算X轴坐标
    this.origin.y += dy || 0; //计算Y轴坐标
    this.context.translate(this.origin.x, this.origin.y); //位移

    3)缩放效果

    缩放是通过改变目标画布的宽高实现的。

    zoomWidth = zoomWidth || 1;
    zoomHeight = zoomHeight || 1;
    var zoom = zoomWidth > zoomHeight ? zoomWidth : zoomHeight; //统一按一个比例做缩放
    this.dWidth *= zoom; //宽度缩放
    this.dHeight *= zoom; //高度缩放

    4)画图

    drawImage实现。语法如下:

    1. sx, sy, sWidth, sHeight分别是图片的坐标点和宽高。

    2. dx, dy, dWidth, dHeight分别是画布缩放后的中心点和缩放后的宽高。

    3. 由于原点是在画布的中心点,所以要画到合适的位置,dx, dy就需要用负数坐标点。

    this.context.drawImage(
      this.image, 0, 0, this.image.width, this.image.height,
      -this.dWidth / 2, -this.dHeight / 2, this.dWidth, this.dHeight
    ); //目标画布的中心点

    5)还原状态

    如果不还原(restore),就会像下图那样,上一张图还在画布中,也不能拖动或缩放了。

    由于前面translate设置了原点,所以下一次画的时候会从这个原点开始。

    将原点设置的小一些,就会像下图那样,出现谍影,上图其实也是谍影,只是已经超出画布了,所以看不到。

    this.origin = {
      x: 30,
      y: 30
    };

    六、裁剪

    1)新建一张画布

    裁剪后的图片画到这张新画布中。

    var canvas = document.createElement("canvas"),
      context = canvas.getContext("2d");

    2)计算比例

    例如效果图上画布的宽是750,那么裁剪的尺寸是相对于750来说的,而实际画布的宽度可能是360,那么这个时候就要做比例计算了。

    rate = this.canvas.width / this.opts.relativeWidth;

    3)计算尺寸

    在获取到比例后,计算真实画布中的坐标和尺寸。

    x *= rate;
    y *= rate;
    canvas.width = w * rate;
    canvas.height = h * rate;
    context.drawImage(
      this.canvas, x, y, canvas.width, canvas.height,
      0, 0, canvas.width, canvas.height
    );
  • 相关阅读:
    HDU2586 How far away?(tarjan的LCA)
    You Raise Me Up
    POJ2891 Strange Way to Express Integers(中国剩余定理)
    POJ2142 The Balance(扩展欧几里得)
    HDU 1166模仿大牛写的线段树
    NetWord Dinic
    HDU 1754 线段树裸题
    hdu1394 Minimum Inversion Number
    hdu2795 Billboard
    【完全版】线段树
  • 原文地址:https://www.cnblogs.com/strick/p/6758356.html
Copyright © 2011-2022 走看看