Repository: ElemeFE/image-cropper Branch: master Commit: 7a2c321893cd Files: 19 Total size: 65.8 KB Directory structure: gitextract_w4s35rzg/ ├── .gitignore ├── LICENSE ├── README.MD ├── bower.json ├── demo/ │ ├── angular-demo.html │ ├── cropper-demo.css │ ├── standalone-noar.html │ └── standalone.html ├── dist/ │ ├── cropper.css │ └── cropper.js ├── doc/ │ └── README.MD ├── package.json └── src/ ├── angular.js ├── build-dom.js ├── cropper.js ├── draggable.js ├── export.js ├── index.js └── resizer.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # dist/*.js # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules # IDE .idea demo/**/*.bundle.js # Prototype File prototype ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 饿了么前端 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.MD ================================================ # Image Cropper A image cropper for cropping user avatar, no dependencies. Document & Demo: http://elemefe.github.io/image-cropper/ # Build & Watch Before Build & Watch, you should install browserify & watchify use: ```Shell npm install ``` Build your own js file after modify source code (including angular & standalone): ```Shell npm run build ``` Build standalone dist: ```Shell npm run build-plain ``` Watch source code then auto build: ```Shell npm run watch ``` ================================================ FILE: bower.json ================================================ { "name": "image-cropper", "main": "dist/cropper.js", "version": "0.3.1", "authors": [ "long.zhang " ], "license": "MIT", "ignore": [ "demo", "test" ] } ================================================ FILE: demo/angular-demo.html ================================================ Angular Demo

Support formats: JPG, PNG

Large

Medium

Small

================================================ FILE: demo/cropper-demo.css ================================================ form { margin-left: 18px; } form > p { padding-top: 22px; line-height: 40px; font-size: 14px; position: relative; color: #666; margin-bottom: 30px; overflow: hidden; } form > p .btn-upload { width: 120px; padding: 12px 20px; font-size: 14px; border: 1px solid #1e89e0; background-color: transparent; color: #1e89e0; margin-right: 20px; } form > p input { position: absolute; font-size: 480px; top: 22px; left: -30px; height: 42px; width: 150px; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; cursor: pointer; } form .btn-submit { font-size: 14px; padding: 12px 20px; width: 120px; margin-right: 20px; } .preview-container { overflow: hidden; color: #999; margin-bottom: 20px; } .preview-container .noavatar { background-color: #efeeef; } .preview-container > div { float: left; height: 320px; } .large-wrapper { border-left: 1px solid #eee; padding-left: 30px; margin-right: 30px; } .image-container { position: relative; overflow: hidden; border: 1px solid #bbb; margin-bottom: 5px; } .image-container .image-wrapper { position: absolute; width: 100%; height: 100%; left: 0; top: 0; overflow: hidden; } .image-container img { position: absolute; } .image-container.target { width: 320px; height: 320px; margin-right: 30px; } .image-container.target img { width: 320px; height: 320px; } .image-container.large { width: 210px; height: 210px; } .image-container.large img { width: 210px; height: 210px; } .image-container.large .noavatar { background-position: -320px 0; } .image-container.medium { width: 48px; height: 48px; } .image-container.medium img { width: 48px; height: 48px; } .image-container.medium .noavatar { background-position: -324px -210px; } .image-container.small { width: 20px; height: 20px; margin-top: 30px; } .image-container.small img { width: 20px; height: 20px; } .image-container.small .noavatar { background-position: -373px -210px; } ================================================ FILE: demo/standalone-noar.html ================================================ Standalone demo

Support formats: JPG, PNG

Large

Medium

Small

================================================ FILE: demo/standalone.html ================================================ Standalone demo

Support formats: JPG, PNG

Large

Medium

Small

================================================ FILE: dist/cropper.css ================================================ .resizer { position: absolute; box-sizing: border-box; border: 1px dashed gray; background-color: transparent; cursor: move; } .resizer .resize-handle { position: absolute; background-color: #333; opacity: 0.5; filter: alpha(opacity=50); font-size: 1px; height: 7px; width: 7px; } .resizer .resize-handle.ord-n { cursor: n-resize; left: 50%; margin-left: -4px; margin-top: -1px; top: 0; } .resizer .resize-handle.ord-s { cursor: s-resize; bottom: 0; left: 50%; margin-bottom: -1px; margin-left: -4px; } .resizer .resize-handle.ord-e { cursor: e-resize; margin-right: -1px; margin-top: -4px; right: 0; top: 50%; } .resizer .resize-handle.ord-w { cursor: w-resize; left: 0; margin-left: -1px; margin-top: -4px; top: 50%; } .resizer .resize-handle.ord-nw { cursor: nw-resize; left: 0; margin-left: -1px; margin-top: -1px; top: 0; } .resizer .resize-handle.ord-ne { cursor: ne-resize; margin-right: -1px; margin-top: -1px; right: 0; top: 0; } .resizer .resize-handle.ord-se { cursor: se-resize; bottom: 0; margin-bottom: -1px; margin-right: -1px; right: 0; } .resizer .resize-handle.ord-sw { cursor: sw-resize; bottom: 0; left: 0; margin-bottom: -1px; margin-left: -1px; } .resizer .resize-bar.ord-n, .resizer .resize-bar.ord-s { position: absolute; height: 7px; width: 100%; } .resizer .resize-bar.ord-e, .resizer .resize-bar.ord-w { position: absolute; height: 100%; width: 7px; } .resizer .resize-bar.ord-n { cursor: n-resize; margin-top: -1px; } .resizer .resize-bar.ord-s { cursor: s-resize; bottom: 0; margin-bottom: -1px; } .resizer .resize-bar.ord-e { cursor: e-resize; margin-right: -1px; right: 0; } .resizer .resize-bar.ord-w { cursor: w-resize; margin-left: -1px; } .resizer .inner-rect { position: absolute; left: 0; top: 0; right: 0; bottom: 0; background-color: white; opacity: 0; filter: alpha(opacity=0); } .cropper { position: absolute; box-sizing: border-box; } .cropper .mask { width: 100%; height: 100%; opacity: 0.4; filter: alpha(opacity=40); display: block; background-color: black; } .cropper .resizer .wrapper { position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; } .cropper .resizer .wrapper img { position: absolute; -webkit-user-select: none; -webkit-user-drag: none; } ================================================ FILE: dist/cropper.js ================================================ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o containment.right) { left = containment.right - dragState.resizerStartWidth; } if (top + dragState.resizerStartHeight > containment.bottom) { top = containment.bottom - dragState.resizerStartHeight; } dom.style.left = left + 'px'; dom.style.top = top + 'px'; self.doOnStateChange(); }, end: function () { dragState = {}; if (self.doOnDragEnd) { self.doOnDragEnd(); } } }); }; Resizer.prototype.bindResizeEvent = function(dom) { var self = this; var resizeState = {}; var aspectRatio = self.aspectRatio; if (typeof aspectRatio !== 'number') { aspectRatio = undefined; } var makeResizable = function (bar) { var type = bar.className.split(' ')[0]; var transformMap = configMap[type.substr(4)]; var containment; draggable(bar, { start: function (event) { var parentNode = dom.parentNode; containment = { left: 0, top: 0, width: parentNode.clientWidth, height: parentNode.clientHeight, right: parentNode.clientWidth, bottom: parentNode.clientHeight }; resizeState.startWidth = dom.clientWidth; resizeState.startHeight = dom.clientHeight; resizeState.startLeft = event.clientX; resizeState.startTop = event.clientY; var position = getPosition(dom); resizeState.resizerStartLeft = position.left; resizeState.resizerStartTop = position.top; }, drag: function (event) { var widthRatio = transformMap.width; var heightRatio = transformMap.height; var offsetLeft = event.clientX - resizeState.startLeft; var offsetTop = event.clientY - resizeState.startTop; var width, height, minWidth = 50, maxWidth = 10000, minHeight = 50, maxHeight = 10000; if (widthRatio !== undefined) { width = resizeState.startWidth + widthRatio * offsetLeft; if (width < minWidth) { width = minWidth; } if (maxWidth && width > maxWidth) { width = maxWidth; } } if (heightRatio !== undefined) { height = resizeState.startHeight + heightRatio * offsetTop; if (height < minHeight) { height = minHeight; } if (maxHeight && height > maxHeight) { height = maxHeight; } } if (aspectRatio !== undefined) { if (type === 'ord-n' || type === 'ord-s') { width = height * aspectRatio; } else if (type === 'ord-w' || type === 'ord-e') { height = width / aspectRatio; } else { if (width / height < aspectRatio) { height = width / aspectRatio; } else { width = height * aspectRatio; } } } var position = { left: resizeState.resizerStartLeft, top: resizeState.resizerStartTop }; if (transformMap.left !== undefined) { position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio; } if (transformMap.top !== undefined) { position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio; } //=== containment start if (width + position.left > containment.right) { width = containment.right - position.left; } if (position.left < containment.left) { width -= containment.left - position.left; position.left = containment.left; } if (height + position.top > containment.bottom) { height = containment.bottom - position.top; } if (position.top < containment.top) { height -= containment.top - position.top; position.top = containment.top; } //=== containment end if (aspectRatio !== undefined) { if (width / height < aspectRatio) { height = width / aspectRatio; } else { width = height * aspectRatio; } } if (transformMap.left !== undefined) { position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio; } if (transformMap.top !== undefined) { position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio; } dom.style.width = width + 'px'; dom.style.height = height + 'px'; if (position.left !== undefined) { dom.style.left = position.left + 'px'; } if (position.top !== undefined) { dom.style.top = position.top + 'px'; } self.doOnStateChange(); }, end: function () { if (self.doOnDragEnd) { self.doOnDragEnd(); } } }); }; var bars = dom.querySelectorAll('.resize-bar'); var handles = dom.querySelectorAll('.resize-handle'); var i, j; for (i = 0, j = bars.length; i < j; i++) { makeResizable(bars[i]); } for (i = 0, j = handles.length; i < j; i++) { makeResizable(handles[i]); } }; Resizer.prototype.render = function(container) { var self = this; var dom = buildDom({ tag: 'div', className: 'resizer', content: [ { tag: 'div', className: 'ord-n resize-bar' }, { tag: 'div', className: 'ord-s resize-bar' }, { tag: 'div', className: 'ord-w resize-bar' }, { tag: 'div', className: 'ord-e resize-bar' }, { tag: 'div', className: 'ord-nw resize-handle' }, { tag: 'div', className: 'ord-n resize-handle' }, { tag: 'div', className: 'ord-ne resize-handle' }, { tag: 'div', className: 'ord-w resize-handle' }, { tag: 'div', className: 'ord-e resize-handle' }, { tag: 'div', className: 'ord-sw resize-handle' }, { tag: 'div', className: 'ord-s resize-handle' }, { tag: 'div', className: 'ord-se resize-handle' } ] }); self.dom = dom; self.bindResizeEvent(dom); self.makeDraggable(dom); if (container) { container.appendChild(dom); } return dom; }; module.exports = Resizer; },{"./build-dom":3,"./draggable":5}],2:[function(require,module,exports){ var Cropper = require('./cropper'); var cropperInstances = {}; Cropper.getInstance = function(id) { return cropperInstances[id]; }; angular.module('cropper', []) .factory('Cropper', function() { return Cropper; }) .directive('cropper', function() { return { restrict: 'A', scope: { cropperContext: '=', cropperAspectRatio: '@' }, link: function(scope, element, attrs) { var id = attrs.cropper; if (!id) throw new Error('cropper id is required'); var cropperAspectRatio = scope.cropperAspectRatio; if (cropperAspectRatio) { if (/^\d*(\.)?\d+$/g.test(cropperAspectRatio)) { cropperAspectRatio = parseFloat(cropperAspectRatio); } } else { cropperAspectRatio = 1; } var cropper = Cropper({ element: element[0], aspectRatio: cropperAspectRatio }); cropperInstances[id] = cropper; var cropperContext = scope.cropperContext; cropper.onCroppedRectChange = function(rect) { if (cropperContext) { cropperContext.left = rect.left; cropperContext.top = rect.top; cropperContext.width = rect.width; cropperContext.height = rect.height; } try { scope.$apply(); } catch(e) {} }; scope.$on('$destroy', function() { cropperInstances[id] = null; delete cropperInstances[id]; }); } }; }).directive('cropperPreview', function(){ return { restrict: 'A', link: function(scope, element, attrs) { var id = attrs.cropperPreview; if (!id) throw new Error('cropper id is required'); var cropper = cropperInstances[id]; if (cropper) { cropper.addPreview(element[0]); } } } }).directive('cropperSource', function() { return { restrict: 'A', link: function ($scope, $el, attrs) { var id = attrs.cropperSource; if (!id) throw new Error('cropper id is required'); var fileValidateRegex = /\.(jpg|png|gif|jpeg)$/i; var fileTypes = attrs.cropperFileTypes; if (fileTypes) { var types = fileTypes.split(','); if (types.length > 0) { fileValidateRegex = new RegExp('\.(' + types.join('|') + ')$', 'i'); } } $el.on('change', function () { var input = this; var cropper = cropperInstances[id]; var fileName = input.value; if (!fileValidateRegex.test(fileName)) { cropper.setImage(); return; } if (typeof FileReader !== 'undefined') { var reader = new FileReader(); reader.onload = function (event) { cropper.setImage(event.target.result); }; if (input.files && input.files[0]) { reader.readAsDataURL(input.files[0]); } } else { input.select(); input.blur(); var src = document.selection.createRange().text; cropper.setImage(src); } }); } }; }); },{"./cropper":4}],3:[function(require,module,exports){ var buildDOM = function(config, refs) { if (!config) return null; var dom, childElement; if (config.tag) { dom = document.createElement(config.tag); for (var prop in config) { if (config.hasOwnProperty(prop)) { if (prop === 'content' || prop === 'tag') continue; if (prop === 'key' && refs) { var key = config[prop]; if (key) { refs[key] = dom; } } dom[prop] = config[prop]; } } var content = config.content; if (content instanceof Array) { for (var i = 0, j = content.length; i < j; i++) { var child = content[i]; childElement = buildDOM(child, refs); dom.appendChild(childElement); } } else if (typeof content === 'string') { childElement = document.createTextNode(content); dom.appendChild(childElement); } } return dom; }; module.exports = buildDOM; },{}],4:[function(require,module,exports){ var Resizer = require('./Resizer'); var buildDom = require('./build-dom'); var blankImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; var preLoadElement; var ieVersion = Number(document.documentMode); var getImageSize = function(src, callback) { if (ieVersion < 10) { if (!preLoadElement) { preLoadElement = document.createElement('div'); preLoadElement.style.position = 'absolute'; preLoadElement.style.width = '1px'; preLoadElement.style.height = '1px'; preLoadElement.style.left = '-9999px'; preLoadElement.style.top = '-9999px'; preLoadElement.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=image)'; document.body.insertBefore(preLoadElement, document.body.firstChild); } preLoadElement.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; var size = { width: preLoadElement.offsetWidth, height: preLoadElement.offsetHeight }; if (typeof callback === 'function') { callback(size); } } else { var image = new Image(); image.onload = function() { var size = { width: image.width, height: image.height }; if (typeof callback === 'function') { callback(size); } }; image.src = src; } }; var Cropper = function(options) { var cropper = this; if (!(this instanceof Cropper)) { cropper = new Cropper(); } cropper.aspectRatio = 1; for (var prop in options) { if (options.hasOwnProperty(prop)) cropper[prop] = options[prop]; } if (cropper.element) { cropper.render(cropper.element); } return cropper; }; Cropper.prototype.resetResizer = function() { var resizer = this.resizer; var cropperRect = this.cropperRect; var aspectRatio = this.aspectRatio; if (typeof aspectRatio !== 'number') { aspectRatio = 1; } var width = 100; var height = 100 / aspectRatio; var resizerDom = resizer.dom; resizerDom.style.width = width + 'px'; resizerDom.style.height = height + 'px'; if (cropperRect) { resizerDom.style.left = (cropperRect.width - width) / 2 + 'px'; resizerDom.style.top = (cropperRect.height - height) / 2 + 'px'; } else { resizerDom.style.left = resizerDom.style.top = ''; } resizer.doOnStateChange(); resizer.doOnDragEnd(); }; Cropper.prototype.setImage = function(src) { var element = this.element; var sourceImage = element.querySelector('img'); var resizeImage = this.refs.image; var self = this; if (src === undefined || src === null) { resizeImage.src = sourceImage.src = blankImage; resizeImage.style.width = resizeImage.style.height = resizeImage.style.left = resizeImage.style.top = ''; sourceImage.style.width = sourceImage.style.height = sourceImage.style.left = sourceImage.style.top = ''; self.updatePreview(blankImage); self.dom.style.display = 'none'; self.resetResizer(); self.dom.style.left = self.dom.style.top = ''; self.dom.style.width = element.offsetWidth + 'px'; self.dom.style.height = element.offsetHeight + 'px'; self.croppedRect = { width: 0, height: 0, left: 0, top: 0 }; self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); return; } getImageSize(src, function(size) { if (ieVersion < 10) { resizeImage.src = sourceImage.src = blankImage; resizeImage.style.filter = sourceImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; sourceImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; resizeImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; } self.imageSize = size; var elementWidth = element.offsetWidth; var elementHeight = element.offsetHeight; var dom = self.dom; var cropperRect = {}; if (size.width / size.height > elementWidth / elementHeight) { cropperRect.width = elementWidth; cropperRect.height = elementWidth * size.height / size.width; cropperRect.top = (elementHeight - cropperRect.height) / 2; cropperRect.left = 0; } else { cropperRect.height = elementHeight; cropperRect.width = elementHeight * size.width / size.height; cropperRect.top = 0; cropperRect.left = (elementWidth - cropperRect.width) / 2; } self.cropperRect = cropperRect; for (var style in cropperRect) { if (cropperRect.hasOwnProperty(style)) { dom.style[style] = sourceImage.style[style] = resizeImage.style[style] = cropperRect[style] + 'px'; } } if (!ieVersion || ieVersion > 9) { resizeImage.src = sourceImage.src = src; } self.dom.style.display = ''; self.resetResizer(); self.updatePreview(src); }); }; Cropper.prototype.addPreview = function(preview) { var previews = this.previews; if (!previews) { previews = this.previews = []; } previews.push(preview); }; Cropper.prototype.render = function(container) { var resizer = new Resizer({ aspectRatio: this.aspectRatio }); var refs = {}; var dom = buildDom({ tag: 'div', className: 'cropper', content: [{ tag: 'div', className: 'mask' }] }, refs); var resizerDom = resizer.render(dom); var img = buildDom({ tag: 'div', className: 'wrapper', content: [{ tag: 'img', key: 'image', src: blankImage }] }, refs); var self = this; self.refs = refs; resizer.doOnStateChange = function() { var left = parseInt(resizerDom.style.left, 10) || 0; var top = parseInt(resizerDom.style.top, 10) || 0; var image = refs.image; image.style.left = -left + 'px'; image.style.top = -top + 'px'; self.updatePreview(); }; resizer.doOnDragEnd = function() { var left = parseInt(resizerDom.style.left, 10) || 0; var top = parseInt(resizerDom.style.top, 10) || 0; var resizerWidth = resizerDom.offsetWidth; var resizerHeight = resizerDom.offsetHeight; var imageSize = self.imageSize; var cropperRect = self.cropperRect; if (cropperRect) { var scale = cropperRect.width / imageSize.width; self.croppedRect = { width: Math.floor(resizerWidth / scale), height: Math.floor(resizerHeight / scale), left: Math.floor(left / scale), top: Math.floor(top / scale) }; self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); } }; self.resizer = resizer; self.dom = dom; resizerDom.insertBefore(img, resizerDom.firstChild); container.appendChild(dom); self.dom.style.display = 'none'; }; Cropper.prototype.updatePreview = function(src) { var imageSize = this.imageSize; var cropperRect = this.cropperRect; if (!imageSize || !cropperRect) return; var previews = this.previews || []; var resizerDom = this.resizer.dom; var resizerLeft = parseInt(resizerDom.style.left, 10) || 0; var resizerTop = parseInt(resizerDom.style.top, 10) || 0; var resizerWidth = resizerDom.offsetWidth; var resizerHeight = resizerDom.offsetHeight; for (var i = 0, j = previews.length; i < j; i++) { var previewElement = previews[i]; var previewImage = previewElement.querySelector('img'); var previewWrapper = previewElement.querySelector('div'); if (!previewImage) continue; if (src === blankImage) { previewImage.style.width = previewImage.style.height = previewImage.style.left = previewImage.style.top = ''; previewImage.src = blankImage; } else { if (ieVersion < 10) { if (src) { previewImage.src = blankImage; previewImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; previewImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; previewImage.style.width = cropperRect.width + 'px'; previewImage.style.height = cropperRect.height + 'px'; } } else if (src) { previewImage.src = src; } var elementWidth = previewElement.offsetWidth; var elementHeight = previewElement.offsetHeight; var scale = elementWidth / resizerWidth; if (previewWrapper) { var elementRatio = elementWidth / elementHeight; var resizerRatio = resizerWidth / resizerHeight; if (elementRatio < resizerRatio) { previewWrapper.style.width = elementWidth + 'px'; previewWrapper.style.height = resizerHeight * elementWidth / resizerWidth + 'px'; previewWrapper.style.top = (elementHeight - previewWrapper.clientHeight) / 2 + 'px'; previewWrapper.style.left = ''; } else { var visibleWidth = resizerWidth * elementHeight / resizerHeight; scale = visibleWidth / resizerWidth; previewWrapper.style.height = elementHeight + 'px'; previewWrapper.style.width = visibleWidth + 'px'; previewWrapper.style.left = (elementWidth - previewWrapper.clientWidth) / 2 + 'px'; previewWrapper.style.top = ''; } } previewImage.style.width = scale * cropperRect.width + 'px'; previewImage.style.height = scale * cropperRect.height + 'px'; previewImage.style.left = -resizerLeft * scale + 'px'; previewImage.style.top = -resizerTop * scale + 'px'; } } }; module.exports = Cropper; },{"./Resizer":1,"./build-dom":3}],5:[function(require,module,exports){ var bind = function(element, event, fn) { if (element.attachEvent) { element.attachEvent('on' + event, fn); } else { element.addEventListener(event, fn, false); } }; var unbind = function(element, event, fn) { if (element.detachEvent) { element.detachEvent('on' + event, fn); } else { element.removeEventListener(event, fn); } }; var isDragging = false; var isIE8 = Number(document.documentMode) < 9; var fixEvent = function(event) { var scrollTop = Math.max(window.scrollY || 0, document.documentElement.scrollTop || 0); var scrollLeft = Math.max(window.scrollX || 0, document.documentElement.scrollLeft || 0); event.target = event.srcElement; event.pageX = scrollLeft + event.clientX; event.pageY = scrollTop + event.clientY; }; module.exports = function(element, options) { var moveFn = function(event) { if (isIE8) { fixEvent(event); } if (options.drag) { options.drag(event); } }; var upFn = function(event) { if (isIE8) { fixEvent(event); } unbind(document, 'mousemove', moveFn); unbind(document, 'mouseup', upFn); document.onselectstart = null; document.ondragstart = null; isDragging = false; if (options.end) { options.end(event); } }; bind(element, 'mousedown', function(event) { if (isIE8) { fixEvent(event); } if (isDragging) return; document.onselectstart = function() { return false; }; document.ondragstart = function() { return false; }; bind(document, 'mousemove', moveFn); bind(document, 'mouseup', upFn); isDragging = true; if (options.start) { options.start(event); } }); }; },{}],6:[function(require,module,exports){ window.Cropper = require('./cropper'); },{"./cropper":4}],7:[function(require,module,exports){ if (typeof angular !== 'undefined') { require('./angular'); } else { require('./export'); } },{"./angular":2,"./export":6}]},{},[7]); ================================================ FILE: doc/README.MD ================================================ ## 介绍 Image Cropper可以为图片显示一个裁剪框,裁剪框允许用户调整大小和位置,常用来做用户自定义头像的裁剪功能。 目前Image Cropper的实现是无依赖的,浏览器支持到IE8+。Image Cropper可以和Angular一块使用,也可以独立使用。 ## 安装 如果你使用bower,可以在bower.json中添加这么一段配置: "image-cropper": "git@github.com:ElemeFE/image-cropper.git#~0.2.0" 如果你使用npm,可以直接这么安装: npm install image-cropper.js --save ## 引入文件 Image Cropper的使用依赖两个文件: - dist/cropper.css - dist/cropper.js 最简单的办法是直接在HTML中引用这两个文件,不过每个项目的情况不同,可以根据情况来决定如何引入这两个文件。 ## 使用说明 Image Cropper是因为一个裁剪头像并上传的需求诞生的。 一个裁剪头像的流程大概是这样: - 用户选择一张本地图片。 - 用户使用鼠标拖拽裁剪头像,并实时预览不同大小的裁剪效果。 - 把用户的裁剪范围和图片提交到服务器端。 从提交到服务器端的角度来看,我们关注的点只有两个: - 用户选择的本地图片 - 用户的裁剪结果(Rect: 相对于图片的left、top、width、height) ### 在Angular中使用 Image Cropper在同一个页面上可以指定多个Instance,该属性定义一个字符串,作为Image Cropper的一个实例的ID。 Image Cropper的ID是必需的,在下面的例子里面,我们使用avatar作为例子中的Image Cropper的实例的ID。 Image Cropper为Angular提供了三个属性Directive,通过这三个Directive的配合,即可实现图像裁剪和预览的效果。 - cropper:添加到要显示裁剪框的元素上,该directive的属性值是cropper的ID。 - cropper-preview:添加到要显示预览的元素上。 - cropper-source:添加到input type=file上。 注意:下面例子中的data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7是一个空的gif文件,用来在用户未选择图片的情况下显示该图片。 #### cropper cropper需要添加到要显示裁剪框的元素上,directive的属性值为Image Cropper的实例的ID,例如:
#### cropper-aspect-ratio cropper默认的款高比为1,如果需要使用其他宽高比或者不限制宽高比,则可以设置此属性。比如不限制宽高比,则可以这么设置:
cropper-aspect-ratio如果不是数字,则为不限制宽高比。 ##### cropper-context 用户的裁剪结果需要导出到Controller中的一个变量里面,我们可以使用cropper-context来指定这个变量,比如Controller中的变量名是cropContext,则这么定义:
#### cropper-source cropper-source需要添加到一个input[type=file]上,directive的属性值为Image Cropper的实例的ID,例如: 在input[type=file]的值发生变化的时候,cropper和cropper-preview会去使用用户选择的图片。 ##### cropper-file-types Image Cropper默认支持的类型为gif、png、jpg,如果需要定义只支持者某几种格式,比如只支持jpg、png,可以这么定义: #### cropper-preview 在用户裁剪图像的过程中,需要动态的预览不同大小的头像的效果,在显示预览图像的元素上添加这个directive。这个directive的属性值为Image Cropper的实例的ID,例如:
注意,如果设置了aspectRatio为非数值,即不限制裁剪图片的宽高比,cropper-preview的HTML应该是这种结构的:
### 独立使用 Image Cropper是可以不和Angular集成,独立使用的。如果你的页面中没有Angular,则在window上可以直接使用Cropper。 #### 创建Cropper实例 独立使用Cropper,需要指定这么几个属性: - element:显示裁剪框的Element。 - aspectRatio:裁剪框的宽高比,默认值为1。如果设置非数字类型,则为不限制款高比。 - previews:数组类型,可以指定多个显示图片预览的元素。 - onCroppedRectChange:在裁剪结果变更之后会触发这个回调。 一个例子如下: var cropper = new Cropper({ element: document.getElementById('cropper-target'), previews: [ document.getElementById('preview-large'), document.getElementById('preview-medium'), document.getElementById('preview-small') ], onCroppedRectChange: function(rect) { console.log(rect); } }); #### 更改Cropper使用的图片 Image Cropper为Angular提供了一个cropper-source来在input[type=file]变更之后更改Cropper的图片,在独立使用的时候,需要手动去做这件事。 变更Cropper的图片使用setImage方法来完成,假设input[type=file]的id为cropper-input,示例代码如下: var input = document.getElementById('cropper-input'); input.onchange = function() { if (typeof FileReader !== 'undefined') { var reader = new FileReader(); reader.onload = function (event) { cropper.setImage(event.target.result); }; if (input.files && input.files[0]) { reader.readAsDataURL(input.files[0]); } } else { // IE10- input.select(); input.blur(); var src = document.selection.createRange().text; cropper.setImage(src); } }; ## License MIT ================================================ FILE: package.json ================================================ { "name": "image-cropper.js", "version": "0.3.1", "description": "A image cropper for cropping user avatar, no dependencies", "repository": { "type": "git", "url": "https://github.com/ElemeFE/image-cropper.git" }, "keywords": [ "crop", "cropper", "image" ], "main": "src/index.js", "author": "long.zhang", "license": "MIT", "devDependencies": { "browserify": "^9.0.8", "watchify": "^3.3.0" }, "scripts": { "build": "browserify src/index.js -o dist/cropper.js", "watch": "watchify src/index.js -o dist/cropper.js -dv", "build-plain": "browserify src/export.js -o dist/cropper-plain.js" } } ================================================ FILE: src/angular.js ================================================ var Cropper = require('./cropper'); var cropperInstances = {}; Cropper.getInstance = function(id) { return cropperInstances[id]; }; angular.module('cropper', []) .factory('Cropper', function() { return Cropper; }) .directive('cropper', function() { return { restrict: 'A', scope: { cropperContext: '=', cropperAspectRatio: '@' }, link: function(scope, element, attrs) { var id = attrs.cropper; if (!id) throw new Error('cropper id is required'); var cropperAspectRatio = scope.cropperAspectRatio; if (cropperAspectRatio) { if (/^\d*(\.)?\d+$/g.test(cropperAspectRatio)) { cropperAspectRatio = parseFloat(cropperAspectRatio); } } else { cropperAspectRatio = 1; } var cropper = Cropper({ element: element[0], aspectRatio: cropperAspectRatio }); cropperInstances[id] = cropper; var cropperContext = scope.cropperContext; cropper.onCroppedRectChange = function(rect) { if (cropperContext) { cropperContext.left = rect.left; cropperContext.top = rect.top; cropperContext.width = rect.width; cropperContext.height = rect.height; } try { scope.$apply(); } catch(e) {} }; scope.$on('$destroy', function() { cropperInstances[id] = null; delete cropperInstances[id]; }); } }; }).directive('cropperPreview', function(){ return { restrict: 'A', link: function(scope, element, attrs) { var id = attrs.cropperPreview; if (!id) throw new Error('cropper id is required'); var cropper = cropperInstances[id]; if (cropper) { cropper.addPreview(element[0]); } } } }).directive('cropperSource', function() { return { restrict: 'A', link: function ($scope, $el, attrs) { var id = attrs.cropperSource; if (!id) throw new Error('cropper id is required'); var fileValidateRegex = /\.(jpg|png|gif|jpeg)$/i; var fileTypes = attrs.cropperFileTypes; if (fileTypes) { var types = fileTypes.split(','); if (types.length > 0) { fileValidateRegex = new RegExp('\.(' + types.join('|') + ')$', 'i'); } } $el.on('change', function () { var input = this; var cropper = cropperInstances[id]; var fileName = input.value; if (!fileValidateRegex.test(fileName)) { cropper.setImage(); return; } if (typeof FileReader !== 'undefined') { var reader = new FileReader(); reader.onload = function (event) { cropper.setImage(event.target.result); }; if (input.files && input.files[0]) { reader.readAsDataURL(input.files[0]); } } else { input.select(); input.blur(); var src = document.selection.createRange().text; cropper.setImage(src); } }); } }; }); ================================================ FILE: src/build-dom.js ================================================ var buildDOM = function(config, refs) { if (!config) return null; var dom, childElement; if (config.tag) { dom = document.createElement(config.tag); for (var prop in config) { if (config.hasOwnProperty(prop)) { if (prop === 'content' || prop === 'tag') continue; if (prop === 'key' && refs) { var key = config[prop]; if (key) { refs[key] = dom; } } dom[prop] = config[prop]; } } var content = config.content; if (content instanceof Array) { for (var i = 0, j = content.length; i < j; i++) { var child = content[i]; childElement = buildDOM(child, refs); dom.appendChild(childElement); } } else if (typeof content === 'string') { childElement = document.createTextNode(content); dom.appendChild(childElement); } } return dom; }; module.exports = buildDOM; ================================================ FILE: src/cropper.js ================================================ var Resizer = require('./Resizer'); var buildDom = require('./build-dom'); var blankImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; var preLoadElement; var ieVersion = Number(document.documentMode); var getImageSize = function(src, callback) { if (ieVersion < 10) { if (!preLoadElement) { preLoadElement = document.createElement('div'); preLoadElement.style.position = 'absolute'; preLoadElement.style.width = '1px'; preLoadElement.style.height = '1px'; preLoadElement.style.left = '-9999px'; preLoadElement.style.top = '-9999px'; preLoadElement.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=image)'; document.body.insertBefore(preLoadElement, document.body.firstChild); } preLoadElement.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; var size = { width: preLoadElement.offsetWidth, height: preLoadElement.offsetHeight }; if (typeof callback === 'function') { callback(size); } } else { var image = new Image(); image.onload = function() { var size = { width: image.width, height: image.height }; if (typeof callback === 'function') { callback(size); } }; image.src = src; } }; var Cropper = function(options) { var cropper = this; if (!(this instanceof Cropper)) { cropper = new Cropper(); } cropper.aspectRatio = 1; for (var prop in options) { if (options.hasOwnProperty(prop)) cropper[prop] = options[prop]; } if (cropper.element) { cropper.render(cropper.element); } return cropper; }; Cropper.prototype.resetResizer = function() { var resizer = this.resizer; var cropperRect = this.cropperRect; var aspectRatio = this.aspectRatio; if (typeof aspectRatio !== 'number') { aspectRatio = 1; } var width = 100; var height = 100 / aspectRatio; var resizerDom = resizer.dom; resizerDom.style.width = width + 'px'; resizerDom.style.height = height + 'px'; if (cropperRect) { resizerDom.style.left = (cropperRect.width - width) / 2 + 'px'; resizerDom.style.top = (cropperRect.height - height) / 2 + 'px'; } else { resizerDom.style.left = resizerDom.style.top = ''; } resizer.doOnStateChange(); resizer.doOnDragEnd(); }; Cropper.prototype.setImage = function(src) { var element = this.element; var sourceImage = element.querySelector('img'); var resizeImage = this.refs.image; var self = this; if (src === undefined || src === null) { resizeImage.src = sourceImage.src = blankImage; resizeImage.style.width = resizeImage.style.height = resizeImage.style.left = resizeImage.style.top = ''; sourceImage.style.width = sourceImage.style.height = sourceImage.style.left = sourceImage.style.top = ''; self.updatePreview(blankImage); self.dom.style.display = 'none'; self.resetResizer(); self.dom.style.left = self.dom.style.top = ''; self.dom.style.width = element.offsetWidth + 'px'; self.dom.style.height = element.offsetHeight + 'px'; self.croppedRect = { width: 0, height: 0, left: 0, top: 0 }; self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); return; } getImageSize(src, function(size) { if (ieVersion < 10) { resizeImage.src = sourceImage.src = blankImage; resizeImage.style.filter = sourceImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; sourceImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; resizeImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; } self.imageSize = size; var elementWidth = element.offsetWidth; var elementHeight = element.offsetHeight; var dom = self.dom; var cropperRect = {}; if (size.width / size.height > elementWidth / elementHeight) { cropperRect.width = elementWidth; cropperRect.height = elementWidth * size.height / size.width; cropperRect.top = (elementHeight - cropperRect.height) / 2; cropperRect.left = 0; } else { cropperRect.height = elementHeight; cropperRect.width = elementHeight * size.width / size.height; cropperRect.top = 0; cropperRect.left = (elementWidth - cropperRect.width) / 2; } self.cropperRect = cropperRect; for (var style in cropperRect) { if (cropperRect.hasOwnProperty(style)) { dom.style[style] = sourceImage.style[style] = resizeImage.style[style] = cropperRect[style] + 'px'; } } if (!ieVersion || ieVersion > 9) { resizeImage.src = sourceImage.src = src; } self.dom.style.display = ''; self.resetResizer(); self.updatePreview(src); }); }; Cropper.prototype.addPreview = function(preview) { var previews = this.previews; if (!previews) { previews = this.previews = []; } previews.push(preview); }; Cropper.prototype.render = function(container) { var resizer = new Resizer({ aspectRatio: this.aspectRatio }); var refs = {}; var dom = buildDom({ tag: 'div', className: 'cropper', content: [{ tag: 'div', className: 'mask' }] }, refs); var resizerDom = resizer.render(dom); var img = buildDom({ tag: 'div', className: 'wrapper', content: [{ tag: 'img', key: 'image', src: blankImage }] }, refs); var self = this; self.refs = refs; resizer.doOnStateChange = function() { var left = parseInt(resizerDom.style.left, 10) || 0; var top = parseInt(resizerDom.style.top, 10) || 0; var image = refs.image; image.style.left = -left + 'px'; image.style.top = -top + 'px'; self.updatePreview(); }; resizer.doOnDragEnd = function() { var left = parseInt(resizerDom.style.left, 10) || 0; var top = parseInt(resizerDom.style.top, 10) || 0; var resizerWidth = resizerDom.offsetWidth; var resizerHeight = resizerDom.offsetHeight; var imageSize = self.imageSize; var cropperRect = self.cropperRect; if (cropperRect) { var scale = cropperRect.width / imageSize.width; self.croppedRect = { width: Math.floor(resizerWidth / scale), height: Math.floor(resizerHeight / scale), left: Math.floor(left / scale), top: Math.floor(top / scale) }; self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); } }; self.resizer = resizer; self.dom = dom; resizerDom.insertBefore(img, resizerDom.firstChild); container.appendChild(dom); self.dom.style.display = 'none'; }; Cropper.prototype.updatePreview = function(src) { var imageSize = this.imageSize; var cropperRect = this.cropperRect; if (!imageSize || !cropperRect) return; var previews = this.previews || []; var resizerDom = this.resizer.dom; var resizerLeft = parseInt(resizerDom.style.left, 10) || 0; var resizerTop = parseInt(resizerDom.style.top, 10) || 0; var resizerWidth = resizerDom.offsetWidth; var resizerHeight = resizerDom.offsetHeight; for (var i = 0, j = previews.length; i < j; i++) { var previewElement = previews[i]; var previewImage = previewElement.querySelector('img'); var previewWrapper = previewElement.querySelector('div'); if (!previewImage) continue; if (src === blankImage) { previewImage.style.width = previewImage.style.height = previewImage.style.left = previewImage.style.top = ''; previewImage.src = blankImage; } else { if (ieVersion < 10) { if (src) { previewImage.src = blankImage; previewImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; previewImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src; previewImage.style.width = cropperRect.width + 'px'; previewImage.style.height = cropperRect.height + 'px'; } } else if (src) { previewImage.src = src; } var elementWidth = previewElement.offsetWidth; var elementHeight = previewElement.offsetHeight; var scale = elementWidth / resizerWidth; if (previewWrapper) { var elementRatio = elementWidth / elementHeight; var resizerRatio = resizerWidth / resizerHeight; if (elementRatio < resizerRatio) { previewWrapper.style.width = elementWidth + 'px'; previewWrapper.style.height = resizerHeight * elementWidth / resizerWidth + 'px'; previewWrapper.style.top = (elementHeight - previewWrapper.clientHeight) / 2 + 'px'; previewWrapper.style.left = ''; } else { var visibleWidth = resizerWidth * elementHeight / resizerHeight; scale = visibleWidth / resizerWidth; previewWrapper.style.height = elementHeight + 'px'; previewWrapper.style.width = visibleWidth + 'px'; previewWrapper.style.left = (elementWidth - previewWrapper.clientWidth) / 2 + 'px'; previewWrapper.style.top = ''; } } previewImage.style.width = scale * cropperRect.width + 'px'; previewImage.style.height = scale * cropperRect.height + 'px'; previewImage.style.left = -resizerLeft * scale + 'px'; previewImage.style.top = -resizerTop * scale + 'px'; } } }; module.exports = Cropper; ================================================ FILE: src/draggable.js ================================================ var bind = function(element, event, fn) { if (element.attachEvent) { element.attachEvent('on' + event, fn); } else { element.addEventListener(event, fn, false); } }; var unbind = function(element, event, fn) { if (element.detachEvent) { element.detachEvent('on' + event, fn); } else { element.removeEventListener(event, fn); } }; var isDragging = false; var isIE8 = Number(document.documentMode) < 9; var fixEvent = function(event) { var scrollTop = Math.max(window.scrollY || 0, document.documentElement.scrollTop || 0); var scrollLeft = Math.max(window.scrollX || 0, document.documentElement.scrollLeft || 0); event.target = event.srcElement; event.pageX = scrollLeft + event.clientX; event.pageY = scrollTop + event.clientY; }; module.exports = function(element, options) { var moveFn = function(event) { if (isIE8) { fixEvent(event); } if (options.drag) { options.drag(event); } }; var upFn = function(event) { if (isIE8) { fixEvent(event); } unbind(document, 'mousemove', moveFn); unbind(document, 'mouseup', upFn); document.onselectstart = null; document.ondragstart = null; isDragging = false; if (options.end) { options.end(event); } }; bind(element, 'mousedown', function(event) { if (isIE8) { fixEvent(event); } if (isDragging) return; document.onselectstart = function() { return false; }; document.ondragstart = function() { return false; }; bind(document, 'mousemove', moveFn); bind(document, 'mouseup', upFn); isDragging = true; if (options.start) { options.start(event); } }); }; ================================================ FILE: src/export.js ================================================ window.Cropper = require('./cropper'); ================================================ FILE: src/index.js ================================================ if (typeof angular !== 'undefined') { require('./angular'); } else { require('./export'); } ================================================ FILE: src/resizer.js ================================================ var buildDom = require('./build-dom'); var draggable = require('./draggable'); var configMap = { 'n': { top: true, height: -1 }, 'w': { left: true, width: -1 }, 'e': { width: 1 }, 's': { height: 1 }, 'nw': { left: true, top: true, width: -1, height: -1 }, 'ne': { top: true, width: 1, height: -1 }, 'sw': { left: true, width: -1, height: 1 }, 'se': { width: 1, height: 1 } }; var getPosition = function (element) { var selfRect = element.getBoundingClientRect(); var parentRect = element.offsetParent.getBoundingClientRect(); return { left: selfRect.left - parentRect.left, top: selfRect.top - parentRect.top }; }; var Resizer = function(options) { for (var prop in options) { if (options.hasOwnProperty(prop)) this[prop] = options[prop]; } }; Resizer.prototype.doOnStateChange = function(state) { }; Resizer.prototype.makeDraggable = function(dom) { var self = this; var dragState = {}; var containment; draggable(dom, { start: function (event) { var parentNode = dom.parentNode; containment = { left: 0, top: 0, width: parentNode.clientWidth, height: parentNode.clientHeight, right: parentNode.clientWidth, bottom: parentNode.clientHeight }; dragState.startLeft = event.clientX; dragState.startTop = event.clientY; var position = getPosition(dom); dragState.resizerStartLeft = position.left; dragState.resizerStartTop = position.top; dragState.resizerStartWidth = dom.offsetWidth; dragState.resizerStartHeight = dom.offsetHeight; }, drag: function (event) { var offsetLeft = event.clientX - dragState.startLeft; var offsetTop = event.clientY - dragState.startTop; var left = dragState.resizerStartLeft + offsetLeft; var top = dragState.resizerStartTop + offsetTop; if (left < containment.left) { left = containment.left; } if (top < containment.top) { top = containment.top; } if (left + dragState.resizerStartWidth > containment.right) { left = containment.right - dragState.resizerStartWidth; } if (top + dragState.resizerStartHeight > containment.bottom) { top = containment.bottom - dragState.resizerStartHeight; } dom.style.left = left + 'px'; dom.style.top = top + 'px'; self.doOnStateChange(); }, end: function () { dragState = {}; if (self.doOnDragEnd) { self.doOnDragEnd(); } } }); }; Resizer.prototype.bindResizeEvent = function(dom) { var self = this; var resizeState = {}; var aspectRatio = self.aspectRatio; if (typeof aspectRatio !== 'number') { aspectRatio = undefined; } var makeResizable = function (bar) { var type = bar.className.split(' ')[0]; var transformMap = configMap[type.substr(4)]; var containment; draggable(bar, { start: function (event) { var parentNode = dom.parentNode; containment = { left: 0, top: 0, width: parentNode.clientWidth, height: parentNode.clientHeight, right: parentNode.clientWidth, bottom: parentNode.clientHeight }; resizeState.startWidth = dom.clientWidth; resizeState.startHeight = dom.clientHeight; resizeState.startLeft = event.clientX; resizeState.startTop = event.clientY; var position = getPosition(dom); resizeState.resizerStartLeft = position.left; resizeState.resizerStartTop = position.top; }, drag: function (event) { var widthRatio = transformMap.width; var heightRatio = transformMap.height; var offsetLeft = event.clientX - resizeState.startLeft; var offsetTop = event.clientY - resizeState.startTop; var width, height, minWidth = 50, maxWidth = 10000, minHeight = 50, maxHeight = 10000; if (widthRatio !== undefined) { width = resizeState.startWidth + widthRatio * offsetLeft; if (width < minWidth) { width = minWidth; } if (maxWidth && width > maxWidth) { width = maxWidth; } } if (heightRatio !== undefined) { height = resizeState.startHeight + heightRatio * offsetTop; if (height < minHeight) { height = minHeight; } if (maxHeight && height > maxHeight) { height = maxHeight; } } if (aspectRatio !== undefined) { if (type === 'ord-n' || type === 'ord-s') { width = height * aspectRatio; } else if (type === 'ord-w' || type === 'ord-e') { height = width / aspectRatio; } else { if (width / height < aspectRatio) { height = width / aspectRatio; } else { width = height * aspectRatio; } } } var position = { left: resizeState.resizerStartLeft, top: resizeState.resizerStartTop }; if (transformMap.left !== undefined) { position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio; } if (transformMap.top !== undefined) { position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio; } //=== containment start if (width + position.left > containment.right) { width = containment.right - position.left; } if (position.left < containment.left) { width -= containment.left - position.left; position.left = containment.left; } if (height + position.top > containment.bottom) { height = containment.bottom - position.top; } if (position.top < containment.top) { height -= containment.top - position.top; position.top = containment.top; } //=== containment end if (aspectRatio !== undefined) { if (width / height < aspectRatio) { height = width / aspectRatio; } else { width = height * aspectRatio; } } if (transformMap.left !== undefined) { position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio; } if (transformMap.top !== undefined) { position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio; } dom.style.width = width + 'px'; dom.style.height = height + 'px'; if (position.left !== undefined) { dom.style.left = position.left + 'px'; } if (position.top !== undefined) { dom.style.top = position.top + 'px'; } self.doOnStateChange(); }, end: function () { if (self.doOnDragEnd) { self.doOnDragEnd(); } } }); }; var bars = dom.querySelectorAll('.resize-bar'); var handles = dom.querySelectorAll('.resize-handle'); var i, j; for (i = 0, j = bars.length; i < j; i++) { makeResizable(bars[i]); } for (i = 0, j = handles.length; i < j; i++) { makeResizable(handles[i]); } }; Resizer.prototype.render = function(container) { var self = this; var dom = buildDom({ tag: 'div', className: 'resizer', content: [ { tag: 'div', className: 'ord-n resize-bar' }, { tag: 'div', className: 'ord-s resize-bar' }, { tag: 'div', className: 'ord-w resize-bar' }, { tag: 'div', className: 'ord-e resize-bar' }, { tag: 'div', className: 'ord-nw resize-handle' }, { tag: 'div', className: 'ord-n resize-handle' }, { tag: 'div', className: 'ord-ne resize-handle' }, { tag: 'div', className: 'ord-w resize-handle' }, { tag: 'div', className: 'ord-e resize-handle' }, { tag: 'div', className: 'ord-sw resize-handle' }, { tag: 'div', className: 'ord-s resize-handle' }, { tag: 'div', className: 'ord-se resize-handle' } ] }); self.dom = dom; self.bindResizeEvent(dom); self.makeDraggable(dom); if (container) { container.appendChild(dom); } return dom; }; module.exports = Resizer;