"Image Crop jQuery"
Bootstrap 3.3.0 Snippet by ExogenesisBen

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!-- Content --> <div class="container"> <div class="row"> <div class="col-md-9"> <!-- <h3 class="page-header">Demo:</h3> --> <div class="img-container"> <img src="https://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-xpf1/t31.0-8/134618_418890451497724_216755457_o.jpg" alt="Picture"> </div> </div> <div class="col-md-3"> <!-- <h3 class="page-header">Preview:</h3> --> <div class="docs-preview clearfix"> <div class="img-preview preview-lg"></div> <div class="img-preview preview-md"></div> <div class="img-preview preview-sm"></div> <div class="img-preview preview-xs"></div> </div> <!-- <h3 class="page-header">Data:</h3> --> <div class="docs-data"> <div class="input-group"> <label class="input-group-addon" for="dataX">X</label> <input class="form-control" id="dataX" type="text" placeholder="x"> <span class="input-group-addon">px</span> </div> <div class="input-group"> <label class="input-group-addon" for="dataY">Y</label> <input class="form-control" id="dataY" type="text" placeholder="y"> <span class="input-group-addon">px</span> </div> <div class="input-group"> <label class="input-group-addon" for="dataWidth">Width</label> <input class="form-control" id="dataWidth" type="text" placeholder="width"> <span class="input-group-addon">px</span> </div> <div class="input-group"> <label class="input-group-addon" for="dataHeight">Height</label> <input class="form-control" id="dataHeight" type="text" placeholder="height"> <span class="input-group-addon">px</span> </div> <div class="input-group"> <label class="input-group-addon" for="dataRotate">Rotate</label> <input class="form-control" id="dataRotate" type="text" placeholder="rotate"> <span class="input-group-addon">deg</span> </div> </div> </div> </div> <div class="row"> <div class="col-md-9 docs-buttons"> <!-- <h3 class="page-header">Toolbar:</h3> --> <div class="btn-group"> <button class="btn btn-primary" data-method="setDragMode" data-option="move" type="button" title="Move"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setDragMode", "move")"> <span class="icon icon-move"></span> </span> </button> <button class="btn btn-primary" data-method="setDragMode" data-option="crop" type="button" title="Crop"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setDragMode", "crop")"> <span class="icon icon-crop"></span> </span> </button> <button class="btn btn-primary" data-method="zoom" data-option="0.1" type="button" title="Zoom In"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("zoom", 0.1)"> <span class="icon icon-zoom-in"></span> </span> </button> <button class="btn btn-primary" data-method="zoom" data-option="-0.1" type="button" title="Zoom Out"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("zoom", -0.1)"> <span class="icon icon-zoom-out"></span> </span> </button> <button class="btn btn-primary" data-method="rotate" data-option="-45" type="button" title="Rotate Left"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", -45)"> <span class="icon icon-rotate-left"></span> </span> </button> <button class="btn btn-primary" data-method="rotate" data-option="45" type="button" title="Rotate Right"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", 45)"> <span class="icon icon-rotate-right"></span> </span> </button> </div> <div class="btn-group"> <button class="btn btn-primary" data-method="crop" type="button" title="Crop"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("crop")"> <span class="icon icon-ok"></span> </span> </button> <button class="btn btn-primary" data-method="clear" type="button" title="Clear"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("clear")"> <span class="icon icon-remove"></span> </span> </button> <button class="btn btn-primary" data-method="disable" type="button" title="Disable"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("disable")"> <span class="icon icon-lock"></span> </span> </button> <button class="btn btn-primary" data-method="enable" type="button" title="Enable"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("enable")"> <span class="icon icon-unlock"></span> </span> </button> <button class="btn btn-primary" data-method="reset" type="button" title="Reset"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("reset")"> <span class="icon icon-refresh"></span> </span> </button> <label class="btn btn-primary btn-upload" for="inputImage" title="Upload image file"> <input class="sr-only" id="inputImage" name="file" type="file" accept="image/*"> <span class="docs-tooltip" data-toggle="tooltip" title="Import image with Blob URLs"> <span class="icon icon-upload"></span> </span> </label> <button class="btn btn-primary" data-method="destroy" type="button" title="Destroy"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("destroy")"> <span class="icon icon-off"></span> </span> </button> </div> <div class="btn-group btn-group-crop"> <button class="btn btn-primary" data-method="getCroppedCanvas" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getCroppedCanvas")"> Get Cropped Canvas </span> </button> <button class="btn btn-primary" data-method="getCroppedCanvas" data-option="{ "width": 160, "height": 90 }" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getCroppedCanvas", { width: 160, height: 90 })"> 160×90 </span> </button> <button class="btn btn-primary" data-method="getCroppedCanvas" data-option="{ "width": 320, "height": 180 }" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getCroppedCanvas", { width: 320, height: 180 })"> 320×180 </span> </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"> <button class="close" data-dismiss="modal" type="button" aria-hidden="true">×</button> <h4 class="modal-title" id="getCroppedCanvasTitle">Cropped</h4> </div> <div class="modal-body"></div> <!-- <div class="modal-footer"> <button class="btn btn-primary" data-dismiss="modal" type="button">Close</button> </div> --> </div> </div> </div><!-- /.modal --> <button class="btn btn-primary" data-method="getData" data-option="" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getData")"> Get Data </span> </button> <button class="btn btn-primary" data-method="setData" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setData", data)"> Set Data </span> </button> <button class="btn btn-primary" data-method="getContainerData" data-option="" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getContainerData")"> Get Container Data </span> </button> <button class="btn btn-primary" data-method="getImageData" data-option="" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getImageData")"> Get Image Data </span> </button> <button class="btn btn-primary" data-method="getCanvasData" data-option="" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getCanvasData")"> Get Canvas Data </span> </button> <button class="btn btn-primary" data-method="setCanvasData" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setCanvasData", data)"> Set Canvas Data </span> </button> <button class="btn btn-primary" data-method="getCropBoxData" data-option="" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("getCropBoxData")"> Get Crop Box Data </span> </button> <button class="btn btn-primary" data-method="setCropBoxData" data-target="#putData" type="button"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setCropBoxData", data)"> Set Crop Box Data </span> </button> <input class="form-control" id="putData" type="text" placeholder="Get data to here or set data with this value"> </div><!-- /.docs-buttons --> <div class="col-md-3 docs-toggles"> <!-- <h3 class="page-header">Toggles:</h3> --> <div class="btn-group btn-group-justified" data-toggle="buttons"> <label class="btn btn-primary active" data-method="setAspectRatio" data-option="1.7777777777777777" title="Set Aspect Ratio"> <input class="sr-only" id="aspestRatio1" name="aspestRatio" value="1.7777777777777777" type="radio"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setAspectRatio", 16 / 9)"> 16:9 </span> </label> <label class="btn btn-primary" data-method="setAspectRatio" data-option="1.3333333333333333" title="Set Aspect Ratio"> <input class="sr-only" id="aspestRatio2" name="aspestRatio" value="1.3333333333333333" type="radio"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setAspectRatio", 4 / 3)"> 4:3 </span> </label> <label class="btn btn-primary" data-method="setAspectRatio" data-option="1" title="Set Aspect Ratio"> <input class="sr-only" id="aspestRatio3" name="aspestRatio" value="1" type="radio"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setAspectRatio", 1 / 1)"> 1:1 </span> </label> <label class="btn btn-primary" data-method="setAspectRatio" data-option="0.6666666666666666" title="Set Aspect Ratio"> <input class="sr-only" id="aspestRatio4" name="aspestRatio" value="0.6666666666666666" type="radio"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setAspectRatio", 2 / 3)"> 2:3 </span> </label> <label class="btn btn-primary" data-method="setAspectRatio" data-option="NaN" title="Set Aspect Ratio"> <input class="sr-only" id="aspestRatio5" name="aspestRatio" value="NaN" type="radio"> <span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("setAspectRatio", NaN)"> Free </span> </label> </div> <div class="dropdown dropup docs-options"> <button class="btn btn-primary btn-block dropdown-toggle" id="toggleOptions" type="button" data-toggle="dropdown" aria-expanded="true"> Toggle Options <span class="caret"></span> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="toggleOptions"> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="strict" checked> strict </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="responsive" checked> responsive </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="checkImageOrigin" checked> checkImageOrigin </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="modal" checked> modal </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="guides" checked> guides </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="highlight" checked> highlight </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="background" checked> background </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="autoCrop" checked> autoCrop </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="dragCrop" checked> dragCrop </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="movable" checked> movable </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="resizable" checked> resizable </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="rotatable" checked> rotatable </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="zoomable" checked> zoomable </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="touchDragZoom" checked> touchDragZoom </label> </li> <li role="presentation"> <label class="checkbox-inline"> <input type="checkbox" name="option" value="mouseWheelZoom" checked> mouseWheelZoom </label> </li> </ul> </div><!-- /.dropdown --> </div><!-- /.docs-toggles --> </div> </div> <!-- Alert --> <div class="docs-alert"><span class="warning message"></span></div>
.cropper-container { position: relative; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; } .cropper-container img { display: block; width: 100%; min-width: 0 !important; max-width: none !important; height: 100%; min-height: 0 !important; max-height: none !important; image-orientation: 0deg !important; } .cropper-canvas, .cropper-drag-box, .cropper-crop-box, .cropper-modal { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } .cropper-drag-box { background-color: #fff; filter: alpha(opacity=0); opacity: 0; } .cropper-modal { background-color: #000; filter: alpha(opacity=50); opacity: .5; } .cropper-view-box { display: block; width: 100%; height: 100%; overflow: hidden; outline: 1px solid #69f; outline-color: rgba(102, 153, 255, .75); } .cropper-dashed { position: absolute; display: block; filter: alpha(opacity=50); border: 0 dashed #fff; opacity: .5; } .cropper-dashed.dashed-h { top: 33.33333333%; left: 0; width: 100%; height: 33.33333333%; border-top-width: 1px; border-bottom-width: 1px; } .cropper-dashed.dashed-v { top: 0; left: 33.33333333%; width: 33.33333333%; height: 100%; border-right-width: 1px; border-left-width: 1px; } .cropper-face, .cropper-line, .cropper-point { position: absolute; display: block; width: 100%; height: 100%; filter: alpha(opacity=10); opacity: .1; } .cropper-face { top: 0; left: 0; cursor: move; background-color: #fff; } .cropper-line { background-color: #69f; } .cropper-line.line-e { top: 0; right: -3px; width: 5px; cursor: e-resize; } .cropper-line.line-n { top: -3px; left: 0; height: 5px; cursor: n-resize; } .cropper-line.line-w { top: 0; left: -3px; width: 5px; cursor: w-resize; } .cropper-line.line-s { bottom: -3px; left: 0; height: 5px; cursor: s-resize; } .cropper-point { width: 5px; height: 5px; background-color: #69f; filter: alpha(opacity=75); opacity: .75; } .cropper-point.point-e { top: 50%; right: -3px; margin-top: -3px; cursor: e-resize; } .cropper-point.point-n { top: -3px; left: 50%; margin-left: -3px; cursor: n-resize; } .cropper-point.point-w { top: 50%; left: -3px; margin-top: -3px; cursor: w-resize; } .cropper-point.point-s { bottom: -3px; left: 50%; margin-left: -3px; cursor: s-resize; } .cropper-point.point-ne { top: -3px; right: -3px; cursor: ne-resize; } .cropper-point.point-nw { top: -3px; left: -3px; cursor: nw-resize; } .cropper-point.point-sw { bottom: -3px; left: -3px; cursor: sw-resize; } .cropper-point.point-se { right: -3px; bottom: -3px; width: 20px; height: 20px; cursor: se-resize; filter: alpha(opacity=100); opacity: 1; } .cropper-point.point-se:before { position: absolute; right: -50%; bottom: -50%; display: block; width: 200%; height: 200%; content: " "; background-color: #69f; filter: alpha(opacity=0); opacity: 0; } @media (min-width: 768px) { .cropper-point.point-se { width: 15px; height: 15px; } } @media (min-width: 992px) { .cropper-point.point-se { width: 10px; height: 10px; } } @media (min-width: 1200px) { .cropper-point.point-se { width: 5px; height: 5px; filter: alpha(opacity=75); opacity: .75; } } .cropper-bg { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC"); } .cropper-invisible { filter: alpha(opacity=0); opacity: 0; } .cropper-hide { position: fixed; top: 0; left: 0; z-index: -1; width: auto!important; min-width: 0!important; max-width: none!important; height: auto!important; min-height: 0!important; max-height: none!important; filter: alpha(opacity=0); opacity: 0; } .cropper-hidden { display: none !important; } .cropper-move { cursor: move; } .cropper-crop { cursor: crosshair; } .cropper-disabled .cropper-drag-box, .cropper-disabled .cropper-face, .cropper-disabled .cropper-line, .cropper-disabled .cropper-point { cursor: not-allowed; } /*Main CSS*/ .icon { display: inline-block; width: 20px; height: 20px; background-image: url("http://fengyuanchen.github.io/cropper/img/icons.png"); vertical-align: middle; } .icon-move { background-position: 0 0; } .icon-crop { background-position: -30px 0; } .icon-zoom-in { background-position: -60px 0; } .icon-zoom-out { background-position: -90px 0; } .icon-rotate-left { background-position: -120px 0; } .icon-rotate-right { background-position: -150px 0; } .icon-lock { background-position: -180px 0; } .icon-unlock { background-position: -210px 0; } .icon-remove { background-position: -240px 0; } .icon-refresh { background-position: -270px 0; } .icon-upload { background-position: -300px 0; } .icon-off { background-position: -330px 0; } .icon-info { background-position: -360px 0; } .icon-ok { background-position: -390px 0; } /* Alerts * -------------------------------------------------------------------------- */ .docs-alert { display: none; position: fixed; top: 20px; left: 0; right: 0; height: 0; text-align: center; opacity: 0.9; } .docs-alert .message { display: inline-block; padding: 5px 10px; border-radius: 2px; background-color: #aaa; color: #fff; } .docs-alert .primary { background-color: #0074d9; } .docs-alert .success { background-color: #2ecc40; } .docs-alert .info { background-color: #39cccc; } .docs-alert .warning { background-color: #ff851b; } .docs-alert .danger { background-color: #ff4136; } /* Button * -------------------------------------------------------------------------- */ .btn-primary { border-color: #003973; /* hsb(210, 100%, 45%) */ background-color: #00468c; /* hsb(210, 100%, 55%) */ } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .btn-primary.active:focus, .btn-primary.active:hover { border-color: #00264d; /* hsb(208, 100%, 10%) */ background-color: #003366; /* hsb(208, 100%, 40%) */ } /* Basic style * -------------------------------------------------------------------------- */ body { overflow-x: hidden; } /* Header */ .docs-header { border-color: #003973; background-color: #00468c; color: #fff; } .docs-header .navbar-brand { color: #eee; } .docs-header .navbar-toggle { border-color: #003973; background-color: #00468c; } .docs-header .navbar-toggle:hover, .docs-header .navbar-toggle:focus { border-color: #003366; background-color: #003973; } .docs-header .navbar-collapse { border-color: #003973; } .docs-header .navbar-text { color: #ddd; } .docs-header .navbar-nav > li > a { color: #eee; } /* Content */ .img-container, .img-preview { background-color: #f7f7f7; overflow: hidden; width: 100%; text-align: center; } .img-container { min-height: 200px; max-height: 466px; margin-bottom: 20px; } @media (min-width: 768px) { .img-container { min-height: 466px; } } .img-container > img { max-width: 100%; } .docs-preview { margin-right: -15px; margin-bottom: 10px; } .img-preview { float: left; margin-right: 10px; margin-bottom: 10px; } .img-preview > img { max-width: 100%; } .preview-lg { width: 263px; height: 148px; } .preview-md { width: 139px; height: 78px; } .preview-sm { width: 69px; height: 39px; } .preview-xs { width: 35px; height: 20px; margin-right: 0; } .docs-data > .input-group { margin-bottom: 10px; } .docs-data > .input-group > label { min-width: 80px; } .docs-data > .input-group > span { min-width: 50px; } .docs-buttons > .btn, .docs-buttons > .btn-group, .docs-buttons > .form-control { margin-right: 5px; margin-bottom: 10px; } .docs-toggles > .btn, .docs-toggles > .btn-group, .docs-toggles > .dropdown { margin-bottom: 10px; } .docs-tooltip { display: block; margin: -6px -12px; padding: 6px 12px; } .docs-tooltip > .icon { margin: 0 -3px; vertical-align: top; } .tooltip-inner { white-space: normal; } .btn-upload .tooltip-inner { white-space: nowrap; } @media (max-width: 400px) { .btn-group-crop { margin-right: -15px!important; } .btn-group-crop > .btn { padding-left: 5px; padding-right: 5px; } .btn-group-crop .docs-tooltip { margin-left: -5px; margin-right: -5px; padding-left: 5px; padding-right: 5px; } } .docs-options .dropdown-menu { width: 100%; } .docs-options .dropdown-menu > li { padding: 3px 20px; } .docs-options .dropdown-menu > li:hover { background-color: #f7f7f7; } .docs-options .dropdown-menu > li > label { display: block; } .docs-cropped .modal-body { text-align: center; } .docs-cropped .modal-body > img, .docs-cropped .modal-body > canvas { max-width: 100%; }
(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' && !isNaN(n); } 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 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')) { return; } url = $this.prop('src'); } else if ($this.is('canvas') && SUPPORT_CANVAS) { url = $this[0].toDataURL(); } } 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 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({ crossOrigin: crossOrigin, // "crossOrigin" must before "src" (#271) src: bustCacheUrl || url }); // 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.setData(options.data); $this.one(EVENT_BUILT, options.built).trigger(EVENT_BUILT); // Only trigger once }; prototype.unbuild = function () { if (!this.built) { return; } this.built = false; this.initialImage = null; this.initialCanvas = null; // This is necessary when replace this.initialCropBox = null; 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, initialCanvas = this.initialCanvas || canvas, initialCanvasWidth = initialCanvas.width, initialCanvasHeight = initialCanvas.height, minCanvasWidth, minCanvasHeight; if (size) { minCanvasWidth = num(options.minCanvasWidth) || 0; minCanvasHeight = num(options.minCanvasHeight) || 0; if (minCanvasWidth) { if (strict) { minCanvasWidth = max(cropped ? cropBox.width : initialCanvasWidth, minCanvasWidth); } minCanvasHeight = minCanvasWidth / aspectRatio; } else if (minCanvasHeight) { if (strict) { minCanvasHeight = max(cropped ? cropBox.height : initialCanvasHeight, 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 = initialCanvasWidth; minCanvasHeight = initialCanvasHeight; } } $.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) { 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) { 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, { crop: function () { if (!this.built || this.disabled) { return; } if (!this.cropped) { this.cropped = true; this.limitCropBox(true, true); if (this.options.modal) { this.$dragBox.addClass(CLASS_MODAL); } this.$cropBox.removeClass(CLASS_HIDDEN); } this.setCropBoxData(this.initialCropBox); }, reset: function () { if (!this.built || this.disabled) { return; } this.image = $.extend({}, this.initialImage); this.canvas = $.extend({}, this.initialCanvas); this.cropBox = $.extend({}, this.initialCropBox); // required for strict mode this.renderCanvas(); if (this.cropped) { 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 if (this.$clone) { this.$clone.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 = this.ready ? image.rotate : 0; return data; }, setData: function (data) { var image = this.image, canvas = this.canvas, cropBoxData = {}, ratio; if (this.built && !this.disabled && $.isPlainObject(data)) { if (isNumber(data.rotate) && data.rotate !== image.rotate && this.options.rotatable) { image.rotate = data.rotate; this.rotated = true; this.renderCanvas(true); } ratio = image.width / image.naturalWidth; if (isNumber(data.x)) { cropBoxData.left = data.x * ratio + canvas.left; } if (isNumber(data.y)) { cropBoxData.top = data.y * ratio + canvas.top; } if (isNumber(data.width)) { cropBoxData.width = data.width * ratio; } if (isNumber(data.height)) { cropBoxData.height = data.height * ratio; } this.setCropBoxData(cropBoxData); } }, 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 (isNumber(data.width)) { cropBox.width = data.width; } if (isNumber(data.height)) { cropBox.height = data.height; } if (aspectRatio) { if (isNumber(data.width)) { cropBox.height = cropBox.width / aspectRatio; } else if (isNumber(data.height)) { cropBox.width = cropBox.height * aspectRatio; } } 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 > minTop) { 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 > minLeft) { 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 > minTop) { 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 > minLeft) { 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, // Previous/latest crop data // Type: Object data: 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 () { 'use strict'; var console = window.console || { log: function () {} }, $alert = $('.docs-alert'), $message = $alert.find('.message'), showMessage = function (message, type) { $message.text(message); if (type) { $message.addClass(type); } $alert.fadeIn(); setTimeout(function () { $alert.fadeOut(); }, 3000); }; // Demo // ------------------------------------------------------------------------- (function () { var $image = $('.img-container > img'), $dataX = $('#dataX'), $dataY = $('#dataY'), $dataHeight = $('#dataHeight'), $dataWidth = $('#dataWidth'), $dataRotate = $('#dataRotate'), options = { // data: { // x: 420, // y: 60, // width: 640, // height: 360 // }, // strict: false, // responsive: false, // checkImageOrigin: false // modal: false, // guides: false, // highlight: false, // background: false, // autoCrop: false, // autoCropArea: 0.5, // dragCrop: false, // movable: false, // resizable: false, // rotatable: false, // zoomable: false, // touchDragZoom: false, // mouseWheelZoom: false, // minCanvasWidth: 320, // minCanvasHeight: 180, // minCropBoxWidth: 160, // minCropBoxHeight: 90, // minContainerWidth: 320, // minContainerHeight: 180, // build: null, // built: null, // dragstart: null, // dragmove: null, // dragend: null, // zoomin: null, // zoomout: null, aspectRatio: 16 / 9, preview: '.img-preview', crop: function (data) { $dataX.val(Math.round(data.x)); $dataY.val(Math.round(data.y)); $dataHeight.val(Math.round(data.height)); $dataWidth.val(Math.round(data.width)); $dataRotate.val(Math.round(data.rotate)); } }; $image.on({ 'build.cropper': function (e) { console.log(e.type); }, 'built.cropper': function (e) { console.log(e.type); }, 'dragstart.cropper': function (e) { console.log(e.type, e.dragType); }, 'dragmove.cropper': function (e) { console.log(e.type, e.dragType); }, 'dragend.cropper': function (e) { console.log(e.type, e.dragType); }, 'zoomin.cropper': function (e) { console.log(e.type); }, 'zoomout.cropper': function (e) { console.log(e.type); } }).cropper(options); // Methods $(document.body).on('click', '[data-method]', function () { var data = $(this).data(), $target, result; if (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); } } } result = $image.cropper(data.method, data.option); if (data.method === 'getCroppedCanvas') { $('#getCroppedCanvasModal').modal().find('.modal-body').html(result); } if ($.isPlainObject(result) && $target) { try { $target.val(JSON.stringify(result)); } catch (e) { console.log(e.message); } } } }).on('keydown', function (e) { 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'), URL = window.URL || window.webkitURL, blobURL; if (URL) { $inputImage.change(function () { var files = this.files, file; if (files && files.length) { file = files[0]; if (/^image\/\w+$/.test(file.type)) { blobURL = URL.createObjectURL(file); $image.one('built.cropper', function () { URL.revokeObjectURL(blobURL); // Revoke when load complete }).cropper('reset').cropper('replace', blobURL); $inputImage.val(''); } else { showMessage('Please choose an image file.'); } } }); } else { $inputImage.parent().remove(); } // Options $('.docs-options :checkbox').on('change', function () { var $this = $(this); options[$this.val()] = $this.prop('checked'); $image.cropper('destroy').cropper(options); }); // Tooltips $('[data-toggle="tooltip"]').tooltip(); }()); });

Related: See More


Questions / Comments: