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
    );
  • 相关阅读:
    window在文件管理器中打开命令行窗口
    js的事件机制
    ext-css
    HTML防止input回车提交表单
    myeclipse调试代码的时候看不到变量的值和jdk源码重新编译
    MANIFEST.MF 文件内容完全详解
    Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'dll.msvcrt'
    JNA知识点
    cookie 和session 的区别
    6- js监听输入框值的即时变化onpropertychange、oninput
  • 原文地址:https://www.cnblogs.com/strick/p/6758356.html
Copyright © 2011-2022 走看看