整体思路  :

在移动端压缩图片并且上传主要用到filereader、canvas 以及 formdata 这三个h5的api。逻辑并不难。整个过程就是:

  (1)用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式)

  (2)把图片数据传入img对象,然后将img绘制到canvas上,再调用canvas.toDataURL对图片进行压缩

  (3)利用copper.js组件进行图片的裁剪旋转处理

  (4)获取到压缩后的base64格式图片数据,转成二进制塞入formdata,再通过XmlHttpRequest提交formdata。、

  说起来好像挺简单,其实还是有些坑的。接下来就直接用代码进行分析:

1)先是获取图片数据,也就是监听input file的change事件,然后获取到上传的文件对象files,将类数组的files转成数组,然后进行forEach遍历。

  先了解一个方法  fileReader

aaarticlea/png;base64,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" alt="" />

aaarticlea/png;base64,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" alt="" />

 

  2)接着判断文件类型,如果不是图片则不作处理。如果是图片就实例化一个filereader,以base64格式读取上传的文件数据,判断数据长度,如果大于200KB的图片就调用compress方法进行压缩,否则调用upload方法进行上传。

    //在我做的项目中并没有判断图片大小进行分别处理

【2】压缩图片

  上面做完图片数据的获取后,就可以做compress压缩图片的方法了。而压缩图片也并不是直接把图片绘制到canvas再调用一下toDataURL就行的。

  在IOS中,canvas绘制图片是有两个限制的:

  首先是图片的大小,如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据。

  再者就是canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的。

  应对第一种限制,处理办法就是瓦片绘制了。瓦片绘制,也就是将图片分割成多块绘制到canvas上,我代码里的做法是把图片分割成100万像素一块的大小,再绘制到canvas上。

  而应对第二种限制,我的处理办法是对图片的宽高进行适当压缩,我代码里为了保险起见,设的上限是四百万像素,如果图片大于四百万像素就压缩到小于四百万像素。四百万像素的图片应该够了,算起来宽高都有2000X2000了。

  如此一来就解决了IOS上的两种限制了。

  除了上面所述的限制,还有两个坑,一个就是canvas的toDataURL是只能压缩jpg的,当用户上传的图片是png的话,就需要转成 jpg,也就是统一用canvas.toDataURL('image/jpeg', 0.1) , 类型统一设成jpeg,而压缩比就自己控制了。

  另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为 canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就 是绘制之前在canvas上铺一层白色的底色。

function compress(img) {
var initSize = img.src.length;
var width = img.width;
var height = img.height; //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000)>1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
}else {
ratio = 1;
} canvas.width = width;
canvas.height = height; // 铺底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height); //如果图片像素大于100万则使用瓦片绘制
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count)+1); //计算要分成多少块瓦片 // 计算每块瓦片的宽和高
var nw = ~~(width / count);
var nh = ~~(height / count); tCanvas.width = nw;
tCanvas.height = nh; for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh); ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
} //进行最小压缩
var ndata = canvas.toDataURL('image/jpeg', 0.1); console.log('压缩前:' + initSize);
console.log('压缩后:' + ndata.length);
console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%"); tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0; return ndata;
} //这里是在别人的博客上面找到的方法 有效的解决了 IOS微信图片压缩后数据量翻倍的问题 下面是我的项目代码
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<!--定义 title keywords description信息 start-->
<title>图书管理</title>
<meta name="keywords" content=""/>
<meta name="description" content=""/>
<!--定义 title keywords description信息 end-->
<!-- 定义文档类型 视口信息 start-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="css/reset.css"/>
<link rel="stylesheet" type="text/css" href="css/common.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />
</head>
<body>
<div class="main-wrap">
<header class="headerTop">
<h3>我要分享</h3>
</header>
<div class="bookBg">
<img src="img/bookBg.png" width="640" height="248">
</div>
<div class="cont">
<form action="" method="" class="form">
<div class="form-sub"><label>姓名&nbsp;:</label><input type="text" id="name" class="text"></div>
<div class="form-sub mbn">
<label>分类&nbsp;:</label>
<select id="type" class="select">
<option value="1">开发语言</option>
<option value="2">数据库</option>
<option value="3">操作系统</option>
<option value="4">前端</option>
<option value="5" selected>综合</option>
</select>
</div>
<div class="form-txt">
<textarea id="word" class="text2">简介</textarea>
</div>
<ul id="preview">
<li><img src="img/get1.png"></li>
<li><img src="img/get.png"><input type="hidden"></li>
<li><img src="img/get.png"><input type="hidden"></li>
<li><img src="img/get.png"><input type="hidden"></li>
<li><img src="img/get.png"><input type="hidden"></li>
<li><img src="img/get.png"><input type="hidden"></li>
</ul>
<button type="button" id="submit" class="button">确认分享</button>
</form>
</div> <div class="tan tan-co">
<div class="file-box">
<div class="fileTxt">
<label>选取图片:</label>
<div>
<button>选择文件</button>
<input type="file" id="getImg">
</div>
</div>
<span id="close"></span>
</div>
<div class="img-wp"></div>
<div class="img-btn">
<p>
<button class="rotate rotate-L" id="rotate-L"></button>
<button class="sure" id="sure"></button>
<button class="rotate rotate-R" id="rotate-R"></button>
</p>
</div>
</div> <footer class="footer">
<a href=""><em></em>图书名单</a>
<a href=""><em></em>已借图书</a>
<a href="" class="cur"><em></em>我要分享</a>
</footer>
</div>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="js/jy.js"></script>
<script type="text/javascript" src="js/cropper.js"></script>
<script type="text/javascript">
$(function(){
var box=$(".img-wp");
var img=null;
var i=null;
//var imgURL;
function init(){
var w=$(window).width();
var h=$(window).height();
$('.tan-co').width(w).height(h);
$('.img-wp').width(w-20).height(h-120);
};
init(); $('#preview li:gt(0)').click(function(){
i=$(this).index();
$('.tan').show(); $('#getImg').change(function(){
var files,file,url;
files=$(this).prop('files');
if(files.length>0){
file = files[0]; //上传按钮上传的一个对象
if(isImageFile(file)){ //判断是否是图片
url = createObjectURLfun(file);
}else{
$('.img-wp').html('您选择的不是图片文件!请重新选择!!');
};
};
startCropper(url,i);
});
}); function startCropper(url,i){ //准备裁剪
img=$('<img src="' + url + '">');
box.empty().html(img); //先清空在放置图片
img.cropper({aspectRatio:2/3});
//图片的压缩速率
} $('#sure').click(function(){
if(img){
var c=img.cropper("getCroppedCanvas");
//获得图片base64的数据
var imgsrc=c.toDataURL("image/jpeg",0.5); //base64位的数据
$('#preview li:eq('+i+') img').attr({'src':imgsrc});
$('#preview li:eq('+i+') input').val(imgsrc);
$('#getImg').val('');
$('.img-wp').empty();
$('.tan').hide();
img=null;
c=null;
}
}); $('#rotate-L').click(function(){
if(img){
img.cropper("rotate", -90);
}
});
$('#rotate-R').click(function(){
if(img){
img.cropper("rotate", 90);
}
}); function isImageFile(file){ //判断是否是图片
if (file.type) {
return /^image\/\w+$/.test(file.type);
} else {
return /\.(jpg|jpeg|png|gif)$/.test(file);
}
} $('#close').click(function(){
$('#getImg').val('');
$('.img-wp').empty();
$('.tan').hide();
}); $('#submit').click(function(){
var name=$('#name').val();
var book=$('#book').val();
var type=$('#type option:selected').val();
var word=$('#word').val(); var pic1=$('#preview li:eq(1) input').val();
var pic2=$('#preview li:eq(2) input').val();
var pic3=$('#preview li:eq(3) input').val();
var pic4=$('#preview li:eq(4) input').val();
var pic5=$('#preview li:eq(5) input').val();
}); createObjectURLfun = function(file) {
if (window.navigator.userAgent.indexOf("Chrome") >= 1 || window.navigator.userAgent.indexOf("Safari") >= 1) {
return window.webkitURL.createObjectURL(file);
} else {
return window.URL.createObjectURL(file);
};
};
}); </script>
</body>
</html>
/*!
* Cropper v0.9.2
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2015 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2015-04-18T04:35:01.500Z
*/ (function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node / CommonJS
factory(require('jquery'));
} else {
// Browser globals.
factory(jQuery);
}
})(function ($) {
'use strict';
var $window = $(window),
$document = $(document),
location = window.location,
// Constants
CROPPER_NAMESPACE = '.cropper',
CROPPER_PREVIEW = 'preview' + CROPPER_NAMESPACE, // RegExps
REGEXP_DRAG_TYPES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/, // Classes
CLASS_MODAL = 'cropper-modal',
CLASS_HIDE = 'cropper-hide',
CLASS_HIDDEN = 'cropper-hidden',
CLASS_INVISIBLE = 'cropper-invisible',
CLASS_MOVE = 'cropper-move',
CLASS_CROP = 'cropper-crop',
CLASS_DISABLED = 'cropper-disabled',
CLASS_BG = 'cropper-bg', // Events
EVENT_MOUSE_DOWN = 'mousedown touchstart',
EVENT_MOUSE_MOVE = 'mousemove touchmove',
EVENT_MOUSE_UP = 'mouseup mouseleave touchend touchleave touchcancel',
EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll',
EVENT_DBLCLICK = 'dblclick',
EVENT_RESIZE = 'resize' + CROPPER_NAMESPACE, // Bind to window with namespace
EVENT_BUILD = 'build' + CROPPER_NAMESPACE,
EVENT_BUILT = 'built' + CROPPER_NAMESPACE,
EVENT_DRAG_START = 'dragstart' + CROPPER_NAMESPACE,
EVENT_DRAG_MOVE = 'dragmove' + CROPPER_NAMESPACE,
EVENT_DRAG_END = 'dragend' + CROPPER_NAMESPACE,
EVENT_ZOOM_IN = 'zoomin' + CROPPER_NAMESPACE,
EVENT_ZOOM_OUT = 'zoomout' + CROPPER_NAMESPACE, // Supports
SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext), // Others
sqrt = Math.sqrt,
min = Math.min,
max = Math.max,
abs = Math.abs,
sin = Math.sin,
cos = Math.cos,
num = parseFloat, // Prototype
prototype = {}; function isNumber(n) {
return typeof n === 'number';
} function isUndefined(n) {
return typeof n === 'undefined';
} function toArray(obj, offset) {
var args = []; if (isNumber(offset)) { // It's necessary for IE8
args.push(offset);
} return args.slice.apply(obj, args);
} // Custom proxy to avoid jQuery's guid
function proxy(fn, context) {
var args = toArray(arguments, 2); return function () {
return fn.apply(context, args.concat(toArray(arguments)));
};
} function isCrossOriginURL(url) {
var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
return parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);
} function addTimestamp(url) { //加上时间搓
var timestamp = 'timestamp=' + (new Date()).getTime();
return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
} function inRange(source, target) { //是否在范围内
return target.left < 0 && source.width < (target.left + target.width) && target.top < 0 && source.height < (target.top + target.height);
} function getRotateValue(degree) { //获取旋转的角度
return degree ? 'rotate(' + degree + 'deg)' : 'none';
} function getRotatedSizes(data, reverse) {
var deg = abs(data.degree) % 180,
arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180,
sinArc = sin(arc),
cosArc = cos(arc),
width = data.width,
height = data.height,
aspectRatio = data.aspectRatio,
newWidth,
newHeight;
if (!reverse) { //如果反向的话
newWidth = width * cosArc + height * sinArc;
newHeight = width * sinArc + height * cosArc;
} else {
newWidth = width / (cosArc + sinArc / aspectRatio);
newHeight = newWidth / aspectRatio;
}
return {
width: newWidth,
height: newHeight
};
} function getSourceCanvas(image, data) {
var canvas = $('<canvas>')[0],
context = canvas.getContext('2d'),
width = data.naturalWidth,
height = data.naturalHeight,
rotate = data.rotate,
rotated = getRotatedSizes({
width: width,
height: height,
degree: rotate
}); if (rotate) {
canvas.width = rotated.width;
canvas.height = rotated.height;
context.save();
context.translate(rotated.width / 2, rotated.height / 2);
context.rotate(rotate * Math.PI / 180);
context.drawImage(image, -width / 2, -height / 2, width, height);
context.restore();
} else {
canvas.width = width;
canvas.height = height;
context.drawImage(image, 0, 0, width, height);
} return canvas;
} function Cropper(element, options) {
this.$element = $(element);
this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options); this.ready = false;
this.built = false;
this.rotated = false;
this.cropped = false;
this.disabled = false;
this.canvas = null;
this.cropBox = null; this.load();
} prototype.load = function (url) {
var options = this.options,
$this = this.$element,
crossOrigin,
bustCacheUrl,
buildEvent,
$clone; if (!url) {
if ($this.is('img')) {
if (!$this.attr('src')) { //没有src属性 就返回
return;
}
url = $this.prop('src');
//什么时候使用attr(),什么时候使用prop()?
//1.添加属性名称该属性就会生效应该使用prop();
//2.是有true,false两个属性使用prop();
//3.其他则使用attr();
} else if ($this.is('canvas') && SUPPORT_CANVAS) { //是画布并且支持画布
url =compress($this[0]);
}
}
if (!url) { //如果找不到地址 就返回
return;
} buildEvent = $.Event(EVENT_BUILD);
$this.one(EVENT_BUILD, options.build).trigger(buildEvent); // Only trigger once if (buildEvent.isDefaultPrevented()) {
return;
} if (options.checkImageOrigin && isCrossOriginURL(url)) {
crossOrigin = 'anonymous'; if (!$this.prop('crossOrigin')) { // Only when there was not a "crossOrigin" property 只有当没有“crossOrigin”属性
bustCacheUrl = addTimestamp(url); // Bust cache (#148)
}
} this.$clone = $clone = $('<img>'); $clone.one('load', $.proxy(function () {
var naturalWidth = $clone.prop('naturalWidth') || $clone.width(),
naturalHeight = $clone.prop('naturalHeight') || $clone.height(); this.image = {
naturalWidth: naturalWidth,
naturalHeight: naturalHeight,
aspectRatio: naturalWidth / naturalHeight,
rotate: 0
}; this.url = url;
this.ready = true;
this.build();
}, this)).one('error', function () {
$clone.remove();
}).attr({
src: bustCacheUrl || url,
crossOrigin: crossOrigin
}); // Hide and insert into the document
$clone.addClass(CLASS_HIDE).insertAfter($this);
}; prototype.build = function () {
var $this = this.$element,
$clone = this.$clone,
options = this.options,
$cropper,
$cropBox; if (!this.ready) {
return;
} if (this.built) {
this.unbuild();
} // Create cropper elements
this.$cropper = $cropper = $(Cropper.TEMPLATE); // Hide the original image
$this.addClass(CLASS_HIDDEN); // Show the clone iamge
$clone.removeClass(CLASS_HIDE); this.$container = $this.parent().append($cropper);
this.$canvas = $cropper.find('.cropper-canvas').append($clone);
this.$dragBox = $cropper.find('.cropper-drag-box');
this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
this.$viewBox = $cropper.find('.cropper-view-box'); this.addListeners();
this.initPreview(); // Format aspect ratio
options.aspectRatio = num(options.aspectRatio) || NaN; // 0 -> NaN if (options.autoCrop) {
this.cropped = true; if (options.modal) {
this.$dragBox.addClass(CLASS_MODAL);
}
} else {
$cropBox.addClass(CLASS_HIDDEN);
} if (options.background) {
$cropper.addClass(CLASS_BG);
} if (!options.highlight) {
$cropBox.find('.cropper-face').addClass(CLASS_INVISIBLE);
} if (!options.guides) {
$cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
} if (!options.movable) {
$cropBox.find('.cropper-face').data('drag', 'move');
} if (!options.resizable) {
$cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
} this.setDragMode(options.dragCrop ? 'crop' : 'move'); this.built = true;
this.render();
$this.one(EVENT_BUILT, options.built).trigger(EVENT_BUILT); // Only trigger once
}; prototype.unbuild = function () {
if (!this.built) {
return;
} this.built = false;
this.container = null;
this.canvas = null;
this.cropBox = null; // This is necessary when replace
this.removeListeners(); this.resetPreview();
this.$preview = null; this.$viewBox = null;
this.$cropBox = null;
this.$dragBox = null;
this.$canvas = null;
this.$container = null; this.$cropper.remove();
this.$cropper = null;
}; $.extend(prototype, {
render: function () {
this.initContainer();
this.initCanvas();
this.initCropBox(); this.renderCanvas(); if (this.cropped) {
this.renderCropBox();
}
}, initContainer: function () {
var $this = this.$element,
$container = this.$container,
$cropper = this.$cropper,
options = this.options; $cropper.addClass(CLASS_HIDDEN);
$this.removeClass(CLASS_HIDDEN); $cropper.css((this.container = {
width: max($container.width(), num(options.minContainerWidth) || 200),
height: max($container.height(), num(options.minContainerHeight) || 100)
})); $this.addClass(CLASS_HIDDEN);
$cropper.removeClass(CLASS_HIDDEN);
}, // image box (wrapper)
initCanvas: function () {
var container = this.container,
containerWidth = container.width,
containerHeight = container.height,
image = this.image,
aspectRatio = image.aspectRatio,
canvas = {
aspectRatio: aspectRatio,
width: containerWidth,
height: containerHeight
}; if (containerHeight * aspectRatio > containerWidth) {
canvas.height = containerWidth / aspectRatio;
} else {
canvas.width = containerHeight * aspectRatio;
} canvas.oldLeft = canvas.left = (containerWidth - canvas.width) / 2;
canvas.oldTop = canvas.top = (containerHeight - canvas.height) / 2; this.canvas = canvas;
this.limitCanvas(true, true);
this.initialImage = $.extend({}, image);
this.initialCanvas = $.extend({}, canvas);
}, limitCanvas: function (size, position) {
var options = this.options,
strict = options.strict,
container = this.container,
containerWidth = container.width,
containerHeight = container.height,
canvas = this.canvas,
aspectRatio = canvas.aspectRatio,
cropBox = this.cropBox,
cropped = this.cropped && cropBox,
minCanvasWidth,
minCanvasHeight; if (size) {
minCanvasWidth = num(options.minCanvasWidth) || 0;
minCanvasHeight = num(options.minCanvasHeight) || 0; if (minCanvasWidth) {
if (strict) {
minCanvasWidth = max(cropped ? cropBox.width : containerWidth, minCanvasWidth);
} minCanvasHeight = minCanvasWidth / aspectRatio;
} else if (minCanvasHeight) { if (strict) {
minCanvasHeight = max(cropped ? cropBox.height : containerHeight, minCanvasHeight);
} minCanvasWidth = minCanvasHeight * aspectRatio;
} else if (strict) {
if (cropped) {
minCanvasWidth = cropBox.width;
minCanvasHeight = cropBox.height; if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasWidth = minCanvasHeight * aspectRatio;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio;
}
} else {
minCanvasWidth = containerWidth;
minCanvasHeight = containerHeight; if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasHeight = minCanvasWidth / aspectRatio;
} else {
minCanvasWidth = minCanvasHeight * aspectRatio;
}
}
} $.extend(canvas, {
minWidth: minCanvasWidth,
minHeight: minCanvasHeight,
maxWidth: Infinity,
maxHeight: Infinity
});
} if (position) {
if (strict) {
if (cropped) {
canvas.minLeft = min(cropBox.left, (cropBox.left + cropBox.width) - canvas.width);
canvas.minTop = min(cropBox.top, (cropBox.top + cropBox.height) - canvas.height);
canvas.maxLeft = cropBox.left;
canvas.maxTop = cropBox.top;
} else {
canvas.minLeft = min(0, containerWidth - canvas.width);
canvas.minTop = min(0, containerHeight - canvas.height);
canvas.maxLeft = max(0, containerWidth - canvas.width);
canvas.maxTop = max(0, containerHeight - canvas.height);
}
} else {
canvas.minLeft = -canvas.width;
canvas.minTop = -canvas.height;
canvas.maxLeft = containerWidth;
canvas.maxTop = containerHeight;
}
}
}, renderCanvas: function (changed) {
var options = this.options,
canvas = this.canvas,
image = this.image,
aspectRatio,
rotated; if (this.rotated) {
this.rotated = false; // Computes rotatation sizes with image sizes
rotated = getRotatedSizes({
width: image.width,
height: image.height,
degree: image.rotate
}); aspectRatio = rotated.width / rotated.height; if (aspectRatio !== canvas.aspectRatio) {
canvas.left -= (rotated.width - canvas.width) / 2;
canvas.top -= (rotated.height - canvas.height) / 2;
canvas.width = rotated.width;
canvas.height = rotated.height;
canvas.aspectRatio = aspectRatio;
this.limitCanvas(true, false);
}
} if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
canvas.left = canvas.oldLeft;
} if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
canvas.top = canvas.oldTop;
} canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight); this.limitCanvas(false, true); canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop); this.$canvas.css({
width: canvas.width,
height: canvas.height,
left: canvas.left,
top: canvas.top
}); this.renderImage(); if (this.cropped && options.strict && !inRange(this.container, canvas)) {
this.limitCropBox(true, true);
} if (changed) {
this.output();
}
}, renderImage: function () {
var canvas = this.canvas,
image = this.image,
reversed; if (image.rotate) {
reversed = getRotatedSizes({
width: canvas.width,
height: canvas.height,
degree: image.rotate,
aspectRatio: image.aspectRatio
}, true);
} $.extend(image, reversed ? {
width: reversed.width,
height: reversed.height,
left: (canvas.width - reversed.width) / 2,
top: (canvas.height - reversed.height) / 2
} : {
width: canvas.width,
height: canvas.height,
left: 0,
top: 0
}); this.$clone.css({
width: image.width,
height: image.height,
marginLeft: image.left,
marginTop: image.top,
transform: getRotateValue(image.rotate)
});
}, initCropBox: function () {
var options = this.options,
canvas = this.canvas,
aspectRatio = options.aspectRatio,
autoCropArea = num(options.autoCropArea) || 0.8,
cropBox = {
width: canvas.width,
height: canvas.height
}; if (aspectRatio) {
if (canvas.height * aspectRatio > canvas.width) {
cropBox.height = cropBox.width / aspectRatio;
} else {
cropBox.width = cropBox.height * aspectRatio;
}
} this.cropBox = cropBox;
this.limitCropBox(true, true); // Initialize auto crop area
cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); // The width of auto crop area must large than "minWidth", and the height too. (#164)
cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2; this.initialCropBox = $.extend({}, cropBox);
}, limitCropBox: function (size, position) {
var options = this.options,
strict = options.strict,
container = this.container,
containerWidth = container.width,
containerHeight = container.height,
canvas = this.canvas,
cropBox = this.cropBox,
aspectRatio = options.aspectRatio,
minCropBoxWidth,
minCropBoxHeight; if (size) {
minCropBoxWidth = num(options.minCropBoxWidth) || 0;
minCropBoxHeight = num(options.minCropBoxHeight) || 0; // min/maxCropBoxWidth/Height must less than conatiner width/height
cropBox.minWidth = min(containerWidth, minCropBoxWidth);
cropBox.minHeight = min(containerHeight, minCropBoxHeight);
cropBox.maxWidth = min(containerWidth, strict ? canvas.width : containerWidth);
cropBox.maxHeight = min(containerHeight, strict ? canvas.height : containerHeight); if (aspectRatio) {
// compare crop box size with container first
if (cropBox.maxHeight * aspectRatio > cropBox.maxWidth) {
cropBox.minHeight = cropBox.minWidth / aspectRatio;
cropBox.maxHeight = cropBox.maxWidth / aspectRatio;
} else {
cropBox.minWidth = cropBox.minHeight * aspectRatio;
cropBox.maxWidth = cropBox.maxHeight * aspectRatio;
}
} // The "minWidth" must be less than "maxWidth", and the "minHeight" too.
cropBox.minWidth = min(cropBox.maxWidth, cropBox.minWidth);
cropBox.minHeight = min(cropBox.maxHeight, cropBox.minHeight);
} if (position) {
if (strict) {
cropBox.minLeft = max(0, canvas.left);
cropBox.minTop = max(0, canvas.top);
cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
} else {
cropBox.minLeft = 0;
cropBox.minTop = 0;
cropBox.maxLeft = containerWidth - cropBox.width;
cropBox.maxTop = containerHeight - cropBox.height;
}
}
}, renderCropBox: function () {
var options = this.options,
container = this.container,
containerWidth = container.width,
containerHeight = container.height,
$cropBox = this.$cropBox,
cropBox = this.cropBox; if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
cropBox.left = cropBox.oldLeft;
} if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
cropBox.top = cropBox.oldTop;
} cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); this.limitCropBox(false, true); cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop); if (options.movable) {
$cropBox.find('.cropper-face').data('drag', (cropBox.width === containerWidth && cropBox.height === containerHeight) ? 'move' : 'all');
} $cropBox.css({
width: cropBox.width,
height: cropBox.height,
left: cropBox.left,
top: cropBox.top
}); if (this.cropped && options.strict && !inRange(container, this.canvas)) {
this.limitCanvas(true, true);
} if (!this.disabled) {
this.output();
}
}, output: function () {
var options = this.options; this.preview(); if (options.crop) {
options.crop.call(this.$element, this.getData());
}
}
}); prototype.initPreview = function () {
var url = this.url; this.$preview = $(this.options.preview);
this.$viewBox.html('<img src="' + url + '">'); // Override img element styles
// Add `display:block` to avoid margin top issue (Occur only when margin-top <= -height)
this.$preview.each(function () {
var $this = $(this); $this.data(CROPPER_PREVIEW, {
width: $this.width(),
height: $this.height(),
original: $this.html()
}).html('<img src="' + url + '" style="display:block;width:100%;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation: 0deg!important">');
});
}; prototype.resetPreview = function () {
this.$preview.each(function () {
var $this = $(this); $this.html($this.data(CROPPER_PREVIEW).original).removeData(CROPPER_PREVIEW);
});
}; prototype.preview = function () {
var image = this.image,
canvas = this.canvas,
cropBox = this.cropBox,
width = image.width,
height = image.height,
left = cropBox.left - canvas.left - image.left,
top = cropBox.top - canvas.top - image.top,
rotate = image.rotate; if (!this.cropped || this.disabled) {
return;
} this.$viewBox.find('img').css({
width: width,
height: height,
marginLeft: -left,
marginTop: -top,
transform: getRotateValue(rotate)
}); this.$preview.each(function () {
var $this = $(this),
data = $this.data(CROPPER_PREVIEW),
ratio = data.width / cropBox.width,
newWidth = data.width,
newHeight = cropBox.height * ratio; if (newHeight > data.height) {
ratio = data.height / cropBox.height;
newWidth = cropBox.width * ratio;
newHeight = data.height;
} $this.width(newWidth).height(newHeight).find('img').css({
width: width * ratio,
height: height * ratio,
marginLeft: -left * ratio,
marginTop: -top * ratio,
transform: getRotateValue(rotate)
});
});
}; prototype.addListeners = function () {
var options = this.options; this.$element.on(EVENT_DRAG_START, options.dragstart).on(EVENT_DRAG_MOVE, options.dragmove).on(EVENT_DRAG_END, options.dragend).on(EVENT_ZOOM_IN, options.zoomin).on(EVENT_ZOOM_OUT, options.zoomout);
this.$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.dragstart, this)).on(EVENT_DBLCLICK, $.proxy(this.dblclick, this)); if (options.zoomable && options.mouseWheelZoom) {
this.$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
} $document.on(EVENT_MOUSE_MOVE, (this._dragmove = proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = proxy(this.dragend, this))); if (options.responsive) {
$window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
}
}; prototype.removeListeners = function () {
var options = this.options; this.$element.off(EVENT_DRAG_START, options.dragstart).off(EVENT_DRAG_MOVE, options.dragmove).off(EVENT_DRAG_END, options.dragend).off(EVENT_ZOOM_IN, options.zoomin).off(EVENT_ZOOM_OUT, options.zoomout);
this.$cropper.off(EVENT_MOUSE_DOWN, this.dragstart).off(EVENT_DBLCLICK, this.dblclick); if (options.zoomable && options.mouseWheelZoom) {
this.$cropper.off(EVENT_WHEEL, this.wheel);
} $document.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend); if (options.responsive) {
$window.off(EVENT_RESIZE, this._resize);
}
}; $.extend(prototype, {
resize: function () {
var $container = this.$container,
container = this.container,
canvasData,
cropBoxData,
ratio; if (this.disabled) {
return;
} ratio = $container.width() / container.width; if (ratio !== 1 || $container.height() !== container.height) {
canvasData = this.getCanvasData();
cropBoxData = this.getCropBoxData(); this.render();
this.setCanvasData($.each(canvasData, function (i, n) {
canvasData[i] = n * ratio;
}));
this.setCropBoxData($.each(cropBoxData, function (i, n) {
cropBoxData[i] = n * ratio;
}));
}
}, dblclick: function () {
if (this.disabled) {
return;
} if (this.$dragBox.hasClass(CLASS_CROP)) {
this.setDragMode('move');
} else {
this.setDragMode('crop');
}
}, wheel: function (event) {
var e = event.originalEvent,
delta = 1; if (this.disabled) {
return;
} event.preventDefault(); if (e.deltaY) {
delta = e.deltaY > 0 ? 1 : -1;
} else if (e.wheelDelta) {
delta = -e.wheelDelta / 120;
} else if (e.detail) {
delta = e.detail > 0 ? 1 : -1;
} this.zoom(-delta * 0.1);
}, dragstart: function (event) {
var options = this.options,
originalEvent = event.originalEvent,
touches = originalEvent && originalEvent.touches,
e = event,
dragType,
dragStartEvent,
touchesLength; if (this.disabled) {
return;
} if (touches) {
touchesLength = touches.length; if (touchesLength > 1) {
if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
e = touches[1];
this.startX2 = e.pageX;
this.startY2 = e.pageY;
dragType = 'zoom';
} else {
return;
}
} e = touches[0];
} dragType = dragType || $(e.target).data('drag'); if (REGEXP_DRAG_TYPES.test(dragType)) {
event.preventDefault(); dragStartEvent = $.Event(EVENT_DRAG_START, {
originalEvent: originalEvent,
dragType: dragType
}); this.$element.trigger(dragStartEvent); if (dragStartEvent.isDefaultPrevented()) {
return;
} this.dragType = dragType;
this.cropping = false;
this.startX = e.pageX;
this.startY = e.pageY; if (dragType === 'crop') {
this.cropping = true;
this.$dragBox.addClass(CLASS_MODAL);
}
}
}, dragmove: function (event) {
var options = this.options,
originalEvent = event.originalEvent,
touches = originalEvent && originalEvent.touches,
e = event,
dragType = this.dragType,
dragMoveEvent,
touchesLength; if (this.disabled) {
return;
} if (touches) {
touchesLength = touches.length; if (touchesLength > 1) {
if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
e = touches[1];
this.endX2 = e.pageX;
this.endY2 = e.pageY;
} else {
return;
}
} e = touches[0];
} if (dragType) {
event.preventDefault(); dragMoveEvent = $.Event(EVENT_DRAG_MOVE, {
originalEvent: originalEvent,
dragType: dragType
}); this.$element.trigger(dragMoveEvent); if (dragMoveEvent.isDefaultPrevented()) {
return;
} this.endX = e.pageX;
this.endY = e.pageY; this.change();
}
}, dragend: function (event) {
var dragType = this.dragType,
dragEndEvent; if (this.disabled) {
return;
} if (dragType) {
event.preventDefault(); dragEndEvent = $.Event(EVENT_DRAG_END, {
originalEvent: event.originalEvent,
dragType: dragType
}); this.$element.trigger(dragEndEvent); if (dragEndEvent.isDefaultPrevented()) {
return;
} if (this.cropping) {
this.cropping = false;
this.$dragBox.toggleClass(CLASS_MODAL, this.cropped && this.options.modal);
} this.dragType = '';
}
}
}); $.extend(prototype, {
reset: function () {
if (!this.built || this.disabled) {
return;
} this.image = $.extend({}, this.initialImage);
this.canvas = $.extend({}, this.initialCanvas);
this.renderCanvas(); if (this.cropped) {
this.cropBox = $.extend({}, this.initialCropBox);
this.renderCropBox();
}
}, clear: function () {
if (!this.cropped || this.disabled) {
return;
} $.extend(this.cropBox, {
left: 0,
top: 0,
width: 0,
height: 0
}); this.cropped = false;
this.renderCropBox(); this.limitCanvas();
this.renderCanvas(); // Render canvas after render crop box this.$dragBox.removeClass(CLASS_MODAL);
this.$cropBox.addClass(CLASS_HIDDEN);
}, destroy: function () {
var $this = this.$element; if (this.ready) {
this.unbuild();
$this.removeClass(CLASS_HIDDEN);
} else {
this.$clone.off('load').remove();
} $this.removeData('cropper');
}, replace: function (url) {
if (!this.disabled && url) {
this.load(url);
}
}, enable: function () {
if (this.built) {
this.disabled = false;
this.$cropper.removeClass(CLASS_DISABLED);
}
}, disable: function () {
if (this.built) {
this.disabled = true;
this.$cropper.addClass(CLASS_DISABLED);
}
}, move: function (offsetX, offsetY) {
var canvas = this.canvas; if (this.built && !this.disabled && isNumber(offsetX) && isNumber(offsetY)) {
canvas.left += offsetX;
canvas.top += offsetY;
this.renderCanvas(true);
}
}, zoom: function (delta) {
var canvas = this.canvas,
zoomEvent,
width,
height; delta = num(delta); if (delta && this.built && !this.disabled && this.options.zoomable) {
zoomEvent = delta > 0 ? $.Event(EVENT_ZOOM_IN) : $.Event(EVENT_ZOOM_OUT);
this.$element.trigger(zoomEvent); if (zoomEvent.isDefaultPrevented()) {
return;
} delta = delta <= -1 ? 1 / (1 - delta) : delta <= 1 ? (1 + delta) : delta;
width = canvas.width * delta;
height = canvas.height * delta;
canvas.left -= (width - canvas.width) / 2;
canvas.top -= (height - canvas.height) / 2;
canvas.width = width;
canvas.height = height;
this.renderCanvas(true);
this.setDragMode('move');
}
}, rotate: function (degree) {
var image = this.image; degree = num(degree); if (degree && this.built && !this.disabled && this.options.rotatable) {
image.rotate = (image.rotate + degree) % 360;
this.rotated = true;
this.renderCanvas(true);
}
}, getData: function () {
var cropBox = this.cropBox,
canvas = this.canvas,
image = this.image,
ratio,
data; if (this.built && this.cropped) {
data = {
x: cropBox.left - canvas.left,
y: cropBox.top - canvas.top,
width: cropBox.width,
height: cropBox.height
}; ratio = image.width / image.naturalWidth; $.each(data, function (i, n) {
n = n / ratio;
data[i] = n;
}); } else {
data = {
x: 0,
y: 0,
width: 0,
height: 0
};
} data.rotate = image.rotate; return data;
}, getContainerData: function () {
return this.built ? this.container : {};
}, getImageData: function () {
return this.ready ? this.image : {};
}, getCanvasData: function () {
var canvas = this.canvas,
data; if (this.built) {
data = {
left: canvas.left,
top: canvas.top,
width: canvas.width,
height: canvas.height
};
} return data || {};
}, setCanvasData: function (data) {
var canvas = this.canvas,
aspectRatio = canvas.aspectRatio; if (this.built && !this.disabled && $.isPlainObject(data)) {
if (isNumber(data.left)) {
canvas.left = data.left;
} if (isNumber(data.top)) {
canvas.top = data.top;
} if (isNumber(data.width)) {
canvas.width = data.width;
canvas.height = data.width / aspectRatio;
} else if (isNumber(data.height)) {
canvas.height = data.height;
canvas.width = data.height * aspectRatio;
} this.renderCanvas(true);
}
}, getCropBoxData: function () {
var cropBox = this.cropBox,
data; if (this.built && this.cropped) {
data = {
left: cropBox.left,
top: cropBox.top,
width: cropBox.width,
height: cropBox.height
};
} return data || {};
}, setCropBoxData: function (data) {
var cropBox = this.cropBox,
aspectRatio = this.options.aspectRatio; if (this.built && this.cropped && !this.disabled && $.isPlainObject(data)) { if (isNumber(data.left)) {
cropBox.left = data.left;
} if (isNumber(data.top)) {
cropBox.top = data.top;
} if (aspectRatio) {
if (isNumber(data.width)) {
cropBox.width = data.width;
cropBox.height = cropBox.width / aspectRatio;
} else if (isNumber(data.height)) {
cropBox.height = data.height;
cropBox.width = cropBox.height * aspectRatio;
}
} else {
if (isNumber(data.width)) {
cropBox.width = data.width;
} if (isNumber(data.height)) {
cropBox.height = data.height;
}
} this.renderCropBox();
}
}, getCroppedCanvas: function (options) { //获取画布
var originalWidth,
originalHeight,
canvasWidth,
canvasHeight,
scaledWidth,
scaledHeight,
scaledRatio,
aspectRatio,
canvas,
context,
data; if (!this.built || !this.cropped || !SUPPORT_CANVAS) {
return;
} if (!$.isPlainObject(options)) {
options = {};
} data = this.getData();
originalWidth = data.width;
originalHeight = data.height;
aspectRatio = originalWidth / originalHeight; if ($.isPlainObject(options)) {
scaledWidth = options.width;
scaledHeight = options.height; if (scaledWidth) {
scaledHeight = scaledWidth / aspectRatio;
scaledRatio = scaledWidth / originalWidth;
} else if (scaledHeight) {
scaledWidth = scaledHeight * aspectRatio;
scaledRatio = scaledHeight / originalHeight;
}
} canvasWidth = scaledWidth || originalWidth;
canvasHeight = scaledHeight || originalHeight; canvas = $('<canvas>')[0];
canvas.width = canvasWidth;
canvas.height = canvasHeight;
context = canvas.getContext('2d'); if (options.fillColor) {
context.fillStyle = options.fillColor;
context.fillRect(0, 0, canvasWidth, canvasHeight);
} // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
context.drawImage.apply(context, (function () {
var source = getSourceCanvas(this.$clone[0], this.image),
sourceWidth = source.width,
sourceHeight = source.height,
args = [source],
srcX = data.x, // source canvas
srcY = data.y,
srcWidth,
srcHeight,
dstX, // destination canvas
dstY,
dstWidth,
dstHeight; if (srcX <= -originalWidth || srcX > sourceWidth) {
srcX = srcWidth = dstX = dstWidth = 0;
} else if (srcX <= 0) {
dstX = -srcX;
srcX = 0;
srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
} else if (srcX <= sourceWidth) {
dstX = 0;
srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
} if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
srcY = srcHeight = dstY = dstHeight = 0;
} else if (srcY <= 0) {
dstY = -srcY;
srcY = 0;
srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
} else if (srcY <= sourceHeight) {
dstY = 0;
srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
} args.push(srcX, srcY, srcWidth, srcHeight); // Scale destination sizes
if (scaledRatio) {
dstX *= scaledRatio;
dstY *= scaledRatio;
dstWidth *= scaledRatio;
dstHeight *= scaledRatio;
} // Avoid "IndexSizeError" in IE and Firefox
if (dstWidth > 0 && dstHeight > 0) {
args.push(dstX, dstY, dstWidth, dstHeight);
} return args;
}).call(this)); return canvas;
}, setAspectRatio: function (aspectRatio) {
var options = this.options; if (!this.disabled && !isUndefined(aspectRatio)) {
options.aspectRatio = num(aspectRatio) || NaN; // 0 -> NaN if (this.built) {
this.initCropBox(); if (this.cropped) {
this.renderCropBox();
}
}
}
}, setDragMode: function (mode) {
var $dragBox = this.$dragBox,
cropable = false,
movable = false; if (!this.ready || this.disabled) {
return;
} switch (mode) {
case 'crop':
if (this.options.dragCrop) {
cropable = true;
$dragBox.data('drag', mode);
} else {
movable = true;
} break; case 'move':
movable = true;
$dragBox.data('drag', mode); break; default:
$dragBox.removeData('drag');
} $dragBox.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable);
}
}); prototype.change = function () {
var dragType = this.dragType,
options = this.options,
canvas = this.canvas,
container = this.container,
cropBox = this.cropBox,
width = cropBox.width,
height = cropBox.height,
left = cropBox.left,
top = cropBox.top,
right = left + width,
bottom = top + height,
minLeft = 0,
minTop = 0,
maxWidth = container.width,
maxHeight = container.height,
renderable = true,
aspectRatio = options.aspectRatio,
range = {
x: this.endX - this.startX,
y: this.endY - this.startY
},
offset; if (options.strict) {
minLeft = cropBox.minLeft;
minTop = cropBox.minTop;
maxWidth = minLeft + min(container.width, canvas.width);
maxHeight = minTop + min(container.height, canvas.height);
} if (aspectRatio) {
range.X = range.y * aspectRatio;
range.Y = range.x / aspectRatio;
} switch (dragType) {
// Move cropBox
case 'all':
left += range.x;
top += range.y;
break; // Resize cropBox
case 'e':
if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
renderable = false;
break;
} width += range.x; if (aspectRatio) {
height = width / aspectRatio;
top -= range.Y / 2;
} if (width < 0) {
dragType = 'w';
width = 0;
} break; case 'n':
if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {
renderable = false;
break;
} height -= range.y;
top += range.y; if (aspectRatio) {
width = height * aspectRatio;
left += range.X / 2;
} if (height < 0) {
dragType = 's';
height = 0;
} break; case 'w':
if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
renderable = false;
break;
} width -= range.x;
left += range.x; if (aspectRatio) {
height = width / aspectRatio;
top += range.Y / 2;
} if (width < 0) {
dragType = 'e';
width = 0;
} break; case 's':
if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {
renderable = false;
break;
} height += range.y; if (aspectRatio) {
width = height * aspectRatio;
left -= range.X / 2;
} if (height < 0) {
dragType = 'n';
height = 0;
} break; case 'ne':
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
renderable = false;
break;
} height -= range.y;
top += range.y;
width = height * aspectRatio;
} else {
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width += range.x;
} if (range.y <= 0) {
if (top > 0) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
} if (width < 0 && height < 0) {
dragType = 'sw';
height = 0;
width = 0;
} else if (width < 0) {
dragType = 'nw';
width = 0;
} else if (height < 0) {
dragType = 'se';
height = 0;
} break; case 'nw':
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
renderable = false;
break;
} height -= range.y;
top += range.y;
width = height * aspectRatio;
left += range.X;
} else {
if (range.x <= 0) {
if (left > 0) {
width -= range.x;
left += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
} if (range.y <= 0) {
if (top > 0) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
} if (width < 0 && height < 0) {
dragType = 'se';
height = 0;
width = 0;
} else if (width < 0) {
dragType = 'ne';
width = 0;
} else if (height < 0) {
dragType = 'sw';
height = 0;
} break; case 'sw':
if (aspectRatio) {
if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
renderable = false;
break;
} width -= range.x;
left += range.x;
height = width / aspectRatio;
} else {
if (range.x <= 0) {
if (left > 0) {
width -= range.x;
left += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
} if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
} if (width < 0 && height < 0) {
dragType = 'ne';
height = 0;
width = 0;
} else if (width < 0) {
dragType = 'se';
width = 0;
} else if (height < 0) {
dragType = 'nw';
height = 0;
} break; case 'se':
if (aspectRatio) {
if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
renderable = false;
break;
} width += range.x;
height = width / aspectRatio;
} else {
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width += range.x;
} if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
} if (width < 0 && height < 0) {
dragType = 'nw';
height = 0;
width = 0;
} else if (width < 0) {
dragType = 'sw';
width = 0;
} else if (height < 0) {
dragType = 'ne';
height = 0;
} break; // Move image
case 'move':
canvas.left += range.x;
canvas.top += range.y;
this.renderCanvas(true);
renderable = false;
break; // Scale image
case 'zoom':
this.zoom(function (x1, y1, x2, y2) {
var z1 = sqrt(x1 * x1 + y1 * y1),
z2 = sqrt(x2 * x2 + y2 * y2); return (z2 - z1) / z1;
}(
abs(this.startX - this.startX2),
abs(this.startY - this.startY2),
abs(this.endX - this.endX2),
abs(this.endY - this.endY2)
)); this.startX2 = this.endX2;
this.startY2 = this.endY2;
renderable = false;
break; // Crop image
case 'crop':
if (range.x && range.y) {
offset = this.$cropper.offset();
left = this.startX - offset.left;
top = this.startY - offset.top;
width = cropBox.minWidth;
height = cropBox.minHeight; if (range.x > 0) {
if (range.y > 0) {
dragType = 'se';
} else {
dragType = 'ne';
top -= height;
}
} else {
if (range.y > 0) {
dragType = 'sw';
left -= width;
} else {
dragType = 'nw';
left -= width;
top -= height;
}
} // Show the cropBox if is hidden
if (!this.cropped) {
this.cropped = true;
this.$cropBox.removeClass(CLASS_HIDDEN);
}
} break; // No default
} if (renderable) {
cropBox.width = width;
cropBox.height = height;
cropBox.left = left;
cropBox.top = top;
this.dragType = dragType; this.renderCropBox();
} // Override
this.startX = this.endX;
this.startY = this.endY;
}; $.extend(Cropper.prototype, prototype); Cropper.DEFAULTS = {
// Defines the aspect ratio of the crop box
// Type: Number
aspectRatio: NaN, // Defines the percentage of automatic cropping area when initializes
// Type: Number (Must large than 0 and less than 1)
autoCropArea: 0.8, // 80% // Outputs the cropping results.
// Type: Function
crop: null, // Add extra containers for previewing
// Type: String (jQuery selector)
preview: '', // Toggles
strict: true, // strict mode, the image cannot zoom out less than the container
responsive: true, // Rebuild when resize the window
checkImageOrigin: true, // Check if the target image is cross origin modal: true, // Show the black modal
guides: true, // Show the dashed lines for guiding
highlight: true, // Show the white modal to highlight the crop box
background: true, // Show the grid background autoCrop: true, // Enable to crop the image automatically when initialize
dragCrop: true, // Enable to create new crop box by dragging over the image
movable: true, // Enable to move the crop box
resizable: true, // Enable to resize the crop box
rotatable: true, // Enable to rotate the image
zoomable: true, // Enable to zoom the image
touchDragZoom: true, // Enable to zoom the image by wheeling mouse
mouseWheelZoom: true, // Enable to zoom the image by dragging touch // Dimensions
minCanvasWidth: 0,
minCanvasHeight: 0,
minCropBoxWidth: 0,
minCropBoxHeight: 0,
minContainerWidth: 200,
minContainerHeight: 100, // Events
build: null, // Function
built: null, // Function
dragstart: null, // Function
dragmove: null, // Function
dragend: null, // Function
zoomin: null, // Function
zoomout: null // Function
}; Cropper.setDefaults = function (options) {
$.extend(Cropper.DEFAULTS, options);
}; // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin)
Cropper.TEMPLATE = (function (source, words) {
words = words.split(',');
return source.replace(/\d+/g, function (i) {
return words[i];
});
})('<0 6="5-container"><0 6="5-canvas"></0><0 6="5-2-9" 3-2="move"></0><0 6="5-crop-9"><1 6="5-view-9"></1><1 6="5-8 8-h"></1><1 6="5-8 8-v"></1><1 6="5-face" 3-2="all"></1><1 6="5-7 7-e" 3-2="e"></1><1 6="5-7 7-n" 3-2="n"></1><1 6="5-7 7-w" 3-2="w"></1><1 6="5-7 7-s" 3-2="s"></1><1 6="5-4 4-e" 3-2="e"></1><1 6="5-4 4-n" 3-2="n"></1><1 6="5-4 4-w" 3-2="w"></1><1 6="5-4 4-s" 3-2="s"></1><1 6="5-4 4-ne" 3-2="ne"></1><1 6="5-4 4-nw" 3-2="nw"></1><1 6="5-4 4-sw" 3-2="sw"></1><1 6="5-4 4-se" 3-2="se"></1></0></0>', 'div,span,drag,data,point,cropper,class,line,dashed,box'); /* Template source:
<div class="cropper-container">
<div class="cropper-canvas"></div>
<div class="cropper-drag-box" data-drag="move"></div>
<div class="cropper-crop-box">
<span class="cropper-view-box"></span>
<span class="cropper-dashed dashed-h"></span>
<span class="cropper-dashed dashed-v"></span>
<span class="cropper-face" data-drag="all"></span>
<span class="cropper-line line-e" data-drag="e"></span>
<span class="cropper-line line-n" data-drag="n"></span>
<span class="cropper-line line-w" data-drag="w"></span>
<span class="cropper-line line-s" data-drag="s"></span>
<span class="cropper-point point-e" data-drag="e"></span>
<span class="cropper-point point-n" data-drag="n"></span>
<span class="cropper-point point-w" data-drag="w"></span>
<span class="cropper-point point-s" data-drag="s"></span>
<span class="cropper-point point-ne" data-drag="ne"></span>
<span class="cropper-point point-nw" data-drag="nw"></span>
<span class="cropper-point point-sw" data-drag="sw"></span>
<span class="cropper-point point-se" data-drag="se"></span>
</div>
</div>
*/ // Save the other cropper
Cropper.other = $.fn.cropper; // Register as jQuery plugin
$.fn.cropper = function (options) {
var args = toArray(arguments, 1),
result; this.each(function () {
var $this = $(this),
data = $this.data('cropper'),
fn; if (!data) {
$this.data('cropper', (data = new Cropper(this, options)));
} if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
result = fn.apply(data, args);
}
}); return isUndefined(result) ? this : result;
}; $.fn.cropper.Constructor = Cropper;
$.fn.cropper.setDefaults = Cropper.setDefaults; // No conflict
$.fn.cropper.noConflict = function () {
$.fn.cropper = Cropper.other;
return this;
}; function compress(img) { //图片压缩的方法
var initSize = img.src.length;
var width = img.width;
var height = img.height;
//如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000)>1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
}else {
ratio = 1;
} canvas.width = width;
canvas.height = height;
// 铺底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height); //如果图片像素大于100万则使用瓦片绘制
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count)+1); //计算要分成多少块瓦片
// 计算每块瓦片的宽和高
var nw = ~~(width / count);
var nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh); ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//进行最小压缩
var ndata = canvas.toDataURL('image/jpeg', 0.1);
console.log('压缩前:' + initSize);
console.log('压缩后:' + ndata.length);
console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
} });

这里的copper.js插件原来用的是  toDataUrl()方法  由于前面提到的问题我将它改成了compress方法

在做这个项目时我发现微信和QQ浏览器base64位数据前面要截取的文档头长度不一致

												

图书管理之HTML5压缩旋转裁剪图片总结的更多相关文章

  1. iOS 压缩与裁剪图片问题

    我们假设要在截图中的举行图片展示区显示图片,由于原图片的宽高比例与图片显示窗口的宽高比例不一定相同,所以,直接将图片扔进去会改变图片的宽高比例,展示效果不好. 这时你可能想到设置UIImageView ...

  2. .mat转成.npy文件+Python(Pytorch)压缩裁剪图片

    需求:现有数据文件V1.mat,里面包含多个数据集,现需将里面的images数据集提取出来,然后进行压缩裁剪成指定大小 V1.mat数据集目录: 1.从mat文件中提取数据(使用Python) V1. ...

  3. iOS开发——UI进阶篇(十八)核心动画小例子,转盘(裁剪图片、自定义按钮、旋转)图片折叠、音量震动条、倒影、粒子效果

    一.转盘(裁剪图片.自定义按钮.旋转) 1.裁剪图片 将一张大图片裁剪为多张 // CGImageCreateWithImageInRect:用来裁剪图片 // image:需要裁剪的图片 // re ...

  4. HTML5 本地裁剪图片

    下面奉上我自己写的一个demo,代码写得比较少,很多细节不会处理.如果有不得当的地方恳请指教,谢谢啦 ^_^ ^_^   功能实现步奏:   一:获取文件,读取文件并生成url   二:根据容器的大小 ...

  5. HTML5 本地裁剪图片并上传至服务器(转)

    很多情况下用户上传的图片都需要经过裁剪,比如头像啊什么的.但以前实现这类需求都很复杂,往往需要先把图片上传到服务器,然后返回给用户,让用户确定裁剪坐标,发送给服务器,服务器裁剪完再返回给用户,来回需要 ...

  6. Python,PIL压缩裁剪图片

    自己写了用来压缩 DC 照片的,批量处理整目录文件,非常方便.需要安装 PIL #!/usr/bin/env python import Image import os import os.path ...

  7. C#实现对图片文件的压缩、裁剪操作实例

    本文实例讲述了C#对图片文件的压缩.裁剪操作方法,在C#项目开发中非常有实用价值.分享给大家供大家参考.具体如下: 一般在做项目时,对图片的处理,以前都采用在上传时,限制其大小的方式,这样带来诸多不便 ...

  8. HTML5裁剪图片并上传至服务器实现原理讲解

    HTML5裁剪图片并上传至服务器实现原理讲解   经常做项目需要本地上传图片裁剪并上传服务器,比如会议头像等功能,但以前实现这类需求都很复杂,往往需要先把图片上传到服务器,然后返回给用户,让用户确定裁 ...

  9. Html5+asp.net mvc 图片压缩上传

    在做图片上传时,大图片如果没有压缩直接上传时间会非常长,因为有的图片太大,传到服务器上再压缩太慢了,而且损耗流量. 思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法 ...

随机推荐

  1. ASINetworkQueue 队列下载

    我们通过一个例子介绍一下请求队列使用,我们设计了一个应用,用户点击GO按钮从服务器同时下载两张图片显示在画面中. 我们直接看看主视图控制器ViewController.h代码如下: #import “ ...

  2. LeetCode Intersection of Two Linked Lists

    原题链接在这里:https://leetcode.com/problems/intersection-of-two-linked-lists/ 思路:1. 找到距离各自tail 相同距离的起始List ...

  3. 注册表操作命令和自定义cmd窗口

    REM @echo offREM clsREM echo Microsoft Windows 7REM echo ------------------------REM echo Welcome to ...

  4. QMainWindow的setLayout的问题

    因为QMainWindow有自己的layout,所以需要一个QWidget,然后setCentralWidget,给这个QWidget调用setLayout http://stackoverflow. ...

  5. MVC 应用程序级别捕捉异常

    捕捉异常: using System; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http. ...

  6. C#和VC++字符集和编码

    C# char 关键字用于声明 .NET framework 使用 Unicode 字符表示 System.Char 结构的实例. Char 对象的值是 16 位数字 (序号值.)将字符表示为 UTF ...

  7. Django 学习

    urls.py 路由系统 from django.conf.urls import url,includefrom django.contrib import adminfrom web import ...

  8. 在HTML中调用iOS

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Java基础之序列化对象——将对象写入到文件中(SerializeObjects)

    控制台程序. 首先定义一个含有任意不同数据类型域的可序列化类: import java.io.Serializable; public class Junk implements Serializab ...

  10. leetcode95 Unique Binary Search Trees II

    题目: Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. F ...