自制一个H5图片拖拽、裁剪插件(原生JS)

前言html

现在的H5运营活动中,有不少都是让用户拍照或者上传图片,而后对照片加滤镜、加贴纸、评颜值之类的。尤为是一些拍照软件公司的运营活动几乎所有都是这样的。jquery

博主也作过很多,为了省事就封装了一个简单的图片拖拽、裁剪的插件。其实网上也有不少相似的插件,只不过有的功能冗余体积大,有的甚至还依赖jquery。索性本身搞一个轻量的,只是不支持缩放功能。git

DEMO(手机上看效果比较好,PC上没有兼容处理),原码github

 

实现web

这里简略说下实现过程,只截取部分代码片断,有兴趣的能够看下原码,反正也很简单。算法

图片上传canvas

这个DEMO里使用的上传方式是HTML5的 File Input,可是不少运营活动须要调用微信的上传&拍照接口,因为之前踩过坑这里就啰嗦两句,帮助新人绕开。api

· 在 wx.config 中的 jsApiList 属性中添加 chooseImage 和 uploadImage API。缓存

· 调用 chooseImage API,得到 localId。服务器

wx.chooseImage({
    count: 1, // 默认9
    sizeType: ['original', 'compressed'],
    sourceType: ['album', 'camera'],
    success: (res) => {
        var localIds = res.localIds[0];
        console.log(localIds);
    }
});

须要注意的是这里的 localId 能够经过 img 标签的 src 属性进行展现,可是没法传给服务器接口或者经过 canvas 裁剪,因此还须要上传一步。

· 调用 uploadImage API,将以前获得的 localId 传入,得到 serverId。

wx.uploadImage({
    localId: localIds,
    isShowProgressTips: 1,
    success: (res) => {
        var serverId = res.serverId;
        console.log(serverId);
    }
});

有了 serverId 其实就能够获得保存在微信服务器上的图片了,只是博主以前还去多余的下载一道。

将一开始微信认证时得到的 accessToken 与 serverId 拼接到下面的连接便可直接引用

const imgUrl = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=" + accessToken + "&media_id=" + serverId;

接下来就能够建立 image 对象,设置 src 属性,完成拖拽裁切等操做。

 

初始化

首先要对图片的尺寸进行调整:

· 若图片宽高比比容器的大,即图片比容器“扁”,就让图片的高度与容器保持一致,宽度自动适应保持原图比例不变

· 若图片宽高比比容器的小,即图片比容器“瘦”,就让图片的宽度与容器保持一致,高度自动适应保持原图比例不变

为了便于理解,咱们假设容器高宽为1:1,为下图中红色线框区域:

代码片断

var img = new Image(),
    _this = this;

img.src = imgUrl;
img.style.webkitUserSelect = 'none';

img.onload = function() {

    var imgWidth = img.width,
        imgHeight = img.height,
        imgRate = imgWidth / imgHeight,
        conRate = conWidth / conHeight;

    if (imgRate > conRate) { //宽型

        var imgCurrentHeight = _this.opts.conHeight,
            imgCurrentWidth = imgCurrentHeight * imgRate,
            maxOffset = conWidth - imgCurrentWidth;

        img.setAttribute('width', 'auto');
        img.setAttribute('height', _this.opts.conHeight);

        //......

    } else { //高型

        var imgCurrentWidth = _this.opts.conWidth,
            imgCurrentHeight = imgCurrentWidth / imgRate,
            maxOffset = conHeight - imgCurrentHeight;

        img.setAttribute('width', '100%');

        //......
    }
}

上述代码就完成了基本的图片大小调整,其中 conWidth, conHeight 是插件接收的容器高与宽,maxOffset 是图片容许拖拽的最大偏移量。

 

拖拽

这里我使用了一个比较轻量的手势库——hammer.js,经过手势的位移改变图片的 translate 属性值。

这里只截取横向拖拽的代码片断,纵向相似

hammer.on('pan', function(e) {
    var current = img.style.transform ? img.style.transform.split('(')[1].split('px')[0] : 0,
    move = Number(current) + (e.deltaX * (_this.opts.speed/10));

    if (move >= 0 || move <= maxOffset) {
        return;
    }

    img.style.transform = 'translateX('+move+'px)';

    _this.cutData.moveX = Math.abs(move);
    _this.cutData.moveY = 0;
});

opts.speed 值为插件初始化时设置的拖动速度,这里速度值算法比较简单,有兴趣的朋友可优化使其更平滑些。cutData 用于缓存拖动的位置,用于以后的裁剪。

 

裁剪

这里使用了 canvas 对图片进行裁剪返回 base64码,能够将 base64码直接展现或者经过 POST 接口传到服务器处理(GET请求长度会超)

cut: function() {
  var canvas = document.createElement('canvas'),
    img = document.querySelector('#cutImgObj'),
    data = this.cutData,
    cutWidth = this.opts.conWidth / data.scaleRate,
    cutHeight = this.opts.conHeight / data.scaleRate;

  canvas.width = cutWidth;
  canvas.height = cutHeight;

  canvas.getContext('2d').drawImage(img, data.moveX/data.scaleRate, data.moveY/data.scaleRate, cutWidth, cutHeight, 0, 0, cutWidth, cutHeight);
  return canvas.toDataURL('image/png');
},

这里要说明下,因为图片进行了缩放,因此须要将画布调整到原图的尺寸,同时记录的拖动位置也须要除以缩放比例。

 

调用

· 初始化

var cutter = new Cutter(picAreaDom, {
    imgUrl: url, //图片连接
    conWidth: containerDom.offsetWidth, //容器宽度
    conHeight: containerDom.offsetWidth * 1.2, //容器高度
    speed: 2, //拖动速度
    callback: function() {
        //doSomething...
    }
});

这里有个地方要说明下,虽然 Cutter 中的 conWidth,conHeight 属性默认为 picAreaDom 的宽高,但若是 picAreaDom 的父级元素为 display:none,那么就获取不到 picAreaDom 尺寸(DEMO中单页应用机制致使的),这时候就须要显式传入裁剪区域的宽高。

callback 为初始化完成时的回调函数,保存实例化对象 cutter,用于以后的裁剪。

· 裁剪

var result = cutter.cut();

result 值即为裁剪后的 base64码。

最后一个小提示,若是须要从新上传图片裁剪,记得将容器中的 img 对象移除。具体的细节能够参考原码,总体比较简单。

 

感谢你的浏览,但愿能有所帮助

相关文章
相关标签/搜索