Cropper.js 实现裁剪图片并上传(PC端)


Posted in Javascript onAugust 20, 2017

由于之前做项目的时候有需求是需要实现裁剪图片来做头像并上传到服务器,所以上网查询了很多资料,也试用了许多案例,发现cropper插件裁剪是比较完善的,所以结合之前的使用情况,编写了此案例。本案例是参考cropper站点实例,进行修改简化。

option相关参数说明:

viewMode 显示模式

Type: Number
Default: 0
Options:
0: the crop box is just within the container 裁剪框只能在 1内移动
1: the crop box should be within the canvas 裁剪框 只能在 2图片内移动
2: the canvas should not be within the container 2图片 不全部铺满1 (即缩小时可以有一边出现空隙)
3: the container should be within the canvas 2图片 全部铺满1 (即 再怎么缩小也不会出现空隙)

dragMode 拖动模式

Default: ‘crop'
Options:
‘crop': create a new crop box 当鼠标 点击一处时根据这个点重新生成一个 裁剪框
‘move': move the canvas 可以拖动图片
‘none': do nothing 图片就不能拖动了

toggleDragModeOnDblclick: 默认true .是否允许 拖动模式 “crop” 跟“move” 的切换状态。。即当点下为crop 模式,如果未松开拖动这时就是“move”模式。放开后又为“crop”模式。

preview: 截图的显示位置。类型:String

responsive:是否在窗口尺寸改变的时候重置cropper。类型:Boolean,默认值true。

checkImageOrigin:类型:Boolean,默认值true。默认情况下,插件会检测图片的源,如果是跨域图片,图片元素会被添加crossOriginclass,并会为图片的url添加一个时间戳来使getCroppedCanvas变为可用。添加时间戳会使图片重新加载,以使跨域图片能够使用getCroppedCanvas。在图片上添加crossOriginclass会阻止在图片url上添加时间戳,及图片的重新加载。

background:类型:Boolean,默认值true。是否在容器上显示网格背景。 要想改背景,是直接改,cropper.css样式中的 cropper-bg。

canvas(图片)相关

movable:类型:Boolean,默认值true。是否允许移动图片
rotatable:类型:Boolean,默认值true。是否允许旋转图片。
scalable: 默认 true 。 是否允许扩展图片。(暂时不知道干嘛用)
zoomable: 默认true, 石头允许缩放图片。
zoomOnWheel: 默认 true 是否允许鼠标滚轴 缩放图片
zoomOnTouch: 默认true 是否允许触摸缩放图片(触摸屏上两手指操作。)
wheelZoomRatio: 默认0.1 师表滚轴缩放图片比例。即滚一下。图片缩放多少。如 0.1 就是图片的10%

crop(裁剪框)相关

aspectRatio裁剪框比例 默认NaN
例如:: 1 / 1,//裁剪框比例 1:1
modal:类型:Boolean,默认值true。是否在剪裁框上显示黑色的模态窗口。
cropBoxMovable:默认true ,是否允许拖动裁剪框
cropBoxResizable:默认 true,//是否允许拖动 改变裁剪框大小
autoCrop:类型:Boolean,默认值true。是否允许在初始化时自动出现裁剪框。
autoCropArea:类型:Number,默认值0.8(图片的80%)。0-1之间的数值,定义自动剪裁框的大小。
highlight:类型:Boolean,默认值true。是否在剪裁框上显示白色的模态窗口。
guides:类型:Boolean,默认值true。是否在剪裁框上显示虚线。
center: 默认true 是否显示裁剪框 中间的+ restore : 类型:Boolean,默认值true 是否
调整窗口大小后恢复裁剪区域。

大小相关

minContainerWidth:类型:Number,默认值200。容器的最小宽度。
minContainerHeight:类型:Number,默认值100。容器的最小高度。
minCanvasWidth:类型:Number,默认值0。canvas 的最小宽度(image wrapper)。
minCanvasHeight:类型:Number,默认值0。canvas 的最小高度(image wrapper)。

监听触发的方法

build:类型:Function,默认值null。build.cropper
事件的简写方式。 ====== 。控件初始化前执行
built:类型:Function,默认值null。built.cropper
事件的简写方式。 ====== 空间初始化完成后执行
dragstart:类型:Function,默认值null。dragstart.cropper
事件的简写方式。 ====== 拖动开始执行
dragmove:类型:Function,默认值null。dragmove.cropper
事件的简写方式。====== 拖动移动中执行
dragend:类型:Function,默认值null。dragend.cropper
事件的简写方式。====== 拖动结束执行
zoomin:类型:Function,默认值null。zoomin.cropper
事件的简写方式。 ====== 缩小执行
zoomout:类型:Function,默认值null。zoomout.cropper
事件的简写方式。 ====== 放大执行

index.html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="external nofollow" >
 <link rel="stylesheet" href="css/cropper.css" rel="external nofollow" >
 <link rel="stylesheet" href="css/main.css" rel="external nofollow" >
 <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
 <!--[if lt IE 9]>
 <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
 <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
 <![endif]-->
</head>
<body>
 <!--[if lt IE 8]>
 <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/" rel="external nofollow" >upgrade your browser</a> to improve your experience.</p>
 <![endif]-->
 <!-- Content -->
 <div class="container" style="margin-top:50px;">
 <div class="row">
 <div class="col-md-9">
 <!-- <h3>Demo:</h3> -->
 <div class="img-container">
  ![](images/picture.jpg)
 </div>
 </div>
 <div class="col-md-3">
 <!-- <h3>Preview:</h3> -->
 <div class="docs-preview clearfix">
  <div class="img-preview preview-lg"></div>
 </div>
 </div>
 </div>
 <div class="row">
 <div class="col-md-9 docs-buttons">
 <!-- <h3>Toolbar:</h3> -->
 <div class="btn-group">
  <button type="button" class="btn btn-primary" data-method="zoom" data-option="0.1" title="Zoom In">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("zoom", 0.1)">
  放大
  </span>
  </button>
  <button type="button" class="btn btn-primary" data-method="zoom" data-option="-0.1" title="Zoom Out">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("zoom", -0.1)">
  缩小
  </span>
  </button>
 </div>
 <div class="btn-group">
  <button type="button" class="btn btn-primary" data-method="move" data-option="-10" data-second-option="0" title="Move Left">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("move", -10, 0)">
  左移
  </span>
  </button>
  <button type="button" class="btn btn-primary" data-method="move" data-option="10" data-second-option="0" title="Move Right">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("move", 10, 0)">
  右移
  </span>
  </button>
  <button type="button" class="btn btn-primary" data-method="move" data-option="0" data-second-option="-10" title="Move Up">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("move", 0, -10)">
  上移
  </span>
  </button>
  <button type="button" class="btn btn-primary" data-method="move" data-option="0" data-second-option="10" title="Move Down">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("move", 0, 10)">
  下移
  </span>
  </button>
 </div>
 <div class="btn-group">
  <button type="button" class="btn btn-primary" data-method="rotate" data-option="-90" title="Rotate Left">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("rotate", -90)">
  左转90º
  </span>
  </button>
  <button type="button" class="btn btn-primary" data-method="rotate" data-option="90" title="Rotate Right">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("rotate", 90)">
  右转90º
  </span>
  </button>
 </div>
 <div class="btn-group">
  <button type="button" class="btn btn-primary" data-method="reset" title="Reset">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="$().cropper("reset")">
  刷新
  </span>
  </button>
  <label class="btn btn-primary btn-upload" for="inputImage" title="Upload image file">
  <input type="file" class="sr-only" id="inputImage" name="file" accept=".jpg,.jpeg,.png,.gif,.bmp,.tiff">
  <span class="docs-tooltip" data-toggle="tooltip" data-animation="false" title="Import image with Blob URLs">
  上传图片
  </span>
  </label>
 </div>
 <div class="btn-group btn-group-crop">
  <button class="btn btn-primary" data-method="getCroppedCanvas" data-option="{ "width": 180, "height": 90 }" type="button">
  上传头像
  </button>
 </div>
 <!-- Show the cropped image in modal -->
 <div class="modal fade docs-cropped" id="getCroppedCanvasModal" aria-hidden="true" aria-labelledby="getCroppedCanvasTitle" role="dialog" tabindex="-1">
  <div class="modal-dialog">
  <div class="modal-content">
  <div class="modal-header">
  <h5 class="modal-title" id="getCroppedCanvasTitle">Cropped</h5>
  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
   <span aria-hidden="true">×</span>
  </button>
  </div>
  <div class="modal-body"></div>
  <div class="modal-footer">
  <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
  <a class="btn btn-primary" id="download" href="javascript:void(0);" rel="external nofollow" download="cropped.jpg">Download</a>
  </div>
  </div>
  </div>
 </div><!-- /.modal -->
 </div><!-- /.docs-buttons -->
 </div>
 <!-- Scripts -->
 <script src="js/jquery.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
 <script src="js/cropper.js"></script>
 <script src="js/main.js"></script>
</body>
</html>

cropper.js部分参数代码:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
 typeof define === 'function' && define.amd ? define(['jquery'], factory) :
 (factory(global.$));
}(this, (function ($) { 'use strict';
$ = 'default' in $ ? $['default'] : $;
var DEFAULTS = {
 // Define the view mode of the cropper
 viewMode: 0, // 0, 1, 2, 3 显示
 // Define the dragging mode of the cropper
 dragMode: 'crop', // 'crop', 'move' or 'none'
 // Define the aspect ratio of the crop box
 aspectRatio: NaN,
 // An object with the previous cropping result data
 data: null,
 // A selector for adding extra containers to preview
 preview: '',
 // Re-render the cropper when resize the window
 responsive: true,
 // Restore the cropped area after resize the window
 restore: true,
 // Check if the current image is a cross-origin image
 checkCrossOrigin: true,
 // Check the current image's Exif Orientation information
 checkOrientation: true,
 // Show the black modal
 modal: true,
 // Show the dashed lines for guiding
 guides: true,
 // Show the center indicator for guiding
 center: true,
 // Show the white modal to highlight the crop box
 highlight: true,
 // Show the grid background
 background: true,
 // Enable to crop the image automatically when initialize
 autoCrop: true,
 // Define the percentage of automatic cropping area when initializes
 autoCropArea: 0.8,
 // Enable to move the image
 movable: true,
 // Enable to rotate the image
 rotatable: true,
 // Enable to scale the image
 scalable: true,
 // Enable to zoom the image
 zoomable: true,
 // Enable to zoom the image by dragging touch
 zoomOnTouch: true,
 // Enable to zoom the image by wheeling mouse
 zoomOnWheel: true,
 // Define zoom ratio when zoom the image by wheeling mouse
 wheelZoomRatio: 0.1,
 // Enable to move the crop box
 cropBoxMovable: true,
 // Enable to resize the crop box
 cropBoxResizable: true,
 // Toggle drag mode between "crop" and "move" when click twice on the cropper
 toggleDragModeOnDblclick: true,
 // Size limitation
 minCanvasWidth: 0,
 minCanvasHeight: 0,
 minCropBoxWidth: 0,
 minCropBoxHeight: 0,
 minContainerWidth: 200,
 minContainerHeight: 100,
 // Shortcuts of events
 ready: null,
 cropstart: null,
 cropmove: null,
 cropend: null,
 crop: null,
 zoom: null
};

main.js 代码如下:

$(function () {
 'use strict';//表示强规则
 var console = window.console || { log: function () {} };
 var URL = window.URL || window.webkitURL;
 var $image = $('#image');
 var $download = $('#download');
 //获取图片截取的位置
 var $dataX = $('#dataX');
 var $dataY = $('#dataY');
 var $dataHeight = $('#dataHeight');
 var $dataWidth = $('#dataWidth');
 var $dataRotate = $('#dataRotate');
 var $dataScaleX = $('#dataScaleX');
 var $dataScaleY = $('#dataScaleY');
 var options = {
 aspectRatio: 1 / 1, //裁剪框比例1:1
 preview: '.img-preview',
 crop: function (e) {
  $dataX.val(Math.round(e.x));
  $dataY.val(Math.round(e.y));
  $dataHeight.val(Math.round(e.height));
  $dataWidth.val(Math.round(e.width));
  $dataRotate.val(e.rotate);
  $dataScaleX.val(e.scaleX);
  $dataScaleY.val(e.scaleY);
 }
 };
 var originalImageURL = $image.attr('src');
 var uploadedImageURL;
 // Tooltip
 $('[data-toggle="tooltip"]').tooltip();
 // Cropper
 $image.on({
 ready: function (e) {
 console.log(e.type);
 },
 cropstart: function (e) {
 console.log(e.type, e.action);
 },
 cropmove: function (e) {
 console.log(e.type, e.action);
 },
 cropend: function (e) {
 console.log(e.type, e.action);
 },
 crop: function (e) {
 console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);
 },
 zoom: function (e) {
 console.log(e.type, e.ratio);
 }
 }).cropper(options);
 // Buttons
 if (!$.isFunction(document.createElement('canvas').getContext)) {
 $('button[data-method="getCroppedCanvas"]').prop('disabled', true);
 }
 if (typeof document.createElement('cropper').style.transition === 'undefined') {
 $('button[data-method="rotate"]').prop('disabled', true);
 $('button[data-method="scale"]').prop('disabled', true);
 }
 // Download
 if (typeof $download[0].download === 'undefined') {
 $download.addClass('disabled');
 }
 // Options
 $('.docs-toggles').on('change', 'input', function () {
 var $this = $(this);
 var name = $this.attr('name');
 var type = $this.prop('type');
 var cropBoxData;
 var canvasData;
 if (!$image.data('cropper')) {
 return;
 }
 if (type === 'checkbox') {
 options[name] = $this.prop('checked');
 cropBoxData = $image.cropper('getCropBoxData');
 canvasData = $image.cropper('getCanvasData');
 options.ready = function () {
 $image.cropper('setCropBoxData', cropBoxData);
 $image.cropper('setCanvasData', canvasData);
 };
 } else if (type === 'radio') {
 options[name] = $this.val();
 }
 $image.cropper('destroy').cropper(options);
 });
 // Methods
 // 点击开始计算图片位置,获取位置
 $('.docs-buttons').on('click', '[data-method]', function () {
 var $this = $(this);
 var data = $this.data();
 var $target;
 var result;
 if ($this.prop('disabled') || $this.hasClass('disabled')) {
 return;
 }
 if ($image.data('cropper') && data.method) {
 data = $.extend({}, data); // Clone a new one
 if (typeof data.target !== 'undefined') {
 $target = $(data.target);
 if (typeof data.option === 'undefined') {
  try {
  data.option = JSON.parse($target.val());
  } catch (e) {
  console.log(e.message);
  }
 }
 }
 if (data.method === 'rotate') {
 $image.cropper('clear');
 }
 result = $image.cropper(data.method, data.option, data.secondOption);
 if (data.method === 'rotate') {
 $image.cropper('crop');
 }
 switch (data.method) {
 case 'scaleX':
 case 'scaleY':
  $(this).data('option', -data.option);
  break;
 case 'getCroppedCanvas':
 //上传头像
  if (result) {
  var imgBase=result.toDataURL('image/jpeg');
  var data={imgBase:imgBase};
  $.post('/docs/upload.php',data,function(ret){
  if(ret=='true'){
  alert('上传成功');
  }else{
  alert('上传失败');
  }
  },'text');
  }
  break;
 case 'destroy':
  if (uploadedImageURL) {
  URL.revokeObjectURL(uploadedImageURL);
  uploadedImageURL = '';
  $image.attr('src', originalImageURL);
  }
  break;
 }
 if ($.isPlainObject(result) && $target) {
 try {
  $target.val(JSON.stringify(result));
 } catch (e) {
  console.log(e.message);
 }
 }
 }
 });
 // Keyboard
 $(document.body).on('keydown', function (e) {
 if (!$image.data('cropper') || this.scrollTop > 300) {
 return;
 }
 switch (e.which) {
 case 37:
 e.preventDefault();
 $image.cropper('move', -1, 0);
 break;
 case 38:
 e.preventDefault();
 $image.cropper('move', 0, -1);
 break;
 case 39:
 e.preventDefault();
 $image.cropper('move', 1, 0);
 break;
 case 40:
 e.preventDefault();
 $image.cropper('move', 0, 1);
 break;
 }
 });
 // Import image
 var $inputImage = $('#inputImage');
 if (URL) {
 $inputImage.change(function () {
 var files = this.files;
 var file;
 if (!$image.data('cropper')) {
 return;
 }
 if (files && files.length) {
 file = files[0];
 if (/^image\/\w+$/.test(file.type)) {
  if (uploadedImageURL) {
  URL.revokeObjectURL(uploadedImageURL);
  }
  uploadedImageURL = URL.createObjectURL(file);
  $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);
  $inputImage.val('');
 } else {
  window.alert('Please choose an image file.');
 }
 }
 });
 } else {
 $inputImage.prop('disabled', true).parent().addClass('disabled');
 }
});

使用canvas生成的截图转换生成base64代码。

后台处理base64代码片段(PHP服务端)。

upload.php代码如下:

<?php
 error_reporting(0);//禁用错误报告 
 if (IS_POST) {
 header('Content-type:text/html;charset=utf-8');
 $base64_image_content = $_POST['imgBase'];
 //将base64编码转换为图片保存
 if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)) {
  $type = $result[2];
  $new_file = "./uploads/";
  if (!file_exists($new_file)) {
  //检查是否有该文件夹,如果没有就创建,并给予最高权限
  mkdir($new_file, 0700);
  }
  $img=time() . ".{$type}";
  $new_file = $new_file . $img;
  //将图片保存到指定的位置
  if (file_put_contents($new_file, base64_decode(str_replace($result[1], '', $base64_image_content)))) {
  echo 'true';
  }else{
  echo 'false';
  }
 }else{
  echo 'false';
 }
 }
?>

实现效果:

Cropper.js 实现裁剪图片并上传(PC端)

选择要截取的图片位置,按“上传头像”按钮,截取的图片就上传并保存到服务器本站点目录的uploads文件夹中。

总结

以上所述是小编给大家介绍的Cropper.js 实现裁剪图片并上传(PC端) ,希望对大家有所帮助,

Javascript 相关文章推荐
IE和FireFox(FF)中js和css的不同
Apr 13 Javascript
单击复制文字兼容各浏览器的完美解决方案
Jul 04 Javascript
jQuery ui实现动感的圆角渐变网站导航菜单效果代码
Aug 26 Javascript
快速掌握Node.js中setTimeout和setInterval的使用方法
Mar 21 Javascript
seajs学习之模块的依赖加载及模块API的导出
Oct 20 Javascript
零基础轻松学JavaScript闭包
Dec 30 Javascript
JS将unicode码转中文方法
May 08 Javascript
JS实现按钮添加背景音乐示例代码
Oct 17 Javascript
vue-cli配置全局sass、less变量的方法
Jun 06 Javascript
jquery实现垂直无限轮播的方法分析
Jul 16 jQuery
JS使用正则表达式判断输入框失去焦点事件
Oct 16 Javascript
Vue.js获取手机系统型号、版本、浏览器类型的示例代码
May 10 Javascript
Bootstrap 模态框(Modal)带参数传值实例
Aug 20 #Javascript
mui 打开新窗口的方式总结及注意事项
Aug 20 #Javascript
ES6扩展运算符的用途实例详解
Aug 20 #Javascript
关于JavaScript的单双引号嵌套问题
Aug 20 #Javascript
Vue动态组件实例解析
Aug 20 #Javascript
jQuery实现表格冻结顶栏效果
Aug 20 #jQuery
Vue组件实例间的直接访问实现代码
Aug 20 #Javascript
You might like
CI框架中cookie的操作方法分析
2014/12/12 PHP
php实现基于微信公众平台开发SDK(demo)扩展的方法
2014/12/22 PHP
php结合正则获取字符串中数字
2015/06/19 PHP
phpcms实现验证码替换及phpcms实现全站搜索功能教程详解
2017/12/13 PHP
php封装的page分页类完整实例代码
2020/02/01 PHP
ext combox 下拉框不出现自动提示,自动选中的解决方法
2010/02/24 Javascript
javascript中的window.location.search方法简介
2013/09/02 Javascript
Javascript中的匿名函数与封装介绍
2015/03/15 Javascript
jQuery插件jRumble实现网页元素抖动
2015/06/05 Javascript
AngularGauge 属性解析详解
2016/09/06 Javascript
Bootstrap源码解读模态弹出框(11)
2016/12/28 Javascript
关于jQuery中fade(),show()起始位置的一点小发现
2017/04/25 jQuery
微信小程序页面缩放式侧滑效果的实现代码
2018/11/15 Javascript
Vue 动态路由的实现及 Springsecurity 按钮级别的权限控制
2019/09/05 Javascript
深入理解redux之compose的具体应用
2020/01/12 Javascript
python中实现迭代器(iterator)的方法示例
2017/01/19 Python
PyGame贪吃蛇的实现代码示例
2018/11/21 Python
Python面向对象类编写细节分析【类,方法,继承,超类,接口等】
2019/01/05 Python
Python 限制线程的最大数量的方法(Semaphore)
2019/02/22 Python
Django集成CAS单点登录的方法示例
2019/06/10 Python
django 自定义filter 判断if var in list的例子
2019/08/20 Python
详解pycharm的python包opencv(cv2)无代码提示问题的解决
2021/01/29 Python
利用CSS3的线性渐变linear-gradient制作边框的示例
2016/06/02 HTML / CSS
GLAMGLOW格莱魅美国官网:美国知名的面膜品牌
2016/12/31 全球购物
Amara德国:家居饰品、设计师品牌和豪华礼品
2019/05/20 全球购物
介绍一下RMI的基本概念
2016/12/17 面试题
Java基础面试题
2014/07/19 面试题
数控技术与应用毕业生自荐信
2013/09/24 职场文书
《邮票齿孔的故事》教学反思
2014/02/22 职场文书
眼镜促销方案
2014/03/15 职场文书
关于读书的演讲稿400字
2014/08/27 职场文书
群众路线自查报告及整改措施
2014/11/04 职场文书
见习报告格式要求
2014/11/04 职场文书
个人培训总结
2015/03/05 职场文书
导游词之峨眉山
2019/12/16 职场文书
Redis 持久化 RDB 与 AOF的执行过程
2021/11/07 Redis