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 相关文章推荐
基于json的jquery地区联动效果代码
Jul 06 Javascript
ie7+背景透明文字不透明超级简单的实现方法
Jan 17 Javascript
基于JQuery制作可编辑的表格特效
Dec 23 Javascript
安装使用Mongoose配合Node.js操作MongoDB的基础教程
Mar 01 Javascript
基于jQuery实现仿51job城市选择功能实例代码
Mar 02 Javascript
js 点击a标签 获取a的自定义属性方法
Nov 21 Javascript
String字符串截取的四种方式总结
Nov 28 Javascript
浅析Vue 生命周期
Jun 21 Javascript
小程序开发中如何使用async-await并封装公共异步请求的方法
Jan 20 Javascript
JS删除String里某个字符的方法
Jan 06 Javascript
js canvas实现俄罗斯方块
Oct 11 Javascript
JS数组的常用方法整理
Mar 31 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
虹吸式咖啡壶操作
2021/03/03 冲泡冲煮
thinkphp3.2.3版本的数据库增删改查实现代码
2016/09/22 PHP
Linux下快速搭建php开发环境
2017/03/13 PHP
Laravel框架FormRequest中重写错误处理的方法
2019/02/18 PHP
JavaScript 字符串与数组转换函数[不用split与join]
2009/12/13 Javascript
十个迅速提升JQuery性能让你的JQuery跑得更快
2012/12/10 Javascript
Javascript 垃圾收集机制介绍理解
2013/05/14 Javascript
jQuery学习笔记之总体架构
2014/06/03 Javascript
jQuery实现指定区域外单击关闭指定层的方法【经典】
2016/06/22 Javascript
微信小程序 加载 app-service.js 错误解决方法
2016/10/12 Javascript
浅谈html转义及防止javascript注入攻击的方法
2016/12/04 Javascript
nodejs个人博客开发第七步 后台登陆
2017/04/12 NodeJs
基于JavaScript实现简单的音频播放功能
2018/01/07 Javascript
JavaScript实现学生在线做题计时器功能
2018/12/05 Javascript
判断js数据类型的函数实例详解
2019/05/23 Javascript
[02:57]DOTA2英雄基础教程 风行者
2014/01/16 DOTA
[00:12]2018DOTA2亚洲邀请赛 Sccc亮相SOLO赛,今年他又会有什么样的战绩?
2018/04/06 DOTA
python通过定义一个类实例作为ftp回调方法
2015/05/04 Python
Python实现将绝对URL替换成相对URL的方法
2015/06/28 Python
python进行TCP端口扫描的实现
2018/12/21 Python
PyCharm的设置方法和第一个Python程序的建立
2019/01/16 Python
Django之无名分组和有名分组的实现
2019/04/16 Python
Python Des加密解密如何实现软件注册码机器码
2020/01/08 Python
python函数定义和调用过程详解
2020/02/09 Python
html5开发之viewport使用
2013/10/17 HTML / CSS
时尚圣经:The Fashion Bible
2019/03/03 全球购物
什么时候需要进行强制类型转换
2016/09/03 面试题
优秀士兵个人事迹材料
2014/01/19 职场文书
企业法人代表任命书
2014/06/06 职场文书
“六查”、“三学”、“三干”查摆问题整改措施
2014/09/27 职场文书
村干部四风问题整改措施
2014/09/30 职场文书
党的群众路线整改落实情况汇报
2014/10/28 职场文书
部门经理助理岗位职责
2015/04/13 职场文书
活着观后感
2015/06/03 职场文书
nginx部署多前端项目的几种方法
2021/05/25 Servers
用Python创建简易网站图文教程
2021/06/11 Python