效果图
图片剪切功能
- 剪切选择框
- 剪切区域实时预览
- 剪切图片
剪切选择框
- 创建一个选择框(如效果图中的白色虚线组成的框)
- 选择框具有拖拽移动、边界检测(不能超出所在容器)、改变大小(我实现的是可以通过八个点改变大小)、可以反向拖动(就是在你右边的点拖动时,当前X坐标小于你选择框的X时,进行反向计算)
- 选择框有最小宽高限制、最大宽高限制、固定宽高限制
- 所选区域坐标换算
代码
css:
/**图片剪切*/
.dx-croping
{
position:absolute;
border:dashed 1px #cbc8c8;
box-sizing:border-box;
z-index:99;
}
.dx-croping .croping-move
{
position:absolute;
left:0;
top:0;
right:0;
bottom:0;
z-index:3;
overflow:hidden;
}
.dx-croping.dx-croping-noresize>.croping-pot
{
display:none;
}
.dx-croping .croping-wrap
{
position:absolute;
left:0;
top:0;
right:0;
bottom:0;
z-index:2;
overflow:hidden;
}
.dx-croping .croping-pot
{
position:absolute;
height:8px;
width:8px;
z-index:4;
border:dashed 1px #cbc8c8;
background-color:transparent;
}
.dx-croping .croping-ltop-pot
{
top:0px;
left:0px;
cursor:se-resize;
border-left:0;
border-top:0;
}
.dx-croping .croping-lcenter-pot
{
top:50%;
margin-top:-5px;
left:0;
cursor:w-resize;
border-left:0;
}
.dx-croping .croping-lbottom-pot{
bottom:0;
left:0;
border-left:0;
border-bottom:0;
cursor:sw-resize;
}
.dx-croping .croping-rtop-pot
{
top:0px;
right:0px;
cursor:sw-resize;
border-right:0;
border-top:0;
}
.dx-croping .croping-rcenter-pot
{
top:50%;
margin-top:-5px;
right:0px;
cursor:w-resize;
border-right:0;
}
.dx-croping .croping-rbottom-pot{
bottom:0px;
right:0px;
border-right:0;
border-bottom:0;
cursor:se-resize;
}
.dx-croping .croping-top-pot
{
top:0;
left:50%;
margin-left:-5px;
border-top:0;
cursor:s-resize;
}
.dx-croping .croping-bottom-pot{
bottom:0;
left:50%;
margin-left:-5px;
border-bottom:0;
cursor:s-resize;
}
.dx-cropimage-wrapper
{
position:relative;
width:640px;
height:428px;
}
.dx-cropimage-image
{
border:0;
}
.dx-cropimage-mask
{
position:absolute;
background-color:rgba(0, 0, 0,0.5);
left:0;
top:0;
right:0;
bottom:0;
z-index:3;
}
/**图片剪切end*/
html:
<img src="/assets/images/domokun.jpg" id="cropImage" /> <div id="previewid"></div>
JS:
var MouseEvents = Dx.ui.Widget.extend((function () {
var MOUSEEVENTS = {
'down': 'mousedown',
'move': 'mousemove',
'up': 'mouseup'
};
function getPointer(e) {
e = e || { x: 0, y: 0 };
return {
x: e.pageX,
y: e.pageY
};
}
return {
options: {
selector: null
},
events: ['onDown', 'onMove', 'onUp'],
initialize: function (element, options) {
this.callSuper('initialize', element, options);
Dx._.bindAll(this, 'onDown', 'onMove', 'onUp')
this.$doc = $(document);
this.delegateEvents(this.element, MOUSEEVENTS.down, this.options.selector, this.onDown);
this.startPointer = getPointer();
this.lastPointer = getPointer();
this.currentPointer = getPointer();
this.vx = 0;
this.vy = 0;
},
onDown: function (e) {
e.preventDefault();
this.startPointer = getPointer(e);
this._startMouse();
this.vx = 0;
this.vy = 0;
this.trigger('onDown', e);
},
onMove: function (e) {
this.lastPointer = this.currentPointer;
this.currentPointer = getPointer(e);
this.vx = this.currentPointer.x - this.startPointer.x;
this.vy = this.currentPointer.y - this.startPointer.y;
this.trigger('onMove', e);
},
onUp: function (e) {
this.undelegateEvents(this.$doc);
this.trigger('onUp', e);
},
_startMouse: function () {
this.delegateEvents(this.$doc, MOUSEEVENTS.move, this.onMove);
this.delegateEvents(this.$doc, MOUSEEVENTS.up, this.onUp);
}
};
})());
var CropZone = Dx.ui.Widget.extend(function () {
var template = '<div class="dx-croping"><div class="croping-wrap"></div><div class="croping-move"></div><div class="croping-pot croping-ltop-pot"></div><div class="croping-pot croping-lcenter-pot"></div><div class="croping-pot croping-lbottom-pot"></div><div class="croping-pot croping-top-pot"></div><div class="croping-pot croping-bottom-pot"></div><div class="croping-pot croping-rtop-pot"></div><div class="croping-pot croping-rcenter-pot"></div><div class="croping-pot croping-rbottom-pot"></div></div>';
function getBoundary(element,isBorder)
{
var offset = element.offset(),
outerWidth = element.outerWidth(),
outerHeight = element.outerHeight(),
borderWidth = parseInt(element.css('borderWidth')), left = offset.left, top = offset.top, right = left + outerWidth, bottom = top + outerHeight;
if (!isBorder)
{
left += borderWidth;
top += borderWidth;
outerWidth -= borderWidth * 2;
outerHeight -= borderWidth * 2;
bottom = top + outerHeight;
right = left + outerWidth;
}
return {
left: left,
top: top,
right: right,
bottom:bottom,
width: outerWidth,
height: outerHeight
};
}
var cursors = {
'croping-move': 'move',
'croping-ltop-pot': "se-resize",
'croping-lcenter-pot': "w-resize",
'croping-lbottom-pot': "sw-resize",
'croping-top-pot': 's-resize',
'croping-bottom-pot': 's-resize',
'croping-rtop-pot': 'sw-resize',
'croping-rcenter-pot': "w-resize",
'croping-rbottom-pot': "se-resize"
};
return {
options: {
width: null,
height: null,
minWidth: 0,
minHeight: 0,
maxWidth: 0,
maxHeight:0,
aspectRatio: 0.4,
isResize:true
},
events: ['onCropEnd', 'onCropChange'],
initialize: function (element, options)
{
this.callSuper('initialize', element, options);
this.element.append(template);
this.$crop = this.element.children('.dx-croping');
this.$cropwrap = this.$crop.children('.croping-wrap');
this.init();
this.initEvents();
},
init:function()
{
if (this.options.width && this.options.height) {
this.element.css({
width: this.options.width,
height: this.options.height
});
}
this.$offsetParent = this.$crop.offsetParent();
this.boundary = getBoundary(this.element);
this.viewportOffset = getBoundary(this.$offsetParent);
this.cropBoxData = {};
this.maxWidth =this.options.maxWidth>0? Math.min(this.options.maxWidth, this.boundary.width):this.boundary.width;
this.maxHeight = this.options.maxHeight > 0 ? Math.min(this.options.maxHeight, this.boundary.height) : this.boundary.height;
this.minWidth = Math.min(Math.max(this.options.minWidth, 0),this.maxWidth);
this.minHeight = Math.min(Math.max(this.options.minHeight,0),this.maxHeight);
var width=Math.min(Math.max(this.boundary.width*this.options.aspectRatio,this.minWidth),this.maxWidth);
var height=Math.min(Math.max(this.boundary.height*this.options.aspectRatio,this.minHeight),this.maxHeight);
this.setResize({
width:width,
height:height,
left:(this.boundary.width-width)/2,
top:(this.boundary.height-height)/2
});
if(!this.options.isResize)
{
this.$crop.addClass('dx-croping-noresize');
}
},
initEvents:function()
{
var that = this;
var _mouseEvents = new MouseEvents(this.$crop, {
onDown: function(e)
{
var className = e.target.className.split(' '), eventName = className.shift();
this.eventName = eventName;
this.className = className.pop() || eventName;
this.cropBoundary = getBoundary(that.$crop, true);
that.setCursor(this.className);
this.trigger(eventName + '-down', e);
},
onMove:function(e)
{
this.trigger(this.eventName + '-move', e);
},
onUp:function(e)
{
that.removeCursor(this.className);
this.trigger(this.eventName + '-up', e);
that.trigger('onCropEnd')
}
});
_mouseEvents.on('croping-move-move', function (e) {
this.left = this.cropBoundary.left + this.vx;
this.top = this.cropBoundary.top + this.vy;
that.setPosition(that.aabb(this.left, this.top, this.cropBoundary.width, this.cropBoundary.height));
});
_mouseEvents.on('croping-pot-down', function (e) {
this.oldLeft=0;
this.oldTop=0;
});
_mouseEvents.on('croping-pot-move', function (e) {
var
boundary = that.boundary,
vx = this.vx,
vy = this.vy,
left = this.cropBoundary.left,
top = this.cropBoundary.top,
width = this.cropBoundary.width,
height = this.cropBoundary.height,
right = this.cropBoundary.right,
bottom = this.cropBoundary.bottom,
vH=height+vy,vW=width+vx;
switch (this.className)
{
case "croping-rtop-pot":
vH = height - vy;
if (vH > 0) {
height = Math.min(vH, bottom - boundary.top);
top = Math.max(top + vy, boundary.top);
} else {
top = bottom;
height = Math.min(Math.abs(vH), boundary.bottom - top);
}
if (vW > 0) {
width = Math.min(vW, boundary.right - left);
} else {
width = Math.min(Math.abs(vW), left - boundary.left);
left = Math.max(left + vW, boundary.left);
}
break;
case "croping-rcenter-pot":
if (vW > 0) {
width = Math.min(vW, boundary.right - left);
} else {
width = Math.min(Math.abs(vW), left - boundary.left);
left = Math.max(left + vW, boundary.left);
}
break;
case "croping-rbottom-pot":
if (vH > 0)
{
height = Math.min(vH, boundary.bottom - top);
} else {
height = Math.min(Math.abs(vH),top-boundary.top);
top = Math.max(top + vH, boundary.top);
}
if (vW > 0) {
width = Math.min(vW, boundary.right - left);
} else {
width = Math.min(Math.abs(vW), left - boundary.left);
left = Math.max(left + vW, boundary.left);
}
break;
case "croping-ltop-pot":
vH = height - vy;
vW = width - vx;
if (vH > 0) {
height = Math.min(vH, bottom - boundary.top);
top = Math.max(top + vy, boundary.top);
} else {
top = bottom;
height = Math.min(Math.abs(vH), boundary.bottom - top);
}
if (vW > 0) {
width = Math.min(vW, right - boundary.left);
left = Math.max(left + vx, boundary.left);
} else {
width = Math.min(Math.abs(vW), boundary.right - right);
left = right;
}
break;
case "croping-lcenter-pot":
vW = width - vx;
if (vW > 0) {
width = Math.min(vW, right - boundary.left);
left = Math.max(left + vx, boundary.left);
} else {
width = Math.min(Math.abs(vW), boundary.right - right);
left = right;
}
break;
case "croping-lbottom-pot":
vW = width - vx;
if (vH > 0) {
height = Math.min(vH, boundary.bottom - top);
} else {
height = Math.min(Math.abs(vH), top - boundary.top);
top = Math.max(top + vH, boundary.top);
}
if (vW > 0) {
width = Math.min(vW, right - boundary.left);
left = Math.max(left + vx, boundary.left);
} else {
width = Math.min(Math.abs(vW), boundary.right-right);
left = right;
}
break;
case "croping-top-pot":
vH = height - vy;
if (vH > 0) {
height = Math.min(vH, bottom - boundary.top);
top = Math.max(top + vy, boundary.top);
} else {
top = bottom;
height = Math.min(Math.abs(vH), boundary.bottom - top);
}
break;
case "croping-bottom-pot":
if (vH > 0) {
height = Math.min(vH, boundary.bottom - top);
} else {
height = Math.min(Math.abs(vH), top - boundary.top);
top = Math.max(top + vH, boundary.top);
}
break;
}
if (that.minWidth > 0 && width < that.minWidth)
{
width = that.minWidth;
left = this.oldLeft;
}
if (that.minHeight > 0 && height < that.minHeight) {
height = that.minHeight;
top = this.oldTop;
}
if (that.maxWidth > 0 && width > that.maxWidth) {
width = that.maxWidth;
left = this.oldLeft;
}
if (that.maxHeight > 0 && height > that.maxHeight) {
height = that.maxHeight;
top = this.oldTop;
}
that.setResize({
left: left,
top: top,
width: width,
height:height
});
this.oldLeft = left;
this.oldTop = top;
});
},
aabb:function(left,top,width,height)
{
return {
left: Math.min(Math.max(this.boundary.left, left), this.boundary.right - width),
top:Math.min(Math.max(this.boundary.top, top), this.boundary.bottom - height)
};
},
setResize: function (obj) {
var width = obj.width, height = obj.height, left = obj.left - this.viewportOffset.left, top = obj.top - this.viewportOffset.top;
this.$crop.css({
width: width,
height: height,
left: left,
top: top
});
this.cropBoxData.left = left;
this.cropBoxData.top = top;
this.cropBoxData.width = width;
this.cropBoxData.height = height;
this.trigger('onCropChange');
},
setPosition: function (position)
{
var left = position.left - this.viewportOffset.left, top = position.top - this.viewportOffset.top;
this.$crop.css({
left: left,
top: top
});
this.cropBoxData.left = left;
this.cropBoxData.top = top;
this.trigger('onCropChange');
},
getCropData:function()
{
return $.extend({}, this.cropBoxData);
},
setCursor:function(className)
{
this.element.css('cursor', cursors[className]);
},
removeCursor: function () {
this.element.css('cursor','');
}
};
}());
var CropImage = Dx.ui.extendWidget('CropImage3', function (parent) {
var template = ' <div id="cropImage" class="dx-cropimage-wrapper"><div class="dx-cropimage-image"></div><div class="dx-cropimage-mask"></div></div>';
return parent.extend({
options: {
width: null,
height: null,
preview:null
},
events: ['onCropEnd', 'onCropChange'],
initialize:function(element,options)
{
this.callSuper('initialize', element, options);
this.wrapper = $(template).insertAfter(this.element);
this.wrapper.find('.dx-cropimage-image').append(this.element);
this.width=this.options.width||this.element[0].width;
this.height = this.options.height || this.element[0].height;
this.element[0].width=this.width;
this.element[0].height = this.height;
this.$cloneImage = this.element.clone();
this.$cloneImage.removeAttr('id');
var that = this;
var _CropZone = this._cropZone = new CropZone(this.wrapper, $.extend({}, this.options, {
width: this.width,
height: this.height
}));
_CropZone.$cropwrap.append(this.$cloneImage);
_CropZone.on('onCropChange', function () {
that.renderCropImage();
});
if (this.options.preview) {
this.$preview = $(this.options.preview);
this.$previewWrap = $('<div>').css('overflow','hidden').appendTo(this.$preview);
this.$previewImage = this.$cloneImage.clone().appendTo(this.$previewWrap);
}
this.renderCropImage();
},
renderCropImage:function()
{
var cropZone = this._cropZone, cropData = cropZone.getCropData();
var styles={
marginLeft: -cropData.left + "px",
marginTop: -cropData.top + "px"
};
this.$cloneImage.css(styles);
if (this.options.preview)
{
this.$previewWrap.css({
width: cropData.width,
height:cropData.height
});
this.$previewImage.css(styles)
}
},
toDataURL:function()
{
var canvas = document.createElement('canvas'), cropZone = this._cropZone, cropData = cropZone.getCropData();
var ctx = canvas.getContext('2d');
canvas.width = cropData.width;
canvas.height = cropData.height;
ctx.drawImage(this.element[0], cropData.left, cropData.top, cropData.width, cropData.height, 0, 0, cropData.width, cropData.height);
return canvas.toDataURL();
}
});
});
var _CropImage = window._CropImage = new Dx.ui.CropImage('#cropImage', {
preview: "#previewid",
minWidth: 100,
minHeight: 100,
isResize: true,
onCropEnd:function()
{
img[0].src = _CropImage.toDataURL();
}
});