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 相关文章推荐
Jquery 插件开发笔记整理
Jan 17 Javascript
JavaScript prototype 使用介绍
Aug 29 Javascript
Javascript检查图片大小不要让大图片撑破页面
Nov 04 Javascript
Javascript oop设计模式 面向对象编程简单实例介绍
Dec 13 Javascript
使用jQuery卸载全部事件的思路详解
Apr 03 jQuery
layer弹窗插件操作方法详解
May 19 Javascript
详解angularjs中的隔离作用域理解以及绑定策略
May 31 Javascript
Angular在模板驱动表单中自定义校验器的方法
Aug 09 Javascript
webpack构建换肤功能的思路详解
Nov 27 Javascript
微信小程序 scroll-view实现锚点滑动的示例
Dec 06 Javascript
vue实现一个矩形标记区域(rectangle marker)的方法
Oct 28 Javascript
JS实现简单控制视频播放倍速的实例代码
Apr 18 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
单一index.php实现PHP任意层级文件夹遍历(Zjmainstay原创)
2012/07/31 PHP
thinkphp中html:list标签传递多个参数实例
2014/10/30 PHP
PHP学习笔记(三):数据类型转换与常量介绍
2015/04/17 PHP
PHP基于array_unique实现二维数组去重
2020/07/14 PHP
前淘宝前端开发工程师阿当的PPT中有JS技术理念问题
2010/01/15 Javascript
javascript预览上传图片发现的问题的解决方法
2010/11/25 Javascript
JQuery select控件的相关操作实现代码
2012/09/14 Javascript
使用jquery.qrcode生成彩色二维码实例
2014/08/08 Javascript
jQuery中[attribute!=value]选择器用法实例
2014/12/31 Javascript
JavaScript插件化开发教程 (四)
2015/01/27 Javascript
jQuery实现仿腾讯迷你首页选项卡效果代码
2015/09/17 Javascript
简单谈谈Javascript中类型的判断
2015/10/19 Javascript
Validform+layer实现漂亮的表单验证特效
2016/01/17 Javascript
js处理层级数据结构的方法小结
2017/01/17 Javascript
JS实现含有中文字符串的友好截取功能分析
2017/03/13 Javascript
Angular5.1新功能分享
2017/12/21 Javascript
使用mint-ui实现省市区三级联动效果的示例代码
2018/02/09 Javascript
jquery拖拽自动排序插件使用方法详解
2020/07/20 jQuery
Layui实现数据表格中鼠标悬浮图片放大效果,离开时恢复原图的方法
2019/09/11 Javascript
如何在vue 中使用柱状图 并自修改配置
2021/01/21 Vue.js
[55:44]完美世界DOTA2联赛决赛 FTD vs Phoenix 第二场 11.08
2020/11/11 DOTA
python实现多线程暴力破解登陆路由器功能代码分享
2015/01/04 Python
python制作一个桌面便签软件
2015/08/09 Python
python绘制热力图heatmap
2020/03/23 Python
Python装饰器用法实例分析
2019/01/14 Python
Python微信操控itchat的方法
2019/05/31 Python
win10安装tesserocr配置 Python使用tesserocr识别字母数字验证码
2020/01/16 Python
tensorflow使用freeze_graph.py将ckpt转为pb文件的方法
2020/04/22 Python
keras 实现轻量级网络ShuffleNet教程
2020/06/19 Python
浅析PyCharm 的初始设置(知道)
2020/10/12 Python
python的setattr函数实例用法
2020/12/16 Python
俄罗斯药房连锁店:ASNA
2020/06/20 全球购物
2015年重阳节活动总结
2015/03/24 职场文书
大学优秀学生主要事迹材料
2015/11/04 职场文书
团干部培训班心得体会
2016/01/06 职场文书
实战Python爬虫爬取酷我音乐
2022/04/11 Python